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"