diff --git a/ChangeLog b/ChangeLog index 4b3802f1..53a43bb4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +* 2015-04-19: Version 0.1.8 + * The GETDNS_TRANSPORT_TLS_ONLY_KEEP_CONNECTIONS_OPEN and + GETDNS_TRANSPORT_TLS_FIRST_AND_FALL_BACK_TO_TCP_KEEP_CONNECTIONS_OPEN + DNS over TLS transport options. + * 2015-04-08: Version 0.1.7 * Individual getter functions for context settings * Fix: --with-current-date function to make build deterministically diff --git a/configure b/configure index f404ad66..490c4ac9 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for getdns 0.1.7. +# Generated by GNU Autoconf 2.69 for getdns 0.1.8. # # Report bugs to . # @@ -590,8 +590,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='getdns' PACKAGE_TARNAME='getdns' -PACKAGE_VERSION='0.1.7' -PACKAGE_STRING='getdns 0.1.7' +PACKAGE_VERSION='0.1.8' +PACKAGE_STRING='getdns 0.1.8' PACKAGE_BUGREPORT='stub-resolver@verisignlabs.com' PACKAGE_URL='http://getdnsapi.net' @@ -1323,7 +1323,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures getdns 0.1.7 to adapt to many kinds of systems. +\`configure' configures getdns 0.1.8 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1388,7 +1388,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of getdns 0.1.7:";; + short | recursive ) echo "Configuration of getdns 0.1.8:";; esac cat <<\_ACEOF @@ -1519,7 +1519,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -getdns configure 0.1.7 +getdns configure 0.1.8 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -2008,7 +2008,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by getdns $as_me 0.1.7, which was +It was created by getdns $as_me 0.1.8, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2367,7 +2367,7 @@ else CURRENT_DATE="`date -u +%Y-%m-%dT%H:%M:%SZ`" fi -GETDNS_COMPILATION_COMMENT="getdns 0.1.7 configured on $CURRENT_DATE for the January 2015 version of the API" +GETDNS_COMPILATION_COMMENT="getdns 0.1.8 configured on $CURRENT_DATE for the January 2015 version of the API" # Library version # --------------- @@ -2387,9 +2387,10 @@ GETDNS_COMPILATION_COMMENT="getdns 0.1.7 configured on $CURRENT_DATE for the Jan # getdns-0.1.4 had libversion 0:0:0 # getdns-0.1.5 had libversion 1:0:0 # getdns-0.1.6 had libversion 1:1:0 -# getdns-0.1.7 will have libversion 1:2:1 +# getdns-0.1.7 had libversion 1:2:1 +# getdns-0.1.8 will have libversion 1:3:0 # -GETDNS_LIBVERSION=1:2:1 +GETDNS_LIBVERSION=1:3:0 @@ -11333,8 +11334,8 @@ $as_echo "found in $ssldir" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for HMAC_CTX_init in -lcrypto" >&5 $as_echo_n "checking for HMAC_CTX_init in -lcrypto... " >&6; } - LIBS="$LIBS -lcrypto" - LIBSSL_LIBS="$LIBSSL_LIBS -lcrypto" + LIBS="$LIBS -lcrypto -lssl" + LIBSSL_LIBS="$LIBSSL_LIBS -lcrypto -lssl" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -11543,6 +11544,51 @@ fi done +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for TLSv1_2_client_method in -lssl" >&5 +$as_echo_n "checking for TLSv1_2_client_method in -lssl... " >&6; } +if ${ac_cv_lib_ssl_TLSv1_2_client_method+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lssl $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char TLSv1_2_client_method (); +int +main () +{ +return TLSv1_2_client_method (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_ssl_TLSv1_2_client_method=yes +else + ac_cv_lib_ssl_TLSv1_2_client_method=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ssl_TLSv1_2_client_method" >&5 +$as_echo "$ac_cv_lib_ssl_TLSv1_2_client_method" >&6; } +if test "x$ac_cv_lib_ssl_TLSv1_2_client_method" = xyes; then : + +$as_echo "#define HAVE_LIBTLS1_2 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Cannot find TLSv1_2_client_method in libssl library. TLS will not be available." >&5 +$as_echo "$as_me: WARNING: Cannot find TLSv1_2_client_method in libssl library. TLS will not be available." >&2;} +fi + @@ -14277,7 +14323,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by getdns $as_me 0.1.7, which was +This file was extended by getdns $as_me 0.1.8, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -14344,7 +14390,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -getdns config.status 0.1.7 +getdns config.status 0.1.8 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/configure.ac b/configure.ac index 141633c2..6e98e01f 100755 --- a/configure.ac +++ b/configure.ac @@ -31,7 +31,7 @@ AC_PREREQ([2.56]) -AC_INIT([getdns], [0.1.7], [stub-resolver@verisignlabs.com], [], [http://getdnsapi.net]) +AC_INIT([getdns], [0.1.8], [stub-resolver@verisignlabs.com], [], [http://getdnsapi.net]) AC_SUBST(RELEASE_CANDIDATE, []) # Set current date from system if not set @@ -60,9 +60,10 @@ GETDNS_COMPILATION_COMMENT="AC_PACKAGE_STRING configured on $CURRENT_DATE for th # getdns-0.1.4 had libversion 0:0:0 # getdns-0.1.5 had libversion 1:0:0 # getdns-0.1.6 had libversion 1:1:0 -# getdns-0.1.7 will have libversion 1:2:1 +# getdns-0.1.7 had libversion 1:2:1 +# getdns-0.1.8 will have libversion 1:3:0 # -GETDNS_LIBVERSION=1:2:1 +GETDNS_LIBVERSION=1:3:0 AC_SUBST(GETDNS_COMPILATION_COMMENT) AC_SUBST(GETDNS_LIBVERSION) @@ -741,6 +742,10 @@ unsigned char *SHA512(void* data, unsigned int data_len, unsigned char *digest); #include #endif +#ifdef HAVE_OPENSSL_SSL_H +#include +#endif + #ifdef HAVE_ATTR_FORMAT # define ATTR_FORMAT(archetype, string_index, first_to_check) \ __attribute__ ((format (archetype, string_index, first_to_check))) diff --git a/m4/acx_openssl.m4 b/m4/acx_openssl.m4 index 68e40f38..87507dce 100644 --- a/m4/acx_openssl.m4 +++ b/m4/acx_openssl.m4 @@ -48,8 +48,8 @@ AC_DEFUN([ACX_SSL_CHECKS], [ fi AC_MSG_CHECKING([for HMAC_CTX_init in -lcrypto]) - LIBS="$LIBS -lcrypto" - LIBSSL_LIBS="$LIBSSL_LIBS -lcrypto" + LIBS="$LIBS -lcrypto -lssl" + LIBSSL_LIBS="$LIBSSL_LIBS -lcrypto -lssl" AC_TRY_LINK(, [ int HMAC_CTX_init(void); (void)HMAC_CTX_init(); @@ -105,6 +105,8 @@ AC_DEFUN([ACX_SSL_CHECKS], [ AC_CHECK_HEADERS([openssl/ssl.h],,, [AC_INCLUDES_DEFAULT]) AC_CHECK_HEADERS([openssl/err.h],,, [AC_INCLUDES_DEFAULT]) AC_CHECK_HEADERS([openssl/rand.h],,, [AC_INCLUDES_DEFAULT]) +AC_CHECK_LIB(ssl, TLSv1_2_client_method,AC_DEFINE([HAVE_LIBTLS1_2], [1], + [Define if you have libssl with tls 1.2]),[AC_MSG_WARN([Cannot find TLSv1_2_client_method in libssl library. TLS will not be available.])]) ])dnl End of ACX_SSL_CHECKS dnl Check for SSL, where SSL is mandatory diff --git a/spec/index.html b/spec/index.html old mode 100644 new mode 100755 index 9e85490f..5b25f8e1 --- a/spec/index.html +++ b/spec/index.html @@ -2193,8 +2193,10 @@ getdns_context_set_dns_transport( The value is GETDNS_TRANSPORT_UDP_FIRST_AND_FALL_BACK_TO_TCP, GETDNS_TRANSPORT_UDP_ONLY, -GETDNS_TRANSPORT_TCP_ONLY, or -GETDNS_TRANSPORT_TCP_ONLY_KEEP_CONNECTIONS_OPEN.

