diff --git a/src/context.c b/src/context.c index 961ea8f9..77fe07df 100644 --- a/src/context.c +++ b/src/context.c @@ -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) { diff --git a/src/context.h b/src/context.h index 816c068b..a9b23317 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; @@ -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_ */ diff --git a/src/stub.c b/src/stub.c index 0d8610c4..72c00faf 100755 --- a/src/stub.c +++ b/src/stub.c @@ -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;