From 6e5b62c5554cd39f3316f3344e5a2b6112929689 Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Mon, 31 Oct 2016 13:36:05 +0100 Subject: [PATCH 01/65] Allow conventional IPv6 address/port parsing from getdns_query --- src/test/getdns_str2dict.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/test/getdns_str2dict.c b/src/test/getdns_str2dict.c index 28323d9d..f80df99e 100644 --- a/src/test/getdns_str2dict.c +++ b/src/test/getdns_str2dict.c @@ -33,6 +33,7 @@ #include "list.h" /* For _getdns_list_create_from_mf() */ #include "dict.h" /* For _getdns_dict_create_from_mf() */ #include /* For bsearch */ +#include /* For isspace */ static struct mem_funcs _getdns_plain_mem_funcs = { MF_PLAIN, .mf.pln = { malloc, realloc, free } @@ -102,7 +103,7 @@ static int _gldns_b64_pton(char const *src, uint8_t *target, size_t targsize) } static getdns_dict * -_getdns_ipaddr_dict_mf(struct mem_funcs *mf, char *ipstr) +_getdns_ipaddr_dict_mf(struct mem_funcs *mf, const char *ipstr) { getdns_dict *r = _getdns_dict_create_with_mf(mf); char *s = strchr(ipstr, '%'), *scope_id_str = ""; @@ -645,6 +646,17 @@ getdns_str2dict(const char *str, getdns_dict **dict) getdns_item item; getdns_return_t r; + while (*str && isspace(*str)) + str++; + + if (*str != '{') { + getdns_dict *dict_r = _getdns_ipaddr_dict_mf( + &_getdns_plain_mem_funcs, str); + if (dict_r) { + *dict = dict_r; + return GETDNS_RETURN_GOOD; + } + } if ((r = _getdns_str2item_mf(&_getdns_plain_mem_funcs, str, &item))) return r; From b857e3d7f1b3b2b7089890fdac6563a51d26bb0c Mon Sep 17 00:00:00 2001 From: Neil Cook Date: Tue, 8 Nov 2016 11:46:29 +0000 Subject: [PATCH 02/65] call SSL_library_init() just once and lock with mutexes --- src/context.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/context.c b/src/context.c index 8893191e..27ef5114 100644 --- a/src/context.c +++ b/src/context.c @@ -62,6 +62,11 @@ typedef unsigned short in_port_t; #include #include +#ifdef HAVE_PTHREADS +#include +#endif +#include + #include "config.h" #ifdef HAVE_LIBUNBOUND #include @@ -85,6 +90,11 @@ typedef unsigned short in_port_t; #define GETDNS_STR_PORT_DNS "53" #define GETDNS_STR_PORT_DNS_OVER_TLS "853" +#ifdef HAVE_PTHREADS +static pthread_mutex_t ssl_init_lock = PTHREAD_MUTEX_INITIALIZER; +#endif +static bool ssl_init=false; + void *plain_mem_funcs_user_arg = MF_PLAIN; typedef struct host_name_addrs { @@ -1306,8 +1316,21 @@ getdns_context_create_with_extended_memory_functions( /* 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. */ - if ((set_from_os & 2) == 0) +#ifdef HAVE_PTHREADS + pthread_mutex_lock(&ssl_init_lock); +#else + /* XXX implement Windows-style lock here */ +#endif + /* Only initialise SSL once and ideally in a thread-safe manner */ + if (ssl_init == false) { SSL_library_init(); + ssl_init = true; + } +#ifdef HAVE_PTHREADS + pthread_mutex_unlock(&ssl_init_lock); +#else + /* XXX implement Windows-style unlock here */ +#endif #ifdef HAVE_LIBUNBOUND result->unbound_ctx = NULL; From 1593129b8577481d0706f98a0ea2477b1a4ef89f Mon Sep 17 00:00:00 2001 From: Sara Dickinson Date: Wed, 9 Nov 2016 16:41:40 +0000 Subject: [PATCH 03/65] Fix mishandling of auth state for name mismatch --- src/stub.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/stub.c b/src/stub.c index bb18f68b..d73798f4 100644 --- a/src/stub.c +++ b/src/stub.c @@ -830,6 +830,8 @@ tls_verify_callback(int preverify_ok, X509_STORE_CTX *ctx) getdns_upstream *upstream; getdns_return_t pinset_ret = GETDNS_RETURN_GOOD; upstream = _getdns_upstream_from_x509_store(ctx); + if (!upstream) + return 0; #if defined(STUB_DEBUG) && STUB_DEBUG || defined(X509_V_ERR_HOSTNAME_MISMATCH) int err = X509_STORE_CTX_get_error(ctx); @@ -841,10 +843,11 @@ tls_verify_callback(int preverify_ok, X509_STORE_CTX *ctx) #ifdef X509_V_ERR_HOSTNAME_MISMATCH /*Report if error is hostname mismatch*/ - if (upstream && upstream->tls_fallback_ok && err == X509_V_ERR_HOSTNAME_MISMATCH) { - DEBUG_STUB("%s %-35s: FD: %d WARNING: Proceeding even though hostname validation failed!\n", - STUB_DEBUG_SETUP_TLS, __FUNCTION__, upstream->fd); + if (err == X509_V_ERR_HOSTNAME_MISMATCH) { upstream->tls_auth_state = GETDNS_AUTH_FAILED; + if (upstream->tls_fallback_ok) + DEBUG_STUB("%s %-35s: FD: %d WARNING: Proceeding even though hostname validation failed!\n", + STUB_DEBUG_SETUP_TLS, __FUNCTION__, upstream->fd); } #else /* if we weren't built against OpenSSL with hostname matching we @@ -853,7 +856,7 @@ tls_verify_callback(int preverify_ok, X509_STORE_CTX *ctx) if (upstream->tls_auth_name[0]) upstream->tls_auth_state = GETDNS_AUTH_FAILED; #endif - if (upstream && upstream->tls_pubkey_pinset) + if (upstream->tls_pubkey_pinset) pinset_ret = _getdns_verify_pinset_match(upstream->tls_pubkey_pinset, ctx); if (pinset_ret != GETDNS_RETURN_GOOD) { @@ -871,7 +874,7 @@ tls_verify_callback(int preverify_ok, X509_STORE_CTX *ctx) upstream->tls_auth_state = GETDNS_AUTH_OK; /* If fallback is allowed, proceed regardless of what the auth error is (might not be hostname or pinset related) */ - return (upstream && upstream->tls_fallback_ok) ? 1 : preverify_ok; + return (upstream->tls_fallback_ok) ? 1 : preverify_ok; } static SSL* From 73165b235fb42f1e0ea0482ab5760a68768b4f05 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Sat, 12 Nov 2016 16:53:21 +0900 Subject: [PATCH 04/65] Allow public key pins higher in the chain than the EE cert This resolves an old TODO; we'd never tested pinning any certs higher than the end-entity cert before. --- src/pubkey-pinning.c | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/src/pubkey-pinning.c b/src/pubkey-pinning.c index 7d286691..25966a63 100644 --- a/src/pubkey-pinning.c +++ b/src/pubkey-pinning.c @@ -382,10 +382,10 @@ _getdns_verify_pinset_match(const sha256_pin_t *pinset, X509_STORE_CTX *store) { getdns_return_t ret = GETDNS_RETURN_GENERIC_ERROR; - X509 *x; + X509 *x, *prev; int i, len; unsigned char raw[4096]; - unsigned char *next = raw; + unsigned char *next; unsigned char buf[sizeof(pinset->pin)]; const sha256_pin_t *p; @@ -408,13 +408,6 @@ _getdns_verify_pinset_match(const sha256_pin_t *pinset, /* TODO: how do we handle raw public keys? */ for (i = 0; i < sk_X509_num(X509_STORE_CTX_get0_untrusted(store)); i++) { - if (i > 0) { - /* TODO: how do we ensure that the certificates in - * each stage appropriately sign the previous one? - * for now, to be safe, we only examine the end-entity - * cert: */ - return GETDNS_RETURN_GENERIC_ERROR; - } x = sk_X509_value(X509_STORE_CTX_get0_untrusted(store), i); #if defined(STUB_DEBUG) && STUB_DEBUG @@ -423,6 +416,24 @@ _getdns_verify_pinset_match(const sha256_pin_t *pinset, X509_NAME_print_ex_fp(stderr, X509_get_subject_name(x), 1, XN_FLAG_ONELINE); fprintf(stderr, "\n"); #endif + if (i > 0) { + /* we ensure that "prev" is signed by "x" */ + EVP_PKEY *pkey = X509_get_pubkey(x); + int verified; + if (!pkey) { + DEBUG_STUB("%s %-35s: Could not get pubkey from cert %d (%p)\n", + STUB_DEBUG_SETUP_TLS, __FUNCTION__, i, x); + return GETDNS_RETURN_GENERIC_ERROR; + } + verified = X509_verify(prev, pkey); + EVP_PKEY_free(pkey); + if (!verified) { + DEBUG_STUB("%s %-35s: cert %d (%p) was not signed by cert %d\n", + STUB_DEBUG_SETUP_TLS, __FUNCTION__, i-1, prev, i); + return GETDNS_RETURN_GENERIC_ERROR; + } + } + /* digest the cert with sha256 */ len = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(x), NULL); if (len > sizeof(raw)) { @@ -430,6 +441,7 @@ _getdns_verify_pinset_match(const sha256_pin_t *pinset, STUB_DEBUG_SETUP_TLS, __FUNCTION__, i, sizeof(raw)); continue; } + next = raw; i2d_X509_PUBKEY(X509_get_X509_PUBKEY(x), &next); if (next - raw != len) { DEBUG_STUB("%s %-35s: Pubkey %d claimed it needed %d octets, really needed "PRIsz"\n", @@ -447,6 +459,7 @@ _getdns_verify_pinset_match(const sha256_pin_t *pinset, } else DEBUG_STUB("%s %-35s: Pubkey %d did not match pin %p\n", STUB_DEBUG_SETUP_TLS, __FUNCTION__, i, p); + prev = x; } return ret; From b0e5f87984fc827d7ea93957862fb366a3ddbb19 Mon Sep 17 00:00:00 2001 From: Sara Dickinson Date: Sun, 13 Nov 2016 13:14:03 +0900 Subject: [PATCH 05/65] Minor logging updates --- src/context.c | 8 ++++++-- src/stub.c | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/context.c b/src/context.c index b8492380..121c536a 100644 --- a/src/context.c +++ b/src/context.c @@ -720,12 +720,16 @@ _getdns_upstream_shutdown(getdns_upstream *upstream) STUB_DEBUG_DAEMON, upstream->addr_str, (int)upstream->responses_received, (int)upstream->responses_timeouts, getdns_auth_str_array[upstream->tls_auth_state], (int)upstream->keepalive_timeout); - DEBUG_DAEMON("%s %s : Upstream stats - Resp=%d,Timeouts=%d,Best_auth=%s,Conns=%d,Conn_fails=%d,Conn_shutdowns=%d,Backoffs=%d\n", + DEBUG_DAEMON("%s %s : Upstream stats - Resp=%d,Timeouts=%d,Best_auth=%s,Conns=%d\n", STUB_DEBUG_DAEMON, upstream->addr_str, (int)upstream->total_responses, (int)upstream->total_timeouts, getdns_auth_str_array[upstream->best_tls_auth_state], - (int)upstream->conn_completed, (int)upstream->conn_setup_failed, + (int)upstream->conn_completed); + DEBUG_DAEMON("%s %s : Upstream stats - Conn_fails=%d,Conn_shutdowns=%d,Backoffs=%d\n", + STUB_DEBUG_DAEMON, upstream->addr_str, + (int)upstream->conn_setup_failed, (int)upstream->conn_shutdowns, (int)upstream->conn_backoffs); + #endif /* Back off connections that never got up service at all (probably no diff --git a/src/stub.c b/src/stub.c index d73798f4..2527ed61 100644 --- a/src/stub.c +++ b/src/stub.c @@ -1523,7 +1523,7 @@ upstream_write_cb(void *userarg) /* Fall through */ case STUB_SETUP_ERROR: /* Could not complete the set up. Need to fallback.*/ - DEBUG_STUB("%s %-35s: MSG: %p ERROR = %d\n", STUB_DEBUG_WRITE, + DEBUG_STUB("%s %-35s: Upstream: %p ERROR = %d\n", STUB_DEBUG_WRITE, __FUNCTION__, ((getdns_network_req *)userarg), q); upstream_failed(upstream, (q == STUB_TCP_ERROR ? 0:1)); /* Fall through */ From 1ba2e5bf4de515c7f02c679cce692fe5364b117a Mon Sep 17 00:00:00 2001 From: Sara Dickinson Date: Sun, 20 Nov 2016 11:19:08 +0000 Subject: [PATCH 06/65] Add stubby to readme. Add transport to stubby log. --- README.md | 21 +++++++++++++++++++-- src/context.c | 11 +++++------ 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 1b5a4807..c1a8bae4 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,8 @@ Traditional access to DNS data from applications has several limitations: * Sophisticated uses of the DNS (things like IDNA and DNSSEC validation) require considerable application work, possibly by application developers with little experience with the vagaries of DNS. +getdns also provides a prototype DNS Privacy enabled client called 'stubby' - see below for more details. + ## Motivation for providing the API The developers are of the opinion that DNSSEC offers a unique global infrastructure for establishing and enhancing cryptographic trust relations. With the development of this API we intend to offer application developers a modern and flexible interface that enables end-to-end trust in the DNS architecture, and which will inspire application developers to implement innovative security solutions in their applications. @@ -73,9 +75,13 @@ If you want to make use of the configuration files that utilise a JSON-like form before building. -If you want to use the getdns_query command line wrapper script for testing or to enable getdns as a daemon then you must build it using +As well as building the getdns library 2 other tools are installed by default by the above process: + +* getdns_query: a command line test script wrapper for getdns +* stubby: a DNS Privacy enabled client + +Note: If you only want to build stubby, then use the `--enable-stub-only` and `--without-libidn` options when running 'configure'. - # make getdns_query ## Minimizing dependencies @@ -91,6 +97,17 @@ The implementation works with a variety of event loops, each built as a separate * [libuv](https://github.com/joyent/libuv) * [libev](http://software.schmorp.de/pkg/libev.html) +## Stubby + +* Stubby is a prototype implementation of a DNS Privacy enabled stub resolver. Feedback is welcome! +* A default configuration file is available here uses a 'Strict' privacy usage profile using some of the available test DNS Privacy servers to resolve queries. Note these servers are test servers that offer no service guarantees. An alternative file can be specified with the '-C' flag +* If you would like minimal logging output from Stubby (which servers are used and connection level statistics) then also use the '--enable-debug-daemon' flag when running 'configure'. + +To use stubby +* Start stubby from the command line +* Test it by doing, for example, 'dig @127.0.0.1 www.example.com' +* Alter the default DNS resolvers on your system to point at localhost (127.0.0.1, ::1) + ## Regression Tests A suite of regression tests are included with the library, if you make changes or just diff --git a/src/context.c b/src/context.c index 121c536a..1d9a046b 100644 --- a/src/context.c +++ b/src/context.c @@ -720,16 +720,15 @@ _getdns_upstream_shutdown(getdns_upstream *upstream) STUB_DEBUG_DAEMON, upstream->addr_str, (int)upstream->responses_received, (int)upstream->responses_timeouts, getdns_auth_str_array[upstream->tls_auth_state], (int)upstream->keepalive_timeout); - DEBUG_DAEMON("%s %s : Upstream stats - Resp=%d,Timeouts=%d,Best_auth=%s,Conns=%d\n", + DEBUG_DAEMON("%s %s : Upstream stats - Resp=%d,Timeouts=%d,Transport=%s,Best_auth=%s\n", STUB_DEBUG_DAEMON, upstream->addr_str, (int)upstream->total_responses, (int)upstream->total_timeouts, - getdns_auth_str_array[upstream->best_tls_auth_state], - (int)upstream->conn_completed); - DEBUG_DAEMON("%s %s : Upstream stats - Conn_fails=%d,Conn_shutdowns=%d,Backoffs=%d\n", + (upstream->transport == GETDNS_TRANSPORT_TLS ? "TLS" : "UDP/TCP"), + getdns_auth_str_array[upstream->best_tls_auth_state]); + DEBUG_DAEMON("%s %s : Upstream stats - Conns=%d,Conn_fails=%d,Conn_shutdowns=%d,Backoffs=%d\n", STUB_DEBUG_DAEMON, upstream->addr_str, - (int)upstream->conn_setup_failed, + (int)upstream->conn_completed, (int)upstream->conn_setup_failed, (int)upstream->conn_shutdowns, (int)upstream->conn_backoffs); - #endif /* Back off connections that never got up service at all (probably no From 0d13ae6d72e59e42b6905f98b57089e31817e7fd Mon Sep 17 00:00:00 2001 From: Christian Huitema Date: Sun, 4 Dec 2016 17:26:38 -0800 Subject: [PATCH 07/65] Fixing several issues in function set_os_defaults_windows that prevent working on Windows. --- src/context.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/context.c b/src/context.c index 8893191e..010b1685 100644 --- a/src/context.c +++ b/src/context.c @@ -929,6 +929,7 @@ set_os_defaults_windows(struct getdns_context *context) getdns_upstream *upstream; size_t length; int s; + uint32_t info_err = 0; if (context->fchg_resolvconf == NULL) { context->fchg_resolvconf = @@ -961,15 +962,16 @@ set_os_defaults_windows(struct getdns_context *context) if (info == NULL) return GETDNS_RETURN_GENERIC_ERROR; - if (GetNetworkParams(info, &buflen) == ERROR_BUFFER_OVERFLOW) { + if ((info_err = GetNetworkParams(info, &buflen)) == ERROR_BUFFER_OVERFLOW) { free(info); info = (FIXED_INFO *)malloc(buflen); if (info == NULL) return GETDNS_RETURN_GENERIC_ERROR; + info_err = GetNetworkParams(info, &buflen); } - if (GetNetworkParams(info, &buflen) == NO_ERROR) { - ptr = info->DnsServerList.Next; + if (info_err == NO_ERROR) { + ptr = &info->DnsServerList; *domain = 0; while (ptr) { for (size_t i = 0; i < GETDNS_UPSTREAM_TRANSPORTS; i++) { @@ -986,11 +988,12 @@ set_os_defaults_windows(struct getdns_context *context) freeaddrinfo(result); } ptr = ptr->Next; - } - free(info); } + if (info != NULL) + free(info); + suffix = getdns_list_create_with_context(context); if (get_dns_suffix_windows(suffix, domain)) { From 576e38977f746fc5aa8af2127d37ce12e7a419db Mon Sep 17 00:00:00 2001 From: Sara Dickinson Date: Mon, 5 Dec 2016 18:05:04 +0000 Subject: [PATCH 08/65] More logging changes to stubby to correctly report profile, transport and stats for TCP and UDP when used as fallbacks. Reporting UDP stats every 100 responses or timeouts to give user some indication UDP is being used. --- src/context.c | 12 ++++++++---- src/context.h | 2 ++ src/stub.c | 24 ++++++++++++++++++++---- 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/context.c b/src/context.c index 1d9a046b..8b8413f8 100644 --- a/src/context.c +++ b/src/context.c @@ -716,17 +716,19 @@ _getdns_upstream_shutdown(getdns_upstream *upstream) if (upstream->tls_auth_state > upstream->best_tls_auth_state) upstream->best_tls_auth_state = upstream->tls_auth_state; #if defined(DAEMON_DEBUG) && DAEMON_DEBUG - DEBUG_DAEMON("%s %s : Conn closed: Conn stats - Resp=%d,Timeouts=%d,Auth=%s,Keepalive(ms)=%d\n", + DEBUG_DAEMON("%s %s : Conn closed : Transport=%s - Resp=%d,Timeouts=%d,Auth=%s,Keepalive(ms)=%d\n", STUB_DEBUG_DAEMON, upstream->addr_str, + (upstream->transport == GETDNS_TRANSPORT_TLS ? "TLS" : "TCP"), (int)upstream->responses_received, (int)upstream->responses_timeouts, getdns_auth_str_array[upstream->tls_auth_state], (int)upstream->keepalive_timeout); - DEBUG_DAEMON("%s %s : Upstream stats - Resp=%d,Timeouts=%d,Transport=%s,Best_auth=%s\n", + DEBUG_DAEMON("%s %s : Upstream stats: Transport=%s - Resp=%d,Timeouts=%d,Best_auth=%s\n", STUB_DEBUG_DAEMON, upstream->addr_str, + (upstream->transport == GETDNS_TRANSPORT_TLS ? "TLS" : "TCP"), (int)upstream->total_responses, (int)upstream->total_timeouts, - (upstream->transport == GETDNS_TRANSPORT_TLS ? "TLS" : "UDP/TCP"), getdns_auth_str_array[upstream->best_tls_auth_state]); - DEBUG_DAEMON("%s %s : Upstream stats - Conns=%d,Conn_fails=%d,Conn_shutdowns=%d,Backoffs=%d\n", + DEBUG_DAEMON("%s %s : Upstream stats: Transport=%s - Conns=%d,Conn_fails=%d,Conn_shutdowns=%d,Backoffs=%d\n", STUB_DEBUG_DAEMON, upstream->addr_str, + (upstream->transport == GETDNS_TRANSPORT_TLS ? "TLS" : "TCP"), (int)upstream->conn_completed, (int)upstream->conn_setup_failed, (int)upstream->conn_shutdowns, (int)upstream->conn_backoffs); #endif @@ -903,6 +905,8 @@ upstream_init(getdns_upstream *upstream, /* How is this upstream doing on UDP? */ upstream->to_retry = 2; upstream->back_off = 1; + upstream->udp_responses = 0; + upstream->udp_timeouts = 0; /* For sharing a socket to this upstream with TCP */ upstream->fd = -1; diff --git a/src/context.h b/src/context.h index 41e7a830..fdf8066e 100644 --- a/src/context.h +++ b/src/context.h @@ -131,6 +131,8 @@ typedef struct getdns_upstream { /* How is this upstream doing over UDP? */ int to_retry; int back_off; + size_t udp_responses; + size_t udp_timeouts; /* For stateful upstreams, need to share the connection and track the activity on the connection */ diff --git a/src/stub.c b/src/stub.c index 2527ed61..83ab992c 100644 --- a/src/stub.c +++ b/src/stub.c @@ -571,6 +571,13 @@ stub_timeout_cb(void *userarg) /* Handle upstream*/ if (netreq->fd >= 0) { close(netreq->fd); + netreq->upstream->udp_timeouts++; +#if defined(DAEMON_DEBUG) && DAEMON_DEBUG + if (netreq->upstream->udp_timeouts % 100 == 0) + DEBUG_DAEMON("%s %s : Upstream stats: Transport=UDP - Resp=%d,Timeouts=%d\n", + STUB_DEBUG_DAEMON, netreq->upstream->addr_str, + (int)netreq->upstream->udp_responses, (int)netreq->upstream->udp_timeouts); +#endif stub_next_upstream(netreq); } else { netreq->upstream->responses_timeouts++; @@ -1305,6 +1312,13 @@ stub_udp_read_cb(void *userarg) dnsreq->upstreams->current_udp = 0; netreq->debug_end_time = _getdns_get_time_as_uintt64(); netreq->state = NET_REQ_FINISHED; + upstream->udp_responses++; +#if defined(DAEMON_DEBUG) && DAEMON_DEBUG + if (upstream->udp_responses % 100 == 0) + DEBUG_DAEMON("%s %s : Upstream stats: Transport=UDP - Resp=%d,Timeouts=%d\n", + STUB_DEBUG_DAEMON, upstream->addr_str, + (int)upstream->udp_responses, (int)upstream->udp_timeouts); +#endif _getdns_check_dns_req_complete(dnsreq); } @@ -1767,15 +1781,16 @@ upstream_connect(getdns_upstream *upstream, getdns_transport_list_t transport, upstream->tls_hs_state = GETDNS_HS_WRITE; } upstream->conn_state = GETDNS_CONN_SETUP; +#if defined(DAEMON_DEBUG) && DAEMON_DEBUG + DEBUG_DAEMON("%s %s : Conn init : Transport= %s - Profile=%s\n", STUB_DEBUG_DAEMON, + upstream->addr_str, transport == GETDNS_TRANSPORT_TLS ? "TLS":"TCP", + dnsreq->context->tls_auth_min == GETDNS_AUTHENTICATION_NONE ? "Opportunistic":"Strict"); +#endif break; default: return -1; /* Nothing to do*/ } -#if defined(DAEMON_DEBUG) && DAEMON_DEBUG - DEBUG_DAEMON("%s %s : Conn init\n", - STUB_DEBUG_DAEMON, upstream->addr_str); -#endif return fd; } @@ -1841,6 +1856,7 @@ fallback_on_write(getdns_network_req *netreq) /* Deal with UDP one day*/ DEBUG_STUB("%s %-35s: MSG: %p FALLING BACK \n", STUB_DEBUG_SCHEDULE, __FUNCTION__, netreq); + DEBUG_DAEMON("%s Falling back...\n", STUB_DEBUG_DAEMON); /* Try to find a fallback transport*/ getdns_return_t result = _getdns_submit_stub_request(netreq); From dee33f53b65e03dd3d31da781863867e9d03bb7a Mon Sep 17 00:00:00 2001 From: Christian Huitema Date: Mon, 5 Dec 2016 11:38:59 -0800 Subject: [PATCH 09/65] Reminder of changes required by the Windows port. This solves the issues 228, 229, 230 and 232. --- src/compat/arc4random.c | 11 +++++ src/compat/gettimeofday.c | 73 +++++++++++++++++++++++++++++++ src/extension/default_eventloop.c | 16 +++---- src/stub.c | 41 +++++++++++++++-- 4 files changed, 129 insertions(+), 12 deletions(-) create mode 100644 src/compat/gettimeofday.c diff --git a/src/compat/arc4random.c b/src/compat/arc4random.c index 2c78818f..bd5f6e3a 100644 --- a/src/compat/arc4random.c +++ b/src/compat/arc4random.c @@ -38,6 +38,9 @@ #ifndef GETDNS_ON_WINDOWS #include #endif +#if defined(GETDNS_ON_WINDOWS) && !defined(MAP_INHERIT_ZERO) +#define explicit_bzero(rnd, rnd_size) memset(rnd, 0, rnd_size) +#endif #define KEYSTREAM_ONLY #include "chacha_private.h" @@ -136,7 +139,15 @@ _rs_stir_if_needed(size_t len) { #ifndef MAP_INHERIT_ZERO static pid_t _rs_pid = 0; +#ifdef GETDNS_ON_WINDOWS + /* + * TODO: if compiling for the Windows Runtime, use GetCurrentProcessId(), + * but this requires linking with kernel32.lib + */ + pid_t pid = _getpid(); +#else pid_t pid = getpid(); +#endif /* If a system lacks MAP_INHERIT_ZERO, resort to getpid() */ if (_rs_pid == 0 || _rs_pid != pid) { diff --git a/src/compat/gettimeofday.c b/src/compat/gettimeofday.c new file mode 100644 index 00000000..d8fe91bc --- /dev/null +++ b/src/compat/gettimeofday.c @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2016 Christian Huitema + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + + /* + * Numerous places in the code make reference to the Unix/Linux + * "gettimeofday()" function, which is not available in the standard + * windows libraries. This code provides a compatible implementation. + */ +#include "config.h" + +#ifdef GETDNS_ON_WINDOWS +int gettimeofday(struct timeval* tv, struct timezone* tz) +{ + FILETIME ft; + uint64_t now = 0; + + /* + * The GetSystemTimeAsFileTime API returns the number + * of 100-nanosecond intervals since January 1, 1601 (UTC), + * in FILETIME format. + */ + GetSystemTimeAsFileTime(&ft); + + /* + * Convert to plain 64 bit format, without making + * assumptions about the FILETIME structure alignment. + */ + now |= ft.dwHighDateTime; + now <<= 32; + now |= ft.dwLowDateTime; + /* + * Convert units from 100ns to 1us + */ + now /= 10; + /* + * Account for microseconds elapsed between 1601 and 1970. + */ + now -= 11644473600000000ULL; + + if (tv != NULL) + { + uint64_t sec = now / 1000000; + uint64_t usec = now % 1000000; + + tv->tv_sec = (long)sec; + tv->tv_usec = (long)usec; + } + + if (tz != NULL) + { + /* + * TODO: implement a timezone retrieval function. + * Not urgent, since the GetDNS code always set this parameter to NULL. + */ + return -1; + } + + return 0; +} +#endif /* GETDNS_ON_WINDOWS */ \ No newline at end of file diff --git a/src/extension/default_eventloop.c b/src/extension/default_eventloop.c index 10efff59..2f9856dd 100644 --- a/src/extension/default_eventloop.c +++ b/src/extension/default_eventloop.c @@ -25,6 +25,8 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "Config.h" + #include "extension/default_eventloop.h" #include "debug.h" @@ -32,7 +34,7 @@ static uint64_t get_now_plus(uint64_t amount) { struct timeval tv; uint64_t now; - + if (gettimeofday(&tv, NULL)) { perror("gettimeofday() failed"); exit(EXIT_FAILURE); @@ -81,8 +83,7 @@ default_eventloop_schedule(getdns_eventloop *loop, #endif default_loop->fd_events[fd] = event; default_loop->fd_timeout_times[fd] = get_now_plus(timeout); - event->ev = (void *) (intptr_t) fd + 1; - + event->ev = (void *)(intptr_t)(fd + 1); DEBUG_SCHED( "scheduled read/write at %d\n", fd); return GETDNS_RETURN_GOOD; } @@ -101,9 +102,8 @@ default_eventloop_schedule(getdns_eventloop *loop, for (i = 0; i < MAX_TIMEOUTS; i++) { if (default_loop->timeout_events[i] == NULL) { default_loop->timeout_events[i] = event; - default_loop->timeout_times[i] = get_now_plus(timeout); - event->ev = (void *) (intptr_t) i + 1; - + default_loop->timeout_times[i] = get_now_plus(timeout); + event->ev = (void *)(intptr_t)(i + 1); DEBUG_SCHED( "scheduled timeout at %d\n", (int)i); return GETDNS_RETURN_GOOD; } @@ -219,8 +219,8 @@ default_eventloop_run_once(getdns_eventloop *loop, int blocking) tv.tv_sec = 0; tv.tv_usec = 0; } else { - tv.tv_sec = (timeout - now) / 1000000; - tv.tv_usec = (timeout - now) % 1000000; + tv.tv_sec = (long)((timeout - now) / 1000000); + tv.tv_usec = (long)((timeout - now) % 1000000); } if (select(max_fd + 1, &readfds, &writefds, NULL, (timeout == ((uint64_t)-1) ? NULL : &tv)) < 0) { diff --git a/src/stub.c b/src/stub.c index 4aa14dae..8232e288 100644 --- a/src/stub.c +++ b/src/stub.c @@ -399,7 +399,11 @@ tcp_connect(getdns_upstream *upstream, getdns_transport_list_t transport) upstream->addr_len) == -1) { if (_getdns_EINPROGRESS || _getdns_EWOULDBLOCK) return fd; +#ifdef USE_WINSOCK + closesocket(fd); +#else close(fd); +#endif return -1; } return fd; @@ -541,7 +545,13 @@ void _getdns_cancel_stub_request(getdns_network_req *netreq) { stub_cleanup(netreq); - if (netreq->fd >= 0) close(netreq->fd); + if (netreq->fd >= 0) { +#ifdef USE_WINSOCK + closesocket(netreq->fd); +#else + close(netreq->fd); +#endif + } } /* May be needed in future for better UDP error handling?*/ @@ -564,7 +574,12 @@ stub_timeout_cb(void *userarg) STUB_DEBUG_CLEANUP, __FUNCTION__, netreq); stub_next_upstream(netreq); stub_cleanup(netreq); - if (netreq->fd >= 0) close(netreq->fd); + if (netreq->fd >= 0) +#ifdef USE_WINSOCK + closesocket(netreq->fd); +#else + close(netreq->fd); +#endif netreq->state = NET_REQ_TIMED_OUT; if (netreq->owner->user_callback) { netreq->debug_end_time = _getdns_get_time_as_uintt64(); @@ -807,8 +822,13 @@ stub_tcp_write(int fd, getdns_tcp_state *tcp, getdns_network_req *netreq) /* Coming back from an earlier unfinished write or handshake. * Try to send remaining data */ +#ifdef USE_WINSOCK + written = send(fd, tcp->write_buf + tcp->written, + tcp->write_buf_len - tcp->written, 0); +#else written = write(fd, tcp->write_buf + tcp->written, tcp->write_buf_len - tcp->written); +#endif if (written == -1) { if (_getdns_EWOULDBLOCK) return STUB_TCP_WOULDBLOCK; @@ -1257,10 +1277,10 @@ stub_tls_write(getdns_upstream *upstream, getdns_tcp_state *tcp, static uint64_t _getdns_get_time_as_uintt64() { - + struct timeval tv; uint64_t now; - + if (gettimeofday(&tv, NULL)) { return 0; } @@ -1268,6 +1288,7 @@ _getdns_get_time_as_uintt64() { return now; } + /**************************/ /* UDP callback functions */ /**************************/ @@ -1305,7 +1326,11 @@ stub_udp_read_cb(void *userarg) upstream, netreq->response, read)) return; /* Client cookie didn't match? */ +#ifdef USE_WINSOCK + closesocket(netreq->fd); +#else close(netreq->fd); +#endif while (GLDNS_TC_WIRE(netreq->response)) { DEBUG_STUB("%s %-35s: MSG: %p TC bit set in response \n", STUB_DEBUG_READ, __FUNCTION__, netreq); @@ -1369,7 +1394,11 @@ stub_udp_write_cb(void *userarg) netreq->fd, (const void *)netreq->query, pkt_len, 0, (struct sockaddr *)&netreq->upstream->addr, netreq->upstream->addr_len)) { +#ifdef USE_WINSOCK + closesocket(netreq->fd); +#else close(netreq->fd); +#endif return; } GETDNS_SCHEDULE_EVENT( @@ -1706,7 +1735,11 @@ upstream_connect(getdns_upstream *upstream, getdns_transport_list_t transport, if (fd == -1) return -1; upstream->tls_obj = tls_create_object(dnsreq, fd, upstream); if (upstream->tls_obj == NULL) { +#ifdef USE_WINSOCK + closesocket(fd); +#else close(fd); +#endif return -1; } From 471e8725e2611031e2a53dd30c7fda65e793f359 Mon Sep 17 00:00:00 2001 From: Sara Dickinson Date: Tue, 6 Dec 2016 14:44:40 +0000 Subject: [PATCH 10/65] Change the default profile for Stubby to use TLS then UDP/TCP - this will only try over TLS a few times before backing off to clear text - but makes the default for Stubby opportunistic privacy (Willem - WDYT?) Also use padding and ECS privacy by default for Stubby. More debugging to help users when there are failures or fallbacks. Also remove a few help options from Stubby that don't apply Add -v to output version on getdns_query/stubby --- src/context.c | 2 +- src/stub.c | 11 +++++++--- src/tools/getdns_query.c | 46 ++++++++++++++++++++++++++++------------ 3 files changed, 42 insertions(+), 17 deletions(-) diff --git a/src/context.c b/src/context.c index 8b8413f8..d0fb6b39 100644 --- a/src/context.c +++ b/src/context.c @@ -755,7 +755,7 @@ _getdns_upstream_shutdown(getdns_upstream *upstream) upstream->conn_shutdowns = 0; upstream->conn_backoffs++; #if defined(DAEMON_DEBUG) && DAEMON_DEBUG - DEBUG_DAEMON("%s %s : !Backing off this upstream - will retry as new upstream at %s\n", + DEBUG_DAEMON("%s %s : !Backing off this upstream - Will retry as new upstream at %s", STUB_DEBUG_DAEMON, upstream->addr_str, asctime(gmtime(&upstream->conn_retry_time))); #endif diff --git a/src/stub.c b/src/stub.c index 83ab992c..ef74beb8 100644 --- a/src/stub.c +++ b/src/stub.c @@ -1314,7 +1314,8 @@ stub_udp_read_cb(void *userarg) netreq->state = NET_REQ_FINISHED; upstream->udp_responses++; #if defined(DAEMON_DEBUG) && DAEMON_DEBUG - if (upstream->udp_responses % 100 == 0) + if (upstream->udp_responses == 1 || + upstream->udp_responses % 100 == 0) DEBUG_DAEMON("%s %s : Upstream stats: Transport=UDP - Resp=%d,Timeouts=%d\n", STUB_DEBUG_DAEMON, upstream->addr_str, (int)upstream->udp_responses, (int)upstream->udp_timeouts); @@ -1545,6 +1546,9 @@ upstream_write_cb(void *userarg) case STUB_NO_AUTH: /* Cleaning up after connection or auth check failure. Need to fallback. */ stub_cleanup(netreq); + DEBUG_DAEMON("%s %s : Conn closed : Transport=%s - *Failure*\n", + STUB_DEBUG_DAEMON, upstream->addr_str, + (upstream->transport == GETDNS_TRANSPORT_TLS ? "TLS" : "TCP")); if (fallback_on_write(netreq) == STUB_TCP_ERROR) { /* TODO: Need new state to report transport unavailable*/ netreq->state = NET_REQ_FINISHED; @@ -1782,7 +1786,7 @@ upstream_connect(getdns_upstream *upstream, getdns_transport_list_t transport, } upstream->conn_state = GETDNS_CONN_SETUP; #if defined(DAEMON_DEBUG) && DAEMON_DEBUG - DEBUG_DAEMON("%s %s : Conn init : Transport= %s - Profile=%s\n", STUB_DEBUG_DAEMON, + DEBUG_DAEMON("%s %s : Conn init : Transport=%s - Profile=%s\n", STUB_DEBUG_DAEMON, upstream->addr_str, transport == GETDNS_TRANSPORT_TLS ? "TLS":"TCP", dnsreq->context->tls_auth_min == GETDNS_AUTHENTICATION_NONE ? "Opportunistic":"Strict"); #endif @@ -1843,6 +1847,8 @@ upstream_find_for_netreq(getdns_network_req *netreq) } /* Handle better, will give generic error*/ DEBUG_STUB("%s %-35s: MSG: %p No valid upstream! \n", STUB_DEBUG_SCHEDULE, __FUNCTION__, netreq); + DEBUG_DAEMON("%s *FAILURE* no valid transports or upstreams available!\n", + STUB_DEBUG_DAEMON); return -1; } @@ -1856,7 +1862,6 @@ fallback_on_write(getdns_network_req *netreq) /* Deal with UDP one day*/ DEBUG_STUB("%s %-35s: MSG: %p FALLING BACK \n", STUB_DEBUG_SCHEDULE, __FUNCTION__, netreq); - DEBUG_DAEMON("%s Falling back...\n", STUB_DEBUG_DAEMON); /* Try to find a fallback transport*/ getdns_return_t result = _getdns_submit_stub_request(netreq); diff --git a/src/tools/getdns_query.c b/src/tools/getdns_query.c index 92a27337..089f005e 100644 --- a/src/tools/getdns_query.c +++ b/src/tools/getdns_query.c @@ -51,8 +51,11 @@ typedef unsigned short in_port_t; static int i_am_stubby = 0; static const char *default_stubby_config = "{ resolution_type: GETDNS_RESOLUTION_STUB" +", dns_transport_list: [ GETDNS_TRANSPORT_TLS, GETDNS_TRANSPORT_UDP, GETDNS_TRANSPORT_TCP ]" ", idle_timeout: 10000" ", listen_addresses: [ 127.0.0.1@53, 0::1@53 ]" +", tls_query_padding_blocksize: 256" +", edns_client_subnet_private : 1" "}"; static int clear_listen_list_on_arg = 0; #ifndef GETDNS_ON_WINDOWS @@ -161,13 +164,19 @@ print_usage(FILE *out, const char *progname) { fprintf(out, "usage: %s [