+GETDNS_TRANSPORT_TCP_ONLY, +GETDNS_TRANSPORT_TCP_ONLY_KEEP_CONNECTIONS_OPEN>, +GETDNS_TRANSPORT_TLS_ONLY_KEEP_CONNECTIONS_OPEN>, or +GETDNS_TRANSPORT_TLS_FIRST_AND_FALL_BACK_TO_TCP_KEEP_CONNECTIONS_OPEN>
getdns_return_t diff --git a/src/config.h.in b/src/config.h.in index c6d368fd..c6e67fae 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -84,6 +84,9 @@ /* Define to 1 if you have the `ldns' library (-lldns). */ #undef HAVE_LIBLDNS +/* Define if you have libssl with tls 1.2 */ +#undef HAVE_LIBTLS1_2 + /* Define to 1 if you have the `unbound' library (-lunbound). */ #undef HAVE_LIBUNBOUND @@ -320,6 +323,10 @@ unsigned char *SHA512(void* data, unsigned int data_len, unsigned char *digest); #include #endif +#ifdef HAVE_OPENSSL_SSL_H +#include +#endif + #ifdef HAVE_ATTR_FORMAT # define ATTR_FORMAT(archetype, string_index, first_to_check) \ __attribute__ ((format (archetype, string_index, first_to_check))) diff --git a/src/const-info.c b/src/const-info.c old mode 100644 new mode 100755 index d759765e..5c5a3d99 --- a/src/const-info.c +++ b/src/const-info.c @@ -39,6 +39,8 @@ static struct const_info consts_info[] = { { 541, "GETDNS_TRANSPORT_UDP_ONLY", GETDNS_TRANSPORT_UDP_ONLY_TEXT }, { 542, "GETDNS_TRANSPORT_TCP_ONLY", GETDNS_TRANSPORT_TCP_ONLY_TEXT }, { 543, "GETDNS_TRANSPORT_TCP_ONLY_KEEP_CONNECTIONS_OPEN", GETDNS_TRANSPORT_TCP_ONLY_KEEP_CONNECTIONS_OPEN_TEXT }, + { 544, "GETDNS_TRANSPORT_TLS_ONLY_KEEP_CONNECTIONS_OPEN", GETDNS_TRANSPORT_TLS_ONLY_KEEP_CONNECTIONS_OPEN_TEXT }, + { 545, "GETDNS_TRANSPORT_TLS_FIRST_AND_FALL_BACK_TO_TCP_KEEP_CONNECTIONS_OPEN", GETDNS_TRANSPORT_TLS_FIRST_AND_FALL_BACK_TO_TCP_KEEP_CONNECTIONS_OPEN_TEXT }, { 550, "GETDNS_APPEND_NAME_ALWAYS", GETDNS_APPEND_NAME_ALWAYS_TEXT }, { 551, "GETDNS_APPEND_NAME_ONLY_TO_SINGLE_LABEL_AFTER_FAILURE", GETDNS_APPEND_NAME_ONLY_TO_SINGLE_LABEL_AFTER_FAILURE_TEXT }, { 552, "GETDNS_APPEND_NAME_ONLY_TO_MULTIPLE_LABEL_NAME_AFTER_FAILURE", GETDNS_APPEND_NAME_ONLY_TO_MULTIPLE_LABEL_NAME_AFTER_FAILURE_TEXT }, diff --git a/src/context.c b/src/context.c index b3c26c48..45e4b472 100644 --- a/src/context.c +++ b/src/context.c @@ -470,11 +470,22 @@ upstreams_resize(getdns_upstreams *upstreams, size_t size) return r; } -static void -upstreams_dereference(getdns_upstreams *upstreams) +void +priv_getdns_upstreams_dereference(getdns_upstreams *upstreams) { - if (upstreams && --upstreams->referenced == 0) + size_t i; + + if (upstreams && --upstreams->referenced == 0) { + for (i = 0; i < upstreams->count; i++) { + if (upstreams->upstreams[i].tls_obj != NULL) { + SSL_shutdown(upstreams->upstreams[i].tls_obj); + SSL_free(upstreams->upstreams[i].tls_obj); + } + if (upstreams->upstreams[i].fd != -1) + close(upstreams->upstreams[i].fd); + } GETDNS_FREE(upstreams->mf, upstreams); + } } static uint8_t* @@ -503,7 +514,8 @@ upstream_scope_id(getdns_upstream *upstream) } static void -upstream_ntop_buf(getdns_upstream *upstream, char *buf, size_t len) +upstream_ntop_buf(getdns_upstream *upstream, getdns_transport_t transport, + char *buf, size_t len) { /* Also possible but prints scope_id by name (nor parsed by unbound) * @@ -515,7 +527,10 @@ upstream_ntop_buf(getdns_upstream *upstream, char *buf, size_t len) if (upstream_scope_id(upstream)) (void) snprintf(buf + strlen(buf), len - strlen(buf), "%%%d", (int)*upstream_scope_id(upstream)); - if (upstream_port(upstream) != 53 && upstream_port(upstream) != 0) + if (transport == GETDNS_TRANSPORT_TLS_ONLY_KEEP_CONNECTIONS_OPEN) + (void) snprintf(buf + strlen(buf), len - strlen(buf), + "@%d", GETDNS_TLS_PORT); + else if (upstream_port(upstream) != 53 && upstream_port(upstream) != 0) (void) snprintf(buf + strlen(buf), len - strlen(buf), "@%d", (int)upstream_port(upstream)); } @@ -541,6 +556,7 @@ upstream_init(getdns_upstream *upstream, /* For sharing a socket to this upstream with TCP */ upstream->fd = -1; + upstream->tls_obj = NULL; upstream->loop = NULL; (void) getdns_eventloop_event_init( &upstream->event, upstream, NULL, NULL, NULL); @@ -770,6 +786,7 @@ getdns_context_create_with_extended_memory_functions( result->edns_extended_rcode = 0; result->edns_version = 0; result->edns_do_bit = 0; + result-> tls_ctx = NULL; result->extension = &result->mini_event.loop; if ((r = getdns_mini_event_init(result, &result->mini_event))) @@ -789,6 +806,9 @@ getdns_context_create_with_extended_memory_functions( 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 + * don't know that till later so we will have to do this every time. */ + SSL_library_init(); result->unbound_ctx = NULL; if ((r = rebuild_ub_ctx(result))) goto error; @@ -876,6 +896,9 @@ getdns_context_destroy(struct getdns_context *context) GETDNS_FREE(context->my_mf, context->fchg_hosts->prevstat); GETDNS_FREE(context->my_mf, context->fchg_hosts); } + if (context->tls_ctx) { + SSL_CTX_free(context->tls_ctx); + } getdns_list_destroy(context->dns_root_servers); getdns_list_destroy(context->suffix); @@ -887,7 +910,7 @@ getdns_context_destroy(struct getdns_context *context) getdns_traverse_postorder(&context->local_hosts, destroy_local_host, context); - upstreams_dereference(context->upstreams); + priv_getdns_upstreams_dereference(context->upstreams); GETDNS_FREE(context->my_mf, context); } /* getdns_context_destroy */ @@ -1101,6 +1124,33 @@ getdns_context_set_namespaces(struct getdns_context *context, return GETDNS_RETURN_GOOD; } /* getdns_context_set_namespaces */ +getdns_base_transport_t +priv_get_base_transport(getdns_transport_t transport, int level) { + if (!(level == 0 || level == 1)) return GETDNS_TRANSPORT_NONE; + switch (transport) { + case GETDNS_TRANSPORT_UDP_FIRST_AND_FALL_BACK_TO_TCP: + if (level == 0) return GETDNS_TRANSPORT_UDP; + if (level == 1) return GETDNS_TRANSPORT_TCP; + case GETDNS_TRANSPORT_UDP_ONLY: + if (level == 0) return GETDNS_TRANSPORT_UDP; + if (level == 1) return GETDNS_TRANSPORT_NONE; + case GETDNS_TRANSPORT_TCP_ONLY: + if (level == 0) return GETDNS_TRANSPORT_TCP_SINGLE; + if (level == 1) return GETDNS_TRANSPORT_NONE; + case GETDNS_TRANSPORT_TCP_ONLY_KEEP_CONNECTIONS_OPEN: + if (level == 0) return GETDNS_TRANSPORT_TCP; + if (level == 1) return GETDNS_TRANSPORT_NONE; + case GETDNS_TRANSPORT_TLS_ONLY_KEEP_CONNECTIONS_OPEN: + if (level == 0) return GETDNS_TRANSPORT_TLS; + if (level == 1) return GETDNS_TRANSPORT_NONE; + case GETDNS_TRANSPORT_TLS_FIRST_AND_FALL_BACK_TO_TCP_KEEP_CONNECTIONS_OPEN: + if (level == 0) return GETDNS_TRANSPORT_TLS; + if (level == 1) return GETDNS_TRANSPORT_TCP; + default: + return GETDNS_TRANSPORT_NONE; + } +} + static getdns_return_t set_ub_dns_transport(struct getdns_context* context, getdns_transport_t value) { @@ -1114,13 +1164,25 @@ set_ub_dns_transport(struct getdns_context* context, set_ub_string_opt(context, "do-tcp:", "no"); break; case GETDNS_TRANSPORT_TCP_ONLY: - case GETDNS_TRANSPORT_TCP_ONLY_KEEP_CONNECTIONS_OPEN: + /* Note: no pipelining available directly in unbound.*/ + case GETDNS_TRANSPORT_TCP_ONLY_KEEP_CONNECTIONS_OPEN: set_ub_string_opt(context, "do-udp:", "no"); set_ub_string_opt(context, "do-tcp:", "yes"); break; - default: - /* TODO GETDNS_CONTEXT_TCP_ONLY_KEEP_CONNECTIONS_OPEN */ - return GETDNS_RETURN_CONTEXT_UPDATE_FAIL; + case GETDNS_TRANSPORT_TLS_ONLY_KEEP_CONNECTIONS_OPEN: + /* Hum. If used in recursive mode this will try TLS on port 53... + * So we need to fix or document that or delay setting it until + * resolution.*/ + set_ub_string_opt(context, "ssl-upstream:", "yes"); + /* Fall through*/ + case GETDNS_TRANSPORT_TLS_FIRST_AND_FALL_BACK_TO_TCP_KEEP_CONNECTIONS_OPEN: + /* Note: no fallback to TCP available directly in unbound, so we just + * use TCP for now to make sure the messages are sent. */ + set_ub_string_opt(context, "do-udp:", "no"); + set_ub_string_opt(context, "do-tcp:", "yes"); + break; + default: + return GETDNS_RETURN_CONTEXT_UPDATE_FAIL; } return GETDNS_RETURN_GOOD; } @@ -1134,6 +1196,11 @@ getdns_context_set_dns_transport(struct getdns_context *context, getdns_transport_t value) { RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER); + /* Note that the call below does not have any effect in unbound after the + * ctx is finalised. So will not apply for recursive mode or stub + dnssec. + * However the method returns success as otherwise the transport could not + * be reset for stub mode..... + * Also, not all transport options supported in libunbound yet */ if (set_ub_dns_transport(context, value) != GETDNS_RETURN_GOOD) { return GETDNS_RETURN_CONTEXT_UPDATE_FAIL; } @@ -1447,7 +1514,7 @@ getdns_context_set_upstream_recursive_servers(struct getdns_context *context, upstreams->count++; freeaddrinfo(ai); } - upstreams_dereference(context->upstreams); + priv_getdns_upstreams_dereference(context->upstreams); context->upstreams = upstreams; dispatch_updated(context, GETDNS_CONTEXT_CODE_UPSTREAM_RECURSIVE_SERVERS); @@ -1457,7 +1524,7 @@ getdns_context_set_upstream_recursive_servers(struct getdns_context *context, invalid_parameter: r = GETDNS_RETURN_INVALID_PARAMETER; error: - upstreams_dereference(upstreams); + priv_getdns_upstreams_dereference(upstreams); return GETDNS_RETURN_CONTEXT_UPDATE_FAIL; } /* getdns_context_set_upstream_recursive_servers */ @@ -1653,17 +1720,18 @@ getdns_cancel_callback(getdns_context *context, } /* getdns_cancel_callback */ static getdns_return_t -ub_setup_stub(struct ub_ctx *ctx, getdns_upstreams *upstreams) +ub_setup_stub(struct ub_ctx *ctx, getdns_context *context) { getdns_return_t r = GETDNS_RETURN_GOOD; size_t i; getdns_upstream *upstream; char addr[1024]; + getdns_upstreams *upstreams = context->upstreams; (void) ub_ctx_set_fwd(ctx, NULL); for (i = 0; i < upstreams->count; i++) { upstream = &upstreams->upstreams[i]; - upstream_ntop_buf(upstream, addr, 1024); + upstream_ntop_buf(upstream, context->dns_transport, addr, 1024); ub_ctx_set_fwd(ctx, addr); } @@ -1735,7 +1803,7 @@ priv_getdns_ns_dns_setup(struct getdns_context *context) case GETDNS_RESOLUTION_STUB: if (!context->upstreams || !context->upstreams->count) return GETDNS_RETURN_GENERIC_ERROR; - return ub_setup_stub(context->unbound_ctx, context->upstreams); + return ub_setup_stub(context->unbound_ctx, context); case GETDNS_RESOLUTION_RECURSING: /* TODO: use the root servers via root hints file */ @@ -1756,6 +1824,33 @@ getdns_context_prepare_for_resolution(struct getdns_context *context, if (context->destroying) { return GETDNS_RETURN_BAD_CONTEXT; } + + /* Transport can in theory be set per query in stub mode */ + /* TODO: move this transport logic to a separate functions*/ + if (context->resolution_type == GETDNS_RESOLUTION_STUB) { + switch (context->dns_transport) { + case GETDNS_TRANSPORT_TLS_ONLY_KEEP_CONNECTIONS_OPEN: + case GETDNS_TRANSPORT_TLS_FIRST_AND_FALL_BACK_TO_TCP_KEEP_CONNECTIONS_OPEN: + if (context->tls_ctx == NULL) { +#ifdef HAVE_LIBTLS1_2 + /* Create client context, use TLS v1.2 only for now */ + context->tls_ctx = SSL_CTX_new(TLSv1_2_client_method()); +#endif + if(!context->tls_ctx && context->dns_transport == + GETDNS_TRANSPORT_TLS_ONLY_KEEP_CONNECTIONS_OPEN) { + return GETDNS_RETURN_BAD_CONTEXT; + } + } + break; + default: + break; + } + } + /* Block use of TLS ONLY in recursive mode as it won't work */ + if (context->resolution_type == GETDNS_RESOLUTION_RECURSING + && context->dns_transport == GETDNS_TRANSPORT_TLS_ONLY_KEEP_CONNECTIONS_OPEN) + return GETDNS_RETURN_BAD_CONTEXT; + if (context->resolution_type_set == context->resolution_type) /* already set and no config changes * have caused this to be bad. diff --git a/src/context.h b/src/context.h index bc30efb3..12a2c263 100644 --- a/src/context.h +++ b/src/context.h @@ -49,6 +49,7 @@ struct ub_ctx; #define GETDNS_FN_RESOLVCONF "/etc/resolv.conf" #define GETDNS_FN_HOSTS "/etc/hosts" +#define GETDNS_TLS_PORT 1021 enum filechgs { GETDNS_FCHG_ERRORS = -1 , GETDNS_FCHG_NOERROR = 0 @@ -71,6 +72,14 @@ struct filechg { struct stat *prevstat; }; +typedef enum getdns_base_transport { + GETDNS_TRANSPORT_NONE, + GETDNS_TRANSPORT_UDP, + GETDNS_TRANSPORT_TCP_SINGLE, + GETDNS_TRANSPORT_TCP, + GETDNS_TRANSPORT_TLS +} getdns_base_transport_t; + typedef struct getdns_upstream { struct getdns_upstreams *upstreams; @@ -83,6 +92,7 @@ typedef struct getdns_upstream { /* For sharing a TCP socket to this upstream */ int fd; + SSL* tls_obj; getdns_eventloop_event event; getdns_eventloop *loop; getdns_tcp_state tcp; @@ -133,6 +143,7 @@ struct getdns_context { uint8_t edns_version; uint8_t edns_do_bit; int edns_maximum_udp_payload_size; /* -1 is unset */ + SSL_CTX* tls_ctx; getdns_update_callback update_callback; getdns_update_callback2 update_callback2; @@ -220,4 +231,8 @@ int filechg_check(struct getdns_context *context, struct filechg *fchg); void priv_getdns_context_ub_read_cb(void *userarg); +getdns_base_transport_t priv_get_base_transport(getdns_transport_t transport, int level); + +void priv_getdns_upstreams_dereference(getdns_upstreams *upstreams); + #endif /* _GETDNS_CONTEXT_H_ */ diff --git a/src/getdns/getdns.h.in b/src/getdns/getdns.h.in old mode 100644 new mode 100755 index 4b0bf8aa..f69a2dfc --- a/src/getdns/getdns.h.in +++ b/src/getdns/getdns.h.in @@ -163,7 +163,9 @@ typedef enum getdns_transport_t { GETDNS_TRANSPORT_UDP_FIRST_AND_FALL_BACK_TO_TCP = 540, GETDNS_TRANSPORT_UDP_ONLY = 541, GETDNS_TRANSPORT_TCP_ONLY = 542, - GETDNS_TRANSPORT_TCP_ONLY_KEEP_CONNECTIONS_OPEN = 543 + GETDNS_TRANSPORT_TCP_ONLY_KEEP_CONNECTIONS_OPEN = 543, + GETDNS_TRANSPORT_TLS_ONLY_KEEP_CONNECTIONS_OPEN = 544, + GETDNS_TRANSPORT_TLS_FIRST_AND_FALL_BACK_TO_TCP_KEEP_CONNECTIONS_OPEN = 545 } getdns_transport_t; /** @@ -174,6 +176,8 @@ typedef enum getdns_transport_t { #define GETDNS_TRANSPORT_UDP_ONLY_TEXT "See getdns_context_set_dns_transport()" #define GETDNS_TRANSPORT_TCP_ONLY_TEXT "See getdns_context_set_dns_transport()" #define GETDNS_TRANSPORT_TCP_ONLY_KEEP_CONNECTIONS_OPEN_TEXT "See getdns_context_set_dns_transport()" +#define GETDNS_TRANSPORT_TLS_ONLY_KEEP_CONNECTIONS_OPEN_TEXT "See getdns_context_set_dns_transport()" +#define GETDNS_TRANSPORT_TLS_FIRST_AND_FALL_BACK_TO_TCP_KEEP_CONNECTIONS_OPEN_TEXT "See getdns_context_set_dns_transport()" /** @} */ diff --git a/src/request-internal.c b/src/request-internal.c index 99e5c7d6..0889b3fc 100644 --- a/src/request-internal.c +++ b/src/request-internal.c @@ -177,8 +177,7 @@ dns_req_free(getdns_dns_req * req) return; } - if (req->upstreams && --req->upstreams->referenced == 0) - GETDNS_FREE(req->upstreams->mf, req->upstreams); + priv_getdns_upstreams_dereference(req->upstreams); /* cleanup network requests */ for (net_req = req->netreqs; *net_req; net_req++) diff --git a/src/stub.c b/src/stub.c old mode 100644 new mode 100755 index bb4277e9..0914aa57 --- a/src/stub.c +++ b/src/stub.c @@ -318,6 +318,13 @@ upstream_erred(getdns_upstream *upstream) netreq->state = NET_REQ_FINISHED; priv_getdns_check_dns_req_complete(netreq->owner); } + /* TODO[TLS]: When we get an error (which is probably a timeout) and are + * using to keep connections open should we leave the connection up here? */ + if (upstream->tls_obj) { + SSL_shutdown(upstream->tls_obj); + SSL_free(upstream->tls_obj); + upstream->tls_obj = NULL; + } close(upstream->fd); upstream->fd = -1; } @@ -521,7 +528,7 @@ stub_tcp_read(int fd, getdns_tcp_state *tcp, struct mem_funcs *mf) tcp->to_read -= read; tcp->read_pos += read; - if (tcp->to_read > 0) + if ((int)tcp->to_read > 0) return STUB_TCP_AGAIN; read = tcp->read_pos - tcp->read_buf; @@ -595,6 +602,187 @@ stub_tcp_read_cb(void *userarg) } } +/** wait for a socket to become ready */ +static int +sock_wait(int sockfd) +{ + int ret; + fd_set fds; + FD_ZERO(&fds); + FD_SET(FD_SET_T sockfd, &fds); + /*TODO[TLS]: Pick up this timeout from the context*/ + struct timeval timeout = {5, 0 }; + ret = select(sockfd+1, NULL, &fds, NULL, &timeout); + if(ret == 0) + /* timeout expired */ + return 0; + else if(ret == -1) + /* error */ + return 0; + return 1; +} + +static int +sock_connected(int sockfd) +{ + /* wait(write) until connected or error */ + while(1) { + int error = 0; + socklen_t len = (socklen_t)sizeof(error); + + if(!sock_wait(sockfd)) { + close(sockfd); + return -1; + } + + /* check if there is a pending error for nonblocking connect */ + if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void*)&error, &len) < 0) { + error = errno; /* on solaris errno is error */ + } + if (error == EINPROGRESS || error == EWOULDBLOCK) + continue; /* try again */ + else if (error != 0) { + close(sockfd); + return -1; + } + /* connected */ + break; + } + return sockfd; +} + +/* The connection testing and handshake should be handled by integrating this + * with the event loop framework, but for now just implement a standalone + * handshake method.*/ +static SSL* +do_tls_handshake(getdns_dns_req *dnsreq, getdns_upstream *upstream) +{ + /*Lets make sure the connection is up before we try a handshake*/ + if (errno == EINPROGRESS && sock_connected(upstream->fd) == -1) { + return NULL; + } + + /* Create SSL instance */ + if (dnsreq->context->tls_ctx == NULL) + return NULL; + SSL* ssl = SSL_new(dnsreq->context->tls_ctx); + if(!ssl) { + return NULL; + } + /* Connect the SSL object with a file descriptor */ + if(!SSL_set_fd(ssl, upstream->fd)) { + SSL_free(ssl); + return NULL; + } + SSL_set_connect_state(ssl); + (void) SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); + + int r; + int want; + fd_set fds; + FD_ZERO(&fds); + FD_SET(upstream->fd, &fds); + struct timeval timeout = {dnsreq->context->timeout/1000, 0 }; + while ((r = SSL_do_handshake(ssl)) != 1) + { + want = SSL_get_error(ssl, r); + switch (want) { + case SSL_ERROR_WANT_READ: + if (select(upstream->fd + 1, &fds, NULL, NULL, &timeout) == 0) { + SSL_free(ssl); + return NULL; + } + break; + case SSL_ERROR_WANT_WRITE: + if (select(upstream->fd + 1, NULL, &fds, NULL, &timeout) == 0) { + SSL_free(ssl); + return NULL; + } + break; + default: + SSL_free(ssl); + return NULL; + } + } + return ssl; +} + +static int +stub_tls_read(SSL* tls_obj, getdns_tcp_state *tcp, struct mem_funcs *mf) +{ + ssize_t read; + uint8_t *buf; + size_t buf_size; + + if (!tcp->read_buf) { + /* First time tls read, create a buffer for reading */ + if (!(tcp->read_buf = GETDNS_XMALLOC(*mf, uint8_t, 4096))) + return STUB_TCP_ERROR; + + tcp->read_buf_len = 4096; + tcp->read_pos = tcp->read_buf; + tcp->to_read = 2; /* Packet size */ + } + + ERR_clear_error(); + read = SSL_read(tls_obj, tcp->read_pos, tcp->to_read); + if (read <= 0) { + /* TODO[TLS]: Handle SSL_ERROR_WANT_WRITE which means handshake + renegotiation. Need to keep handshake state to do that.*/ + int want = SSL_get_error(tls_obj, read); + if (want == SSL_ERROR_WANT_READ) { + return STUB_TCP_AGAIN; /* read more later */ + } else + return STUB_TCP_ERROR; + } + tcp->to_read -= read; + tcp->read_pos += read; + + if ((int)tcp->to_read > 0) + return STUB_TCP_AGAIN; + + read = tcp->read_pos - tcp->read_buf; + if (read == 2) { + /* Read the packet size short */ + tcp->to_read = gldns_read_uint16(tcp->read_buf); + + if (tcp->to_read < GLDNS_HEADER_SIZE) + return STUB_TCP_ERROR; + + /* Resize our buffer if needed */ + if (tcp->to_read > tcp->read_buf_len) { + buf_size = tcp->read_buf_len; + while (tcp->to_read > buf_size) + buf_size *= 2; + + if (!(buf = GETDNS_XREALLOC(*mf, + tcp->read_buf, uint8_t, buf_size))) + return STUB_TCP_ERROR; + + tcp->read_buf = buf; + tcp->read_buf_len = buf_size; + } + + /* Ready to start reading the packet */ + tcp->read_pos = tcp->read_buf; + read = SSL_read(tls_obj, tcp->read_pos, tcp->to_read); + if (read <= 0) { + /* TODO[TLS]: Handle SSL_ERROR_WANT_WRITE which means handshake + renegotiation. Need to keep handshake state to do that.*/ + int want = SSL_get_error(tls_obj, read); + if (want == SSL_ERROR_WANT_READ) { + return STUB_TCP_AGAIN; /* read more later */ + } else + return STUB_TCP_ERROR; + } + tcp->to_read -= read; + tcp->read_pos += read; + if ((int)tcp->to_read > 0) + return STUB_TCP_AGAIN; + } + return GLDNS_ID_WIRE(tcp->read_buf); +} + static void netreq_upstream_read_cb(void *userarg); static void netreq_upstream_write_cb(void *userarg); static void @@ -607,8 +795,14 @@ upstream_read_cb(void *userarg) uint16_t query_id; intptr_t query_id_intptr; - switch ((q = stub_tcp_read(upstream->fd, &upstream->tcp, - &upstream->upstreams->mf))) { + if (upstream->tls_obj) + q = stub_tls_read(upstream->tls_obj, &upstream->tcp, + &upstream->upstreams->mf); + else + q = stub_tcp_read(upstream->fd, &upstream->tcp, + &upstream->upstreams->mf); + + switch (q) { case STUB_TCP_AGAIN: return; @@ -617,6 +811,7 @@ upstream_read_cb(void *userarg) return; default: + /* Lookup netreq */ query_id = (uint16_t) q; query_id_intptr = (intptr_t) query_id; @@ -705,9 +900,9 @@ stub_tcp_write(int fd, getdns_tcp_state *tcp, getdns_network_req *netreq) * the write_queue) for that upstream. Register this netreq * by query_id in the process. */ - if (dnsreq->context->dns_transport != - GETDNS_TRANSPORT_TCP_ONLY_KEEP_CONNECTIONS_OPEN) - + if ((dnsreq->context->dns_transport == GETDNS_TRANSPORT_TCP_ONLY) || + (dnsreq->context->dns_transport == GETDNS_TRANSPORT_UDP_ONLY) || + (dnsreq->context->dns_transport == GETDNS_TRANSPORT_UDP_FIRST_AND_FALL_BACK_TO_TCP)) query_id = arc4random(); else do { query_id = arc4random(); @@ -822,6 +1017,54 @@ stub_tcp_write_cb(void *userarg) } } +static int +stub_tls_write(SSL* tls_obj, getdns_tcp_state *tcp, getdns_network_req *netreq) +{ + size_t pkt_len = netreq->response - netreq->query; + ssize_t written; + uint16_t query_id; + intptr_t query_id_intptr; + + /* Do we have remaining data that we could not write before? */ + if (! tcp->write_buf) { + /* No, this is an initial write. Try to send + */ + + /* Find a unique query_id not already written (or in + * the write_queue) for that upstream. Register this netreq + * by query_id in the process. + */ + do { + query_id = ldns_get_random(); + query_id_intptr = (intptr_t)query_id; + netreq->node.key = (void *)query_id_intptr; + + } while (!getdns_rbtree_insert( + &netreq->upstream->netreq_by_query_id, &netreq->node)); + + GLDNS_ID_SET(netreq->query, query_id); + if (netreq->opt) + /* no limits on the max udp payload size with tcp */ + gldns_write_uint16(netreq->opt + 3, 65535); + + /* We have an initialized packet buffer. + * Lets see how much of it we can write */ + + // TODO[TLS]: Handle error cases, partial writes, renegotiation etc. + ERR_clear_error(); + written = SSL_write(tls_obj, netreq->query - 2, pkt_len + 2); + if (written <= 0) + return STUB_TCP_ERROR; + + /* We were able to write everything! Start reading. */ + return (int) query_id; + + } + + return STUB_TCP_ERROR; +} + + static void upstream_write_cb(void *userarg) { @@ -830,7 +1073,12 @@ upstream_write_cb(void *userarg) getdns_dns_req *dnsreq = netreq->owner; int q; - switch ((q = stub_tcp_write(upstream->fd, &upstream->tcp, netreq))) { + if (upstream->tls_obj) + q = stub_tls_write(upstream->tls_obj, &upstream->tcp, netreq); + else + q = stub_tcp_write(upstream->fd, &upstream->tcp, netreq); + + switch (q) { case STUB_TCP_AGAIN: return; @@ -902,6 +1150,58 @@ upstream_schedule_netreq(getdns_upstream *upstream, getdns_network_req *netreq) } } +static in_port_t +get_port(struct sockaddr_storage* addr) +{ + return ntohs(addr->ss_family == AF_INET + ? ((struct sockaddr_in *)addr)->sin_port + : ((struct sockaddr_in6*)addr)->sin6_port); +} + +static void +set_port(struct sockaddr_storage* addr, in_port_t port) +{ + addr->ss_family == AF_INET + ? (((struct sockaddr_in *)addr)->sin_port = htons(port)) + : (((struct sockaddr_in6*)addr)->sin6_port = htons(port)); +} + +static int +tcp_connect (getdns_upstream *upstream, getdns_base_transport_t transport) { + + int fd =-1; + struct sockaddr_storage connect_addr; + struct sockaddr_storage* addr = &upstream->addr; + socklen_t addr_len = upstream->addr_len; + + /* TODO[TLS]: For now, override the port to a hardcoded value*/ + if (transport == GETDNS_TRANSPORT_TLS && + (int)get_port(addr) != GETDNS_TLS_PORT) { + connect_addr = upstream->addr; + addr = &connect_addr; + set_port(addr, GETDNS_TLS_PORT); + } + + if ((fd = socket(addr->ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1) + return -1; + + getdns_sock_nonblock(fd); +#ifdef USE_TCP_FASTOPEN + /* Leave the connect to the later call to sendto() if using TCP*/ + if (transport == GETDNS_TRANSPORT_TCP || + transport == GETDNS_TRANSPORT_TCP_SINGLE) + return fd; +#endif + if (connect(fd, (struct sockaddr *)addr, + addr_len) == -1) { + if (errno != EINPROGRESS) { + close(fd); + return -1; + } + } + return fd; +} + getdns_return_t priv_getdns_submit_stub_request(getdns_network_req *netreq) { @@ -911,9 +1211,13 @@ priv_getdns_submit_stub_request(getdns_network_req *netreq) if (!upstream) return GETDNS_RETURN_GENERIC_ERROR; - switch(dnsreq->context->dns_transport) { - case GETDNS_TRANSPORT_UDP_ONLY: - case GETDNS_TRANSPORT_UDP_FIRST_AND_FALL_BACK_TO_TCP: + // Work out the primary and fallback transport options + getdns_base_transport_t transport = priv_get_base_transport( + dnsreq->context->dns_transport,0); + getdns_base_transport_t fb_transport = priv_get_base_transport( + dnsreq->context->dns_transport,1); + switch(transport) { + case GETDNS_TRANSPORT_UDP: if ((netreq->fd = socket( upstream->addr.ss_family, SOCK_DGRAM, IPPROTO_UDP)) == -1) @@ -929,23 +1233,10 @@ priv_getdns_submit_stub_request(getdns_network_req *netreq) return GETDNS_RETURN_GOOD; - case GETDNS_TRANSPORT_TCP_ONLY: + case GETDNS_TRANSPORT_TCP_SINGLE: - if ((netreq->fd = socket( - upstream->addr.ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1) + if ((netreq->fd = tcp_connect(upstream, transport)) == -1) return GETDNS_RETURN_GENERIC_ERROR; - - getdns_sock_nonblock(netreq->fd); -#ifdef USE_TCP_FASTOPEN - /* Leave the connect to the later call to sendto() */ -#else - if (connect(netreq->fd, (struct sockaddr *)&upstream->addr, - upstream->addr_len) == -1 && errno != EINPROGRESS) { - - close(netreq->fd); - return GETDNS_RETURN_GENERIC_ERROR; - } -#endif netreq->upstream = upstream; GETDNS_SCHEDULE_EVENT( @@ -955,34 +1246,51 @@ priv_getdns_submit_stub_request(getdns_network_req *netreq) return GETDNS_RETURN_GOOD; - case GETDNS_TRANSPORT_TCP_ONLY_KEEP_CONNECTIONS_OPEN: + case GETDNS_TRANSPORT_TCP: + case GETDNS_TRANSPORT_TLS: /* In coming comments, "global" means "context wide" */ /* Are we the first? (Is global socket initialized?) */ if (upstream->fd == -1) { - /* We are the first. Make global socket and connect. */ - if ((upstream->fd = socket(upstream->addr.ss_family, - SOCK_STREAM, IPPROTO_TCP)) == -1) - return GETDNS_RETURN_GENERIC_ERROR; - - getdns_sock_nonblock(upstream->fd); -#ifdef USE_TCP_FASTOPEN - /* Leave the connect to the later call to sendto() */ -#else - if (connect(upstream->fd, - (struct sockaddr *)&upstream->addr, - upstream->addr_len) == -1 && errno != EINPROGRESS){ + /* TODO[TLS]: We should remember on the context if we had to fallback + * for this upstream so when re-connecting from a dropped TCP + * connection we don't retry TLS. */ + int fallback = 0; - close(upstream->fd); - upstream->fd = -1; - return GETDNS_RETURN_GENERIC_ERROR; + /* We are the first. Make global socket and connect. */ + if ((upstream->fd = tcp_connect(upstream, transport)) == -1) { + if (fb_transport == GETDNS_TRANSPORT_NONE) + return GETDNS_RETURN_GENERIC_ERROR; + if ((upstream->fd = tcp_connect(upstream, fb_transport)) == -1) + return GETDNS_RETURN_GENERIC_ERROR; + fallback = 1; + } + + /* Now do a handshake for TLS. Note waiting for this to succeed or + * timeout blocks the scheduling of any messages for this upstream*/ + if (transport == GETDNS_TRANSPORT_TLS && (fallback == 0)) { + upstream->tls_obj = do_tls_handshake(dnsreq, upstream); + if (!upstream->tls_obj) { + if (fb_transport == GETDNS_TRANSPORT_NONE) + return GETDNS_RETURN_GENERIC_ERROR; + close(upstream->fd); + if ((upstream->fd = tcp_connect(upstream, fb_transport)) == -1) + return GETDNS_RETURN_GENERIC_ERROR; + } } -#endif /* Attach to the global event loop * so it can do it's own scheduling */ upstream->loop = dnsreq->context->extension; + } else { + /* Cater for the case of the user downgrading and existing TLS + connection to TCP for some reason...*/ + if (transport == GETDNS_TRANSPORT_TCP && upstream->tls_obj) { + SSL_shutdown(upstream->tls_obj); + SSL_free(upstream->tls_obj); + upstream->tls_obj = NULL; + } } netreq->upstream = upstream; diff --git a/src/test/getdns_query.c b/src/test/getdns_query.c index 524a9a7c..abe21529 100644 --- a/src/test/getdns_query.c +++ b/src/test/getdns_query.c @@ -29,9 +29,23 @@ #include #include #include +#include #include #include +static int quiet = 0; +static int batch_mode = 0; +static char *query_file = NULL; +static int json = 0; +static char *the_root = "."; +static char *name; +static getdns_context *context; +static getdns_dict *extensions; +static uint16_t request_type = GETDNS_RRTYPE_NS; +static int timeout, edns0_size; +static int async = 0, interactive = 0; +static enum { GENERAL, ADDRESS, HOSTNAME, SERVICE } calltype = GENERAL; + int get_rrtype(const char *t); getdns_dict * @@ -90,6 +104,7 @@ print_usage(FILE *out, const char *progname) fprintf(out, "\t-b \tSet edns0 max_udp_payload size\n"); fprintf(out, "\t-D\tSet edns0 do bit\n"); fprintf(out, "\t-d\tclear edns0 do bit\n"); + fprintf(out, "\t-F \tread the queries from the specified file\n"); fprintf(out, "\t-G\tgeneral lookup\n"); fprintf(out, "\t-H\thostname lookup. ( must be an IP address; is ignored)\n"); fprintf(out, "\t-h\tPrint this help\n"); @@ -104,29 +119,45 @@ print_usage(FILE *out, const char *progname) fprintf(out, "\t-t \tSet timeout in miliseconds\n"); fprintf(out, "\t-T\tSet transport to TCP only\n"); fprintf(out, "\t-O\tSet transport to TCP only keep connections open\n"); + fprintf(out, "\t-L\tSet transport to TLS only keep connections open\n"); + fprintf(out, "\t-E\tSet transport to TLS with TCP fallback only keep connections open\n"); fprintf(out, "\t-u\tSet transport to UDP with TCP fallback\n"); fprintf(out, "\t-U\tSet transport to UDP only\n"); + fprintf(out, "\t-B\tBatch mode. Schedule all messages before processing responses.\n"); + fprintf(out, "\t-q\tQuiet mode - don't print response\n"); } void callback(getdns_context *context, getdns_callback_type_t callback_type, getdns_dict *response, void *userarg, getdns_transaction_t trans_id) { - getdns_dict **response_ptr = (getdns_dict **)userarg; + char *response_str; - if (response) - *response_ptr = response; + if (callback_type == GETDNS_CALLBACK_COMPLETE) { + /* This is a callback with data */; + if (!quiet && (response_str = json ? + getdns_print_json_dict(response, json == 1) + : getdns_pretty_print_dict(response))) { + + fprintf(stdout, "ASYNC response:\n%s\n", response_str); + free(response_str); + } + fprintf(stderr, + "The callback with ID %llu was successfull.\n", + (unsigned long long)trans_id); + + } else if (callback_type == GETDNS_CALLBACK_CANCEL) + fprintf(stderr, + "The callback with ID %llu was cancelled. Exiting.\n", + (unsigned long long)trans_id); + else + fprintf(stderr, + "The callback got a callback_type of %d. Exiting.\n", + callback_type); + + getdns_dict_destroy(response); + response = NULL; } -static char *the_root = "."; -static char *name; -static getdns_context *context; -static getdns_dict *extensions; -static uint16_t request_type = GETDNS_RRTYPE_NS; -static int timeout, edns0_size; -static int async = 0, interactive = 0; -static enum { GENERAL, ADDRESS, HOSTNAME, SERVICE } calltype = GENERAL; -static int json = 0; - #define CONTINUE ((getdns_return_t)-2) static getdns_return_t set_cookie(getdns_dict *exts, char *cookie) @@ -259,6 +290,15 @@ getdns_return_t parse_args(int argc, char **argv) case 'd': (void) getdns_context_set_edns_do_bit(context, 0); break; + case 'F': + if (c[1] != 0 || ++i >= argc || !*argv[i]) { + fprintf(stderr, "file name expected " + "after -F\n"); + return GETDNS_RETURN_GENERIC_ERROR; + } + query_file = argv[i]; + interactive = 1; + break; case 'G': calltype = GENERAL; break; @@ -282,6 +322,8 @@ getdns_return_t parse_args(int argc, char **argv) break; case 'p': json = 0; + case 'q': + quiet = 1; break; case 'r': getdns_context_set_resolution_type( @@ -319,6 +361,14 @@ getdns_return_t parse_args(int argc, char **argv) getdns_context_set_dns_transport(context, GETDNS_TRANSPORT_TCP_ONLY_KEEP_CONNECTIONS_OPEN); break; + case 'L': + getdns_context_set_dns_transport(context, + GETDNS_TRANSPORT_TLS_ONLY_KEEP_CONNECTIONS_OPEN); + break; + case 'E': + getdns_context_set_dns_transport(context, + GETDNS_TRANSPORT_TLS_FIRST_AND_FALL_BACK_TO_TCP_KEEP_CONNECTIONS_OPEN); + break; case 'u': getdns_context_set_dns_transport(context, GETDNS_TRANSPORT_UDP_FIRST_AND_FALL_BACK_TO_TCP); @@ -327,6 +377,9 @@ getdns_return_t parse_args(int argc, char **argv) getdns_context_set_dns_transport(context, GETDNS_TRANSPORT_UDP_ONLY); break; + case 'B': + batch_mode = 1; + break; default: @@ -361,6 +414,7 @@ main(int argc, char **argv) char *response_str; getdns_return_t r; getdns_dict *address = NULL; + FILE *fp = NULL; name = the_root; if ((r = getdns_context_create(&context, 1))) { @@ -376,14 +430,28 @@ main(int argc, char **argv) if ((r = parse_args(argc, argv))) goto done_destroy_context; + if (query_file) { + fp = fopen(query_file, "rt"); + if (fp == NULL) { + fprintf(stderr, "Could not open query file: %s\n", query_file); + goto done_destroy_context; + } + } + /* Make the call */ do { char line[1024], *token, *linev[256]; int linec; if (interactive) { - fprintf(stdout, "> "); - if (!fgets(line, 1024, stdin) || !*line) - break; + if (!query_file) { + fprintf(stdout, "> "); + if (!fgets(line, 1024, stdin) || !*line) + break; + } else { + if (!fgets(line, 1024, fp) || !*line) + break; + fprintf(stdout,"Found query: %s", line); + } linev[0] = argv[0]; linec = 1; @@ -430,8 +498,8 @@ main(int argc, char **argv) } if (r) goto done_destroy_extensions; - - getdns_context_run(context); + if (!batch_mode) + getdns_context_run(context); } else { switch (calltype) { case GENERAL: @@ -456,22 +524,29 @@ main(int argc, char **argv) } if (r) goto done_destroy_extensions; - } - if (json) - response_str = getdns_print_json_dict( - response, json == 1); - else - response_str = getdns_pretty_print_dict(response); + if (!quiet) { + if ((response_str = json ? + getdns_print_json_dict(response, json == 1) + : getdns_pretty_print_dict(response))) { - if (response_str) { - fprintf(stdout, "%s\n", response_str); - free(response_str); - } else { - r = GETDNS_RETURN_MEMORY_ERROR; - fprintf(stderr, "Could not print response\n"); + fprintf( stdout, "SYNC response:\n%s\n" + , response_str); + free(response_str); + } else { + r = GETDNS_RETURN_MEMORY_ERROR; + fprintf( stderr + , "Could not print response\n"); + } + } else if (r == GETDNS_RETURN_GOOD) + fprintf(stdout, "Response code was: GOOD\n"); + else if (interactive) + fprintf(stderr, "An error occurred: %d\n", r); } } while (interactive); + if (batch_mode) + getdns_context_run(context); + /* Clean up */ done_destroy_extensions: getdns_dict_destroy(extensions); @@ -479,6 +554,9 @@ done_destroy_context: if (response) getdns_dict_destroy(response); getdns_context_destroy(context); + if (fp) + fclose(fp); + if (r == CONTINUE) return 0; if (r) diff --git a/src/test/tests_stub_async.c b/src/test/tests_stub_async.c index 4c6e0b4a..fb0baf63 100644 --- a/src/test/tests_stub_async.c +++ b/src/test/tests_stub_async.c @@ -44,6 +44,8 @@ #define TRANSPORT_UDP "udp" #define TRANSPORT_TCP "tcp" #define TRANSPORT_PIPELINE "pipeline" +#define TRANSPORT_TLS_KEEPOPEN "tls" +#define TRANSPORT_TLS_TCP_KEEPOPEN "dns-over-tls" #define RESOLUTION_STUB "stub" #define RESOLUTION_REC "rec" @@ -98,6 +100,10 @@ main(int argc, char** argv) getdns_context_set_dns_transport(this_context, GETDNS_TRANSPORT_TCP_ONLY); else if (strncmp(transport, TRANSPORT_PIPELINE, 8) == 0) getdns_context_set_dns_transport(this_context, GETDNS_TRANSPORT_TCP_ONLY_KEEP_CONNECTIONS_OPEN); + else if (strncmp(transport, TRANSPORT_TLS_KEEPOPEN, 3) == 0) + getdns_context_set_dns_transport(this_context, GETDNS_TRANSPORT_TLS_ONLY_KEEP_CONNECTIONS_OPEN); + else if (strncmp(transport, TRANSPORT_TLS_TCP_KEEPOPEN, 12) == 0) + getdns_context_set_dns_transport(this_context, GETDNS_TRANSPORT_TLS_FIRST_AND_FALL_BACK_TO_TCP_KEEP_CONNECTIONS_OPEN); else if (strncmp(transport, TRANSPORT_UDP, 3) != 0) { fprintf(stderr, "Invalid transport %s, must be one of udp, tcp or pipeline\n", transport); exit(EXIT_FAILURE); diff --git a/src/test/tests_stub_sync.c b/src/test/tests_stub_sync.c index 4b04d5fb..496fb7ab 100644 --- a/src/test/tests_stub_sync.c +++ b/src/test/tests_stub_sync.c @@ -41,6 +41,8 @@ #define TRANSPORT_UDP "udp" #define TRANSPORT_TCP "tcp" #define TRANSPORT_PIPELINE "pipeline" +#define TRANSPORT_TLS_KEEPOPEN "tls" +#define TRANSPORT_TLS_TCP_KEEPOPEN "dns-over-tls" #define RESOLUTION_STUB "stub" #define RESOLUTION_REC "rec" @@ -82,6 +84,10 @@ main(int argc, char** argv) getdns_context_set_dns_transport(this_context, GETDNS_TRANSPORT_TCP_ONLY); else if (strncmp(transport, TRANSPORT_PIPELINE, 8) == 0) getdns_context_set_dns_transport(this_context, GETDNS_TRANSPORT_TCP_ONLY_KEEP_CONNECTIONS_OPEN); + else if (strncmp(transport, TRANSPORT_TLS_KEEPOPEN, 3) == 0) + getdns_context_set_dns_transport(this_context, GETDNS_TRANSPORT_TLS_ONLY_KEEP_CONNECTIONS_OPEN); + else if (strncmp(transport, TRANSPORT_TLS_TCP_KEEPOPEN, 12) == 0) + getdns_context_set_dns_transport(this_context, GETDNS_TRANSPORT_TLS_FIRST_AND_FALL_BACK_TO_TCP_KEEP_CONNECTIONS_OPEN); else if (strncmp(transport, TRANSPORT_UDP, 3) != 0) { fprintf(stderr, "Invalid transport %s, must be one of udp, tcp or pipeline\n", transport); exit(EXIT_FAILURE); diff --git a/src/types-internal.h b/src/types-internal.h index 81141bcc..5220d599 100644 --- a/src/types-internal.h +++ b/src/types-internal.h @@ -36,7 +36,6 @@ #ifndef TYPES_INTERNAL_H_ #define TYPES_INTERNAL_H_ -#include #include "getdns/getdns.h" #include "getdns/getdns_extra.h" #include "util/rbtree.h"