Enable GETDNS_TRANSPORT_TLS_ONLY_KEEP_CONNECTIONS_OPEN for libunbound. Should only be used in stub mode.

GETDNS_TRANSPORT_TLS_FIRST_AND_FALL_BACK_TO_TCP_KEEP_CONNECTIONS_OPEN still just does TCP.
Also some tidy up of new transport types.
This commit is contained in:
Sara Dickinson 2015-04-17 15:50:08 +01:00
parent 99c1973fae
commit ab4fb8d9e9
3 changed files with 80 additions and 70 deletions

View File

@ -521,7 +521,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)
*
@ -533,9 +534,14 @@ 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", (int)upstream_port(upstream));
"@%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));
}
}
static int
@ -809,6 +815,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;
@ -1125,6 +1134,33 @@ getdns_context_set_namespaces(struct getdns_context *context,
return GETDNS_RETURN_GOOD;
} /* getdns_context_set_namespaces */
getdns_transport_t
priv_get_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) {
@ -1138,22 +1174,22 @@ set_ub_dns_transport(struct getdns_context* context,
set_ub_string_opt(context, "do-tcp:", "no");
break;
case GETDNS_TRANSPORT_TCP_ONLY:
/* 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;
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:
/* TODO: Investigate why ssl-upstream in Unbound isn't working (error
* that the SSL lib isn't init'ed but that is done in prep_for_res.
* Note: no fallback or pipelining available directly in unbound.*/
/* 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");
/* set_ub_string_opt(context, "ssl-upstream:", "yes");*/
/* TODO: Specifying a different port to do TLS on in unbound is a bit
* tricky as it involves modifying each fwd upstream defined on the
* unbound ctx... And to support fallback this would have to be reset
* from the stub code while trying to connect...*/
break;
default:
return GETDNS_RETURN_CONTEXT_UPDATE_FAIL;
@ -1695,17 +1731,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, struct 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);
}
@ -1777,7 +1814,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 */
@ -1805,9 +1842,6 @@ getdns_context_prepare_for_resolution(struct getdns_context *context,
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) {
/* Init the SSL library */
SSL_library_init();
/* Create client context, use TLS v1.2 only for now */
SSL_CTX* tls_ctx = SSL_CTX_new(TLSv1_2_client_method());
if(!tls_ctx) {

View File

@ -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;
@ -222,4 +231,6 @@ int filechg_check(struct getdns_context *context, struct filechg *fchg);
void priv_getdns_context_ub_read_cb(void *userarg);
getdns_transport_t priv_get_transport(getdns_transport_t transport, int level);
#endif /* _GETDNS_CONTEXT_H_ */

View File

@ -41,8 +41,6 @@
#include "util-internal.h"
#include "general.h"
#define TLS_PORT 1021
static time_t secret_rollover_time = 0;
static uint32_t secret = 0;
static uint32_t prev_secret = 0;
@ -656,7 +654,7 @@ sock_connected(int 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.*/
SSL*
static SSL*
do_tls_handshake(getdns_dns_req *dnsreq, getdns_upstream *upstream)
{
/*Lets make sure the connection is up before we try a handshake*/
@ -1158,7 +1156,7 @@ get_port(struct sockaddr_storage* addr)
: ((struct sockaddr_in6*)addr)->sin6_port);
}
void
static void
set_port(struct sockaddr_storage* addr, in_port_t port)
{
addr->ss_family == AF_INET
@ -1166,42 +1164,7 @@ set_port(struct sockaddr_storage* addr, in_port_t port)
: (((struct sockaddr_in6*)addr)->sin6_port = htons(port));
}
typedef enum getdns_base_transport {
NONE,
UDP,
TCP_SINGLE,
TCP,
TLS
} getdns_base_transport_t;
getdns_transport_t
get_transport(getdns_transport_t transport, int level) {
if (!(level == 0 || level == 1)) return NONE;
switch (transport) {
case GETDNS_TRANSPORT_UDP_FIRST_AND_FALL_BACK_TO_TCP:
if (level == 0) return UDP;
if (level == 1) return TCP;
case GETDNS_TRANSPORT_UDP_ONLY:
if (level == 0) return UDP;
if (level == 1) return NONE;
case GETDNS_TRANSPORT_TCP_ONLY:
if (level == 0) return TCP_SINGLE;
if (level == 1) return NONE;
case GETDNS_TRANSPORT_TCP_ONLY_KEEP_CONNECTIONS_OPEN:
if (level == 0) return TCP;
if (level == 1) return NONE;
case GETDNS_TRANSPORT_TLS_ONLY_KEEP_CONNECTIONS_OPEN:
if (level == 0) return TLS;
if (level == 1) return NONE;
case GETDNS_TRANSPORT_TLS_FIRST_AND_FALL_BACK_TO_TCP_KEEP_CONNECTIONS_OPEN:
if (level == 0) return TLS;
if (level == 1) return TCP;
default:
return NONE;
}
}
int
static int
tcp_connect (getdns_upstream *upstream, getdns_base_transport_t transport) {
int fd =-1;
@ -1210,10 +1173,11 @@ tcp_connect (getdns_upstream *upstream, getdns_base_transport_t transport) {
socklen_t addr_len = upstream->addr_len;
/* TODO[TLS]: For now, override the port to a hardcoded value*/
if (transport == TLS && (int)get_port(addr) != TLS_PORT) {
if (transport == GETDNS_TRANSPORT_TLS &&
(int)get_port(addr) != GETDNS_TLS_PORT) {
connect_addr = upstream->addr;
addr = &connect_addr;
set_port(addr, TLS_PORT);
set_port(addr, GETDNS_TLS_PORT);
}
if ((fd = socket(addr->ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1)
@ -1222,7 +1186,8 @@ tcp_connect (getdns_upstream *upstream, getdns_base_transport_t transport) {
getdns_sock_nonblock(fd);
#ifdef USE_TCP_FASTOPEN
/* Leave the connect to the later call to sendto() if using TCP*/
if (transport == TCP || transport == TCP_SINGLE)
if (transport == GETDNS_TRANSPORT_TCP ||
transport == GETDNS_TRANSPORT_TCP_SINGLE)
return fd;
#endif
if (connect(fd, (struct sockaddr *)addr,
@ -1245,12 +1210,12 @@ priv_getdns_submit_stub_request(getdns_network_req *netreq)
return GETDNS_RETURN_GENERIC_ERROR;
// Work out the primary and fallback transport options
getdns_base_transport_t transport = get_transport(
getdns_base_transport_t transport = priv_get_transport(
dnsreq->context->dns_transport,0);
getdns_base_transport_t fb_transport = get_transport(
getdns_base_transport_t fb_transport = priv_get_transport(
dnsreq->context->dns_transport,1);
switch(transport) {
case UDP:
case GETDNS_TRANSPORT_UDP:
if ((netreq->fd = socket(
upstream->addr.ss_family, SOCK_DGRAM, IPPROTO_UDP)) == -1)
@ -1266,7 +1231,7 @@ priv_getdns_submit_stub_request(getdns_network_req *netreq)
return GETDNS_RETURN_GOOD;
case TCP_SINGLE:
case GETDNS_TRANSPORT_TCP_SINGLE:
if ((netreq->fd = tcp_connect(upstream, transport)) == -1)
return GETDNS_RETURN_GENERIC_ERROR;
@ -1279,8 +1244,8 @@ priv_getdns_submit_stub_request(getdns_network_req *netreq)
return GETDNS_RETURN_GOOD;
case TCP:
case TLS:
case GETDNS_TRANSPORT_TCP:
case GETDNS_TRANSPORT_TLS:
/* In coming comments, "global" means "context wide" */
@ -1293,7 +1258,7 @@ priv_getdns_submit_stub_request(getdns_network_req *netreq)
/* We are the first. Make global socket and connect. */
if ((upstream->fd = tcp_connect(upstream, transport)) == -1) {
if (fb_transport == NONE)
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;
@ -1302,10 +1267,10 @@ priv_getdns_submit_stub_request(getdns_network_req *netreq)
/* 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 == TLS && (fallback == 0)) {
if (transport == GETDNS_TRANSPORT_TLS && (fallback == 0)) {
upstream->tls_obj = do_tls_handshake(dnsreq, upstream);
if (!upstream->tls_obj) {
if (fb_transport == NONE)
if (fb_transport == GETDNS_TRANSPORT_NONE)
return GETDNS_RETURN_GENERIC_ERROR;
close(upstream->fd);
if ((upstream->fd = tcp_connect(upstream, fb_transport)) == -1)
@ -1319,7 +1284,7 @@ priv_getdns_submit_stub_request(getdns_network_req *netreq)
} else {
/* Cater for the case of the user downgrading and existing TLS
connection to TCP for some reason...*/
if (transport == TCP && upstream->tls_obj) {
if (transport == GETDNS_TRANSPORT_TCP && upstream->tls_obj) {
SSL_shutdown(upstream->tls_obj);
SSL_free(upstream->tls_obj);
upstream->tls_obj = NULL;