First pass at TLS implementation - needs work!

This commit is contained in:
saradickinson 2014-12-07 19:03:34 +00:00 committed by Sara Dickinson
parent 793423b325
commit 99aa79b48f
11 changed files with 634 additions and 84 deletions

6
spec/index.html Normal file → Executable file
View File

@ -2193,8 +2193,10 @@ getdns_context_set_dns_transport(
The value is <span class=default> The value is <span class=default>
<code>GETDNS_TRANSPORT_UDP_FIRST_AND_FALL_BACK_TO_TCP</code></span>, <code>GETDNS_TRANSPORT_UDP_FIRST_AND_FALL_BACK_TO_TCP</code></span>,
<code>GETDNS_TRANSPORT_UDP_ONLY</code>, <code>GETDNS_TRANSPORT_UDP_ONLY</code>,
<code>GETDNS_TRANSPORT_TCP_ONLY</code>, or <code>GETDNS_TRANSPORT_TCP_ONLY</code>,
<code>GETDNS_TRANSPORT_TCP_ONLY_KEEP_CONNECTIONS_OPEN</code>.</p> <code>GETDNS_TRANSPORT_TCP_ONLY_KEEP_CONNECTIONS_OPEN></code>,
<code>GETDNS_TRANSPORT_TLS_ONLY_KEEP_CONNECTIONS_OPEN></code>, or
<code>GETDNS_TRANSPORT_TLS_FIRST_AND_FALL_BACK_TO_TCP_KEEP_CONNECTIONS_OPEN></code>
<div class=forh> <div class=forh>
getdns_return_t getdns_return_t

2
src/const-info.c Normal file → Executable file
View File

@ -39,6 +39,8 @@ static struct const_info consts_info[] = {
{ 541, "GETDNS_TRANSPORT_UDP_ONLY", GETDNS_TRANSPORT_UDP_ONLY_TEXT }, { 541, "GETDNS_TRANSPORT_UDP_ONLY", GETDNS_TRANSPORT_UDP_ONLY_TEXT },
{ 542, "GETDNS_TRANSPORT_TCP_ONLY", GETDNS_TRANSPORT_TCP_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 }, { 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 }, { 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 }, { 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 }, { 552, "GETDNS_APPEND_NAME_ONLY_TO_MULTIPLE_LABEL_NAME_AFTER_FAILURE", GETDNS_APPEND_NAME_ONLY_TO_MULTIPLE_LABEL_NAME_AFTER_FAILURE_TEXT },

View File

@ -470,6 +470,24 @@ upstreams_resize(getdns_upstreams *upstreams, size_t size)
return r; return r;
} }
static void
upstreams_cleanup(getdns_upstreams *upstreams)
{
if (!upstreams)
return;
for (int i = 0; i < (int)upstreams->count; i++) {
if (upstreams->upstreams[i].tls_obj != NULL) {
SSL_shutdown(upstreams->upstreams[i].tls_obj);
SSL_free(upstreams->upstreams[i].tls_obj);
upstreams->upstreams[i].tls_obj = NULL;
}
if (upstreams->upstreams[i].fd != -1) {
close(upstreams->upstreams[i].fd);
upstreams->upstreams[i].fd = -1;
}
}
}
static void static void
upstreams_dereference(getdns_upstreams *upstreams) upstreams_dereference(getdns_upstreams *upstreams)
{ {
@ -541,6 +559,7 @@ upstream_init(getdns_upstream *upstream,
/* For sharing a socket to this upstream with TCP */ /* For sharing a socket to this upstream with TCP */
upstream->fd = -1; upstream->fd = -1;
upstream->tls_obj = NULL;
upstream->loop = NULL; upstream->loop = NULL;
(void) getdns_eventloop_event_init( (void) getdns_eventloop_event_init(
&upstream->event, upstream, NULL, NULL, NULL); &upstream->event, upstream, NULL, NULL, NULL);
@ -770,6 +789,7 @@ getdns_context_create_with_extended_memory_functions(
result->edns_extended_rcode = 0; result->edns_extended_rcode = 0;
result->edns_version = 0; result->edns_version = 0;
result->edns_do_bit = 0; result->edns_do_bit = 0;
result-> tls_ctx = NULL;
result->extension = &result->mini_event.loop; result->extension = &result->mini_event.loop;
if ((r = getdns_mini_event_init(result, &result->mini_event))) if ((r = getdns_mini_event_init(result, &result->mini_event)))
@ -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->prevstat);
GETDNS_FREE(context->my_mf, context->fchg_hosts); 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->dns_root_servers);
getdns_list_destroy(context->suffix); getdns_list_destroy(context->suffix);
@ -887,6 +910,7 @@ getdns_context_destroy(struct getdns_context *context)
getdns_traverse_postorder(&context->local_hosts, getdns_traverse_postorder(&context->local_hosts,
destroy_local_host, context); destroy_local_host, context);
upstreams_cleanup(context->upstreams);
upstreams_dereference(context->upstreams); upstreams_dereference(context->upstreams);
GETDNS_FREE(context->my_mf, context); GETDNS_FREE(context->my_mf, context);
@ -1118,8 +1142,20 @@ set_ub_dns_transport(struct getdns_context* context,
set_ub_string_opt(context, "do-udp:", "no"); set_ub_string_opt(context, "do-udp:", "no");
set_ub_string_opt(context, "do-tcp:", "yes"); set_ub_string_opt(context, "do-tcp:", "yes");
break; break;
case GETDNS_TRANSPORT_TLS_ONLY_KEEP_CONNECTIONS_OPEN:
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.*/
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: default:
/* TODO GETDNS_CONTEXT_TCP_ONLY_KEEP_CONNECTIONS_OPEN */
return GETDNS_RETURN_CONTEXT_UPDATE_FAIL; return GETDNS_RETURN_CONTEXT_UPDATE_FAIL;
} }
return GETDNS_RETURN_GOOD; return GETDNS_RETURN_GOOD;
@ -1134,6 +1170,11 @@ getdns_context_set_dns_transport(struct getdns_context *context,
getdns_transport_t value) getdns_transport_t value)
{ {
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER); 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) { if (set_ub_dns_transport(context, value) != GETDNS_RETURN_GOOD) {
return GETDNS_RETURN_CONTEXT_UPDATE_FAIL; return GETDNS_RETURN_CONTEXT_UPDATE_FAIL;
} }
@ -1448,6 +1489,7 @@ getdns_context_set_upstream_recursive_servers(struct getdns_context *context,
freeaddrinfo(ai); freeaddrinfo(ai);
} }
upstreams_dereference(context->upstreams); upstreams_dereference(context->upstreams);
/*Don't the existing upstreams need to be handled before overwritting here?*/
context->upstreams = upstreams; context->upstreams = upstreams;
dispatch_updated(context, dispatch_updated(context,
GETDNS_CONTEXT_CODE_UPSTREAM_RECURSIVE_SERVERS); GETDNS_CONTEXT_CODE_UPSTREAM_RECURSIVE_SERVERS);
@ -1756,6 +1798,34 @@ getdns_context_prepare_for_resolution(struct getdns_context *context,
if (context->destroying) { if (context->destroying) {
return GETDNS_RETURN_BAD_CONTEXT; return GETDNS_RETURN_BAD_CONTEXT;
} }
/* Transport can in theory be set per query in stub mode so deal with it
here */
printf("[TLS] preparing for resolution, checking transport type\n");
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) {
/* Init the SSL library */
SSL_library_init();
/* Load error messages */
SSL_load_error_strings();
/* Create client context, use TLS v1.2 only for now */
SSL_CTX* tls_ctx = SSL_CTX_new(TLSv1_2_client_method());
if(!tls_ctx) {
ERR_print_errors_fp(stderr);
return GETDNS_RETURN_BAD_CONTEXT;
}
context->tls_ctx = tls_ctx;
}
break;
default:
break;
}
}
if (context->resolution_type_set == context->resolution_type) if (context->resolution_type_set == context->resolution_type)
/* already set and no config changes /* already set and no config changes
* have caused this to be bad. * have caused this to be bad.

View File

@ -83,6 +83,7 @@ typedef struct getdns_upstream {
/* For sharing a TCP socket to this upstream */ /* For sharing a TCP socket to this upstream */
int fd; int fd;
SSL* tls_obj;
getdns_eventloop_event event; getdns_eventloop_event event;
getdns_eventloop *loop; getdns_eventloop *loop;
getdns_tcp_state tcp; getdns_tcp_state tcp;
@ -133,6 +134,7 @@ struct getdns_context {
uint8_t edns_version; uint8_t edns_version;
uint8_t edns_do_bit; uint8_t edns_do_bit;
int edns_maximum_udp_payload_size; /* -1 is unset */ int edns_maximum_udp_payload_size; /* -1 is unset */
SSL_CTX* tls_ctx;
getdns_update_callback update_callback; getdns_update_callback update_callback;
getdns_update_callback2 update_callback2; getdns_update_callback2 update_callback2;

6
src/getdns/getdns.h.in Normal file → Executable file
View File

@ -163,7 +163,9 @@ typedef enum getdns_transport_t {
GETDNS_TRANSPORT_UDP_FIRST_AND_FALL_BACK_TO_TCP = 540, GETDNS_TRANSPORT_UDP_FIRST_AND_FALL_BACK_TO_TCP = 540,
GETDNS_TRANSPORT_UDP_ONLY = 541, GETDNS_TRANSPORT_UDP_ONLY = 541,
GETDNS_TRANSPORT_TCP_ONLY = 542, 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; } 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_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_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_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()"
/** @} /** @}
*/ */

View File

@ -98,6 +98,7 @@ network_req_init(getdns_network_req *net_req, getdns_dns_req *owner,
net_req->write_queue_tail = NULL; net_req->write_queue_tail = NULL;
net_req->query_len = 0; net_req->query_len = 0;
net_req->response_len = 0; net_req->response_len = 0;
net_req->tls_obj = NULL;
net_req->wire_data_sz = wire_data_sz; net_req->wire_data_sz = wire_data_sz;
if (max_query_sz) { if (max_query_sz) {

457
src/stub.c Normal file → Executable file
View File

@ -41,6 +41,8 @@
#include "util-internal.h" #include "util-internal.h"
#include "general.h" #include "general.h"
#define TLS_PORT 1021
static time_t secret_rollover_time = 0; static time_t secret_rollover_time = 0;
static uint32_t secret = 0; static uint32_t secret = 0;
static uint32_t prev_secret = 0; static uint32_t prev_secret = 0;
@ -318,6 +320,13 @@ upstream_erred(getdns_upstream *upstream)
netreq->state = NET_REQ_FINISHED; netreq->state = NET_REQ_FINISHED;
priv_getdns_check_dns_req_complete(netreq->owner); 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); close(upstream->fd);
upstream->fd = -1; upstream->fd = -1;
} }
@ -498,6 +507,8 @@ stub_tcp_read(int fd, getdns_tcp_state *tcp, struct mem_funcs *mf)
uint8_t *buf; uint8_t *buf;
size_t buf_size; size_t buf_size;
fprintf(stderr, "[TLS] method: stub_tcp_read\n");
if (!tcp->read_buf) { if (!tcp->read_buf) {
/* First time tcp read, create a buffer for reading */ /* First time tcp read, create a buffer for reading */
if (!(tcp->read_buf = GETDNS_XMALLOC(*mf, uint8_t, 4096))) if (!(tcp->read_buf = GETDNS_XMALLOC(*mf, uint8_t, 4096)))
@ -518,10 +529,11 @@ stub_tcp_read(int fd, getdns_tcp_state *tcp, struct mem_funcs *mf)
/* TODO: Try to reconnect */ /* TODO: Try to reconnect */
return STUB_TCP_ERROR; return STUB_TCP_ERROR;
} }
fprintf(stderr, "[TLS] method: read %d TCP bytes \n", (int)read);
tcp->to_read -= read; tcp->to_read -= read;
tcp->read_pos += read; tcp->read_pos += read;
if (tcp->to_read > 0) if ((int)tcp->to_read > 0)
return STUB_TCP_AGAIN; return STUB_TCP_AGAIN;
read = tcp->read_pos - tcp->read_buf; read = tcp->read_pos - tcp->read_buf;
@ -563,13 +575,16 @@ stub_tcp_read_cb(void *userarg)
&dnsreq->context->mf))) { &dnsreq->context->mf))) {
case STUB_TCP_AGAIN: case STUB_TCP_AGAIN:
fprintf(stderr, "[TLS] method: stub_tcp_read_cb -> tcp again\n");
return; return;
case STUB_TCP_ERROR: case STUB_TCP_ERROR:
fprintf(stderr, "[TLS] method: stub_tcp_read_cb -> tcp error\n");
stub_erred(netreq); stub_erred(netreq);
return; return;
default: default:
fprintf(stderr, "[TLS] method: stub_tcp_read_cb -> All done. close fd %d\n", netreq->fd);
GETDNS_CLEAR_EVENT(dnsreq->loop, &netreq->event); GETDNS_CLEAR_EVENT(dnsreq->loop, &netreq->event);
if (q != netreq->query_id) if (q != netreq->query_id)
return; return;
@ -595,8 +610,200 @@ 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);
struct timeval timeout = {2, 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)
{
fprintf(stderr, "[TLS] connect in progress \n");
/* 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.*/
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) {
fprintf(stderr, "[TLS] connect failed \n");
return NULL;
}
fprintf(stderr, "[TLS] connect done \n");
/* Create SSL instance */
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);
fprintf(stderr, "[TLS] in handshake loop %d, want is %d \n", r, want);
switch (want) {
case SSL_ERROR_WANT_READ:
if (select(upstream->fd + 1, &fds, NULL, NULL, &timeout) == 0) {
fprintf(stderr, "[TLS] ssl handshake timeout %d\n", want);
SSL_free(ssl);
return NULL;
}
break;
case SSL_ERROR_WANT_WRITE:
if (select(upstream->fd + 1, NULL, &fds, NULL, &timeout) == 0) {
fprintf(stderr, "[TLS] ssl handshake timeout %d\n", want);
SSL_free(ssl);
return NULL;
}
break;
default:
fprintf(stderr, "[TLS] got ssl error code %d\n", want);
SSL_free(ssl);
return NULL;
}
}
fprintf(stderr, "[TLS] got TLS connection\n");
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;
fprintf(stderr, "[TLS] method: stub_tls_read\n");
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;
}
fprintf(stderr, "[TLS] method: read %d TLS bytes \n", (int)read);
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);
fprintf(stderr, "[TLS] method: %d TLS bytes to read \n", (int)tcp->to_read);
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 */
fprintf(stderr, "[TLS] method: resetting read_pos \n");
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_read_cb(void *userarg);
static void netreq_upstream_write_cb(void *userarg); static void netreq_upstream_write_cb(void *userarg);
static void upstream_write_cb(void *userarg);
static void static void
upstream_read_cb(void *userarg) upstream_read_cb(void *userarg)
{ {
@ -607,9 +814,18 @@ upstream_read_cb(void *userarg)
uint16_t query_id; uint16_t query_id;
intptr_t query_id_intptr; intptr_t query_id_intptr;
switch ((q = stub_tcp_read(upstream->fd, &upstream->tcp, fprintf(stderr, "[TLS] method: upstream_read_cb\n");
&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: case STUB_TCP_AGAIN:
fprintf(stderr, "[TLS] method: upstream_read_cb -> STUB_TCP_AGAIN\n");
return; return;
case STUB_TCP_ERROR: case STUB_TCP_ERROR:
@ -617,6 +833,8 @@ upstream_read_cb(void *userarg)
return; return;
default: default:
fprintf(stderr, "[TLS] method: upstream_read_cb -> processing reponse\n");
/* Lookup netreq */ /* Lookup netreq */
query_id = (uint16_t) q; query_id = (uint16_t) q;
query_id_intptr = (intptr_t) query_id; query_id_intptr = (intptr_t) query_id;
@ -633,6 +851,7 @@ upstream_read_cb(void *userarg)
netreq->response = upstream->tcp.read_buf; netreq->response = upstream->tcp.read_buf;
netreq->response_len = netreq->response_len =
upstream->tcp.read_pos - upstream->tcp.read_buf; upstream->tcp.read_pos - upstream->tcp.read_buf;
netreq->tls_obj = upstream->tls_obj;
upstream->tcp.read_buf = NULL; upstream->tcp.read_buf = NULL;
upstream->upstreams->current = 0; upstream->upstreams->current = 0;
@ -687,6 +906,7 @@ static int
stub_tcp_write(int fd, getdns_tcp_state *tcp, getdns_network_req *netreq) stub_tcp_write(int fd, getdns_tcp_state *tcp, getdns_network_req *netreq)
{ {
getdns_dns_req *dnsreq = netreq->owner; getdns_dns_req *dnsreq = netreq->owner;
fprintf(stderr, "[TLS] method: stub_tcp_write\n");
size_t pkt_len = netreq->response - netreq->query; size_t pkt_len = netreq->response - netreq->query;
ssize_t written; ssize_t written;
@ -705,9 +925,9 @@ stub_tcp_write(int fd, getdns_tcp_state *tcp, getdns_network_req *netreq)
* the write_queue) for that upstream. Register this netreq * the write_queue) for that upstream. Register this netreq
* by query_id in the process. * by query_id in the process.
*/ */
if (dnsreq->context->dns_transport != if ((dnsreq->context->dns_transport == GETDNS_TRANSPORT_TCP_ONLY) ||
GETDNS_TRANSPORT_TCP_ONLY_KEEP_CONNECTIONS_OPEN) (dnsreq->context->dns_transport == GETDNS_TRANSPORT_UDP_ONLY) ||
(dnsreq->context->dns_transport == GETDNS_TRANSPORT_UDP_FIRST_AND_FALL_BACK_TO_TCP))
query_id = arc4random(); query_id = arc4random();
else do { else do {
query_id = arc4random(); query_id = arc4random();
@ -822,6 +1042,56 @@ stub_tcp_write_cb(void *userarg)
} }
} }
static int
stub_tls_write(SSL* tls_obj, getdns_tcp_state *tcp, getdns_network_req *netreq)
{
fprintf(stderr, "[TLS] method: stub_tls_write\n");
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 static void
upstream_write_cb(void *userarg) upstream_write_cb(void *userarg)
{ {
@ -830,7 +1100,14 @@ upstream_write_cb(void *userarg)
getdns_dns_req *dnsreq = netreq->owner; getdns_dns_req *dnsreq = netreq->owner;
int q; int q;
switch ((q = stub_tcp_write(upstream->fd, &upstream->tcp, netreq))) { fprintf(stderr, "[TLS] method: upstream_write_cb for %s with class %d\n", dnsreq->name, (int)netreq->request_class);
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: case STUB_TCP_AGAIN:
return; return;
@ -902,18 +1179,110 @@ 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);
}
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));
}
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
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 == TLS && (int)get_port(addr) != TLS_PORT) {
connect_addr = upstream->addr;
addr = &connect_addr;
set_port(addr, TLS_PORT);
fprintf(stderr, "[TLS] Forcing switch to port %d for TLS\n", 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 == TCP || 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 getdns_return_t
priv_getdns_submit_stub_request(getdns_network_req *netreq) priv_getdns_submit_stub_request(getdns_network_req *netreq)
{ {
getdns_dns_req *dnsreq = netreq->owner; getdns_dns_req *dnsreq = netreq->owner;
getdns_upstream *upstream = pick_upstream(dnsreq); getdns_upstream *upstream = pick_upstream(dnsreq);
fprintf(stderr, "[TLS] method: priv_getdns_submit_stub_request\n");
if (!upstream) if (!upstream)
return GETDNS_RETURN_GENERIC_ERROR; return GETDNS_RETURN_GENERIC_ERROR;
switch(dnsreq->context->dns_transport) { // Work out the primary and fallback transport options
case GETDNS_TRANSPORT_UDP_ONLY: getdns_base_transport_t transport = get_transport(
case GETDNS_TRANSPORT_UDP_FIRST_AND_FALL_BACK_TO_TCP: dnsreq->context->dns_transport,0);
getdns_base_transport_t fb_transport = get_transport(
dnsreq->context->dns_transport,1);
switch(transport) {
case UDP:
if ((netreq->fd = socket( if ((netreq->fd = socket(
upstream->addr.ss_family, SOCK_DGRAM, IPPROTO_UDP)) == -1) upstream->addr.ss_family, SOCK_DGRAM, IPPROTO_UDP)) == -1)
@ -929,23 +1298,10 @@ priv_getdns_submit_stub_request(getdns_network_req *netreq)
return GETDNS_RETURN_GOOD; return GETDNS_RETURN_GOOD;
case GETDNS_TRANSPORT_TCP_ONLY: case TCP_SINGLE:
if ((netreq->fd = socket( if ((netreq->fd = tcp_connect(upstream, transport)) == -1)
upstream->addr.ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1)
return GETDNS_RETURN_GENERIC_ERROR; 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; netreq->upstream = upstream;
GETDNS_SCHEDULE_EVENT( GETDNS_SCHEDULE_EVENT(
@ -955,36 +1311,57 @@ priv_getdns_submit_stub_request(getdns_network_req *netreq)
return GETDNS_RETURN_GOOD; return GETDNS_RETURN_GOOD;
case GETDNS_TRANSPORT_TCP_ONLY_KEEP_CONNECTIONS_OPEN: case TCP:
case TLS:
/* In coming comments, "global" means "context wide" */ /* In coming comments, "global" means "context wide" */
/* Are we the first? (Is global socket initialized?) */ /* Are we the first? (Is global socket initialized?) */
if (upstream->fd == -1) { if (upstream->fd == -1) {
/* 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;
/* We are the first. Make global socket and connect. */ /* We are the first. Make global socket and connect. */
if ((upstream->fd = socket(upstream->addr.ss_family, if ((upstream->fd = tcp_connect(upstream, transport)) == -1) {
SOCK_STREAM, IPPROTO_TCP)) == -1) //TODO: Hum, a reset doesn't make the connect fail...
if (fb_transport == NONE)
return GETDNS_RETURN_GENERIC_ERROR; return GETDNS_RETURN_GENERIC_ERROR;
fprintf(stderr, "[TLS] Connect failed on fd... %d\n", upstream->fd);
if ((upstream->fd = tcp_connect(upstream, fb_transport)) == -1)
return GETDNS_RETURN_GENERIC_ERROR;
fallback = 1;
}
getdns_sock_nonblock(upstream->fd); /* Now do a handshake for TLS. Note waiting for this to succeed or
#ifdef USE_TCP_FASTOPEN timeout blocks the scheduling of any messages for this upstream*/
/* Leave the connect to the later call to sendto() */ if (transport == TLS && (fallback == 0)) {
#else fprintf(stderr, "[TLS] Doing SSL handshake... %d\n", upstream->fd);
if (connect(upstream->fd, upstream->tls_obj = do_tls_handshake(dnsreq, upstream);
(struct sockaddr *)&upstream->addr, if (!upstream->tls_obj) {
upstream->addr_len) == -1 && errno != EINPROGRESS){ if (fb_transport == NONE)
return GETDNS_RETURN_GENERIC_ERROR;
close(upstream->fd); close(upstream->fd);
upstream->fd = -1; if ((upstream->fd = tcp_connect(upstream, fb_transport)) == -1)
return GETDNS_RETURN_GENERIC_ERROR; return GETDNS_RETURN_GENERIC_ERROR;
} }
#endif }
} else {
/* Cater for the case of the user downgrading and existing TLS
connection to TCP for some reason...*/
if (transport == TCP && upstream->tls_obj) {
SSL_shutdown(upstream->tls_obj);
SSL_free(upstream->tls_obj);
upstream->tls_obj = NULL;
}
}
netreq->upstream = upstream;
/* Attach to the global event loop /* Attach to the global event loop
* so it can do it's own scheduling * so it can do it's own scheduling
*/ */
upstream->loop = dnsreq->context->extension; upstream->loop = dnsreq->context->extension;
}
netreq->upstream = upstream;
/* We have a context wide socket. /* We have a context wide socket.
* Now schedule the write request. * Now schedule the write request.

View File

@ -29,9 +29,23 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <dirent.h>
#include <getdns/getdns.h> #include <getdns/getdns.h>
#include <getdns/getdns_extra.h> #include <getdns/getdns_extra.h>
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); int get_rrtype(const char *t);
getdns_dict * getdns_dict *
@ -90,6 +104,7 @@ print_usage(FILE *out, const char *progname)
fprintf(out, "\t-b <bufsize>\tSet edns0 max_udp_payload size\n"); fprintf(out, "\t-b <bufsize>\tSet edns0 max_udp_payload size\n");
fprintf(out, "\t-D\tSet edns0 do bit\n"); fprintf(out, "\t-D\tSet edns0 do bit\n");
fprintf(out, "\t-d\tclear edns0 do bit\n"); fprintf(out, "\t-d\tclear edns0 do bit\n");
fprintf(out, "\t-F <filename>\tread the queries from the specified file\n");
fprintf(out, "\t-G\tgeneral lookup\n"); fprintf(out, "\t-G\tgeneral lookup\n");
fprintf(out, "\t-H\thostname lookup. (<name> must be an IP address; <type> is ignored)\n"); fprintf(out, "\t-H\thostname lookup. (<name> must be an IP address; <type> is ignored)\n");
fprintf(out, "\t-h\tPrint this help\n"); fprintf(out, "\t-h\tPrint this help\n");
@ -104,28 +119,46 @@ print_usage(FILE *out, const char *progname)
fprintf(out, "\t-t <timeout>\tSet timeout in miliseconds\n"); fprintf(out, "\t-t <timeout>\tSet timeout in miliseconds\n");
fprintf(out, "\t-T\tSet transport to TCP only\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-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 with TCP fallback\n");
fprintf(out, "\t-U\tSet transport to UDP only\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");
fprintf(out, "\t+sit[=cookie]\tSet edns cookie\n");
} }
void callback(getdns_context *context, getdns_callback_type_t callback_type, void callback(getdns_context *context, getdns_callback_type_t callback_type,
getdns_dict *response, void *userarg, getdns_transaction_t trans_id) getdns_dict *response, void *userarg, getdns_transaction_t trans_id)
{ {
getdns_dict **response_ptr = (getdns_dict **)userarg; char *response_str;
if (response) if (callback_type == GETDNS_CALLBACK_COMPLETE) {
*response_ptr = response; /* This is a callback with data */;
if (!quiet) {
if (json)
response_str = getdns_print_json_dict(
response, json == 1);
else if ((response_str = 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);
static char *the_root = "."; } else if (callback_type == GETDNS_CALLBACK_CANCEL)
static char *name; fprintf(stderr,
static getdns_context *context; "The callback with ID %llu was cancelled. Exiting.\n",
static getdns_dict *extensions; (unsigned long long)trans_id);
static uint16_t request_type = GETDNS_RRTYPE_NS; else
static int timeout, edns0_size; fprintf(stderr,
static int async = 0, interactive = 0; "The callback got a callback_type of %d. Exiting.\n",
static enum { GENERAL, ADDRESS, HOSTNAME, SERVICE } calltype = GENERAL; callback_type);
static int json = 0; getdns_dict_destroy(response);
response = NULL;
}
#define CONTINUE ((getdns_return_t)-2) #define CONTINUE ((getdns_return_t)-2)
@ -259,6 +292,15 @@ getdns_return_t parse_args(int argc, char **argv)
case 'd': case 'd':
(void) getdns_context_set_edns_do_bit(context, 0); (void) getdns_context_set_edns_do_bit(context, 0);
break; 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': case 'G':
calltype = GENERAL; calltype = GENERAL;
break; break;
@ -282,6 +324,8 @@ getdns_return_t parse_args(int argc, char **argv)
break; break;
case 'p': case 'p':
json = 0; json = 0;
case 'q':
quiet = 1;
break; break;
case 'r': case 'r':
getdns_context_set_resolution_type( getdns_context_set_resolution_type(
@ -319,6 +363,14 @@ getdns_return_t parse_args(int argc, char **argv)
getdns_context_set_dns_transport(context, getdns_context_set_dns_transport(context,
GETDNS_TRANSPORT_TCP_ONLY_KEEP_CONNECTIONS_OPEN); GETDNS_TRANSPORT_TCP_ONLY_KEEP_CONNECTIONS_OPEN);
break; 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': case 'u':
getdns_context_set_dns_transport(context, getdns_context_set_dns_transport(context,
GETDNS_TRANSPORT_UDP_FIRST_AND_FALL_BACK_TO_TCP); GETDNS_TRANSPORT_UDP_FIRST_AND_FALL_BACK_TO_TCP);
@ -327,6 +379,9 @@ getdns_return_t parse_args(int argc, char **argv)
getdns_context_set_dns_transport(context, getdns_context_set_dns_transport(context,
GETDNS_TRANSPORT_UDP_ONLY); GETDNS_TRANSPORT_UDP_ONLY);
break; break;
case 'B':
batch_mode = 1;
break;
default: default:
@ -361,6 +416,7 @@ main(int argc, char **argv)
char *response_str; char *response_str;
getdns_return_t r; getdns_return_t r;
getdns_dict *address = NULL; getdns_dict *address = NULL;
FILE *fp = NULL;
name = the_root; name = the_root;
if ((r = getdns_context_create(&context, 1))) { if ((r = getdns_context_create(&context, 1))) {
@ -376,14 +432,28 @@ main(int argc, char **argv)
if ((r = parse_args(argc, argv))) if ((r = parse_args(argc, argv)))
goto done_destroy_context; 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 */ /* Make the call */
do { do {
char line[1024], *token, *linev[256]; char line[1024], *token, *linev[256];
int linec; int linec;
if (interactive) { if (interactive) {
if (!query_file) {
fprintf(stdout, "> "); fprintf(stdout, "> ");
if (!fgets(line, 1024, stdin) || !*line) if (!fgets(line, 1024, stdin) || !*line)
break; break;
} else {
if (!fgets(line, 1024, fp) || !*line)
break;
fprintf(stdout,"Found query: %s", line);
}
linev[0] = argv[0]; linev[0] = argv[0];
linec = 1; linec = 1;
@ -430,7 +500,7 @@ main(int argc, char **argv)
} }
if (r) if (r)
goto done_destroy_extensions; goto done_destroy_extensions;
if (!batch_mode)
getdns_context_run(context); getdns_context_run(context);
} else { } else {
switch (calltype) { switch (calltype) {
@ -456,22 +526,27 @@ main(int argc, char **argv)
} }
if (r) if (r)
goto done_destroy_extensions; goto done_destroy_extensions;
} if (!quiet) {
if (json) if (json)
response_str = getdns_print_json_dict( response_str = getdns_print_json_dict(
response, json == 1); response, json == 1);
else else if ((response_str = getdns_pretty_print_dict(response))) {
response_str = getdns_pretty_print_dict(response); fprintf(stdout, "SYNC response:\n%s\n", response_str);
if (response_str) {
fprintf(stdout, "%s\n", response_str);
free(response_str); free(response_str);
} else { } else {
r = GETDNS_RETURN_MEMORY_ERROR; r = GETDNS_RETURN_MEMORY_ERROR;
fprintf(stderr, "Could not print response\n"); 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); } while (interactive);
if (batch_mode)
getdns_context_run(context);
/* Clean up */ /* Clean up */
done_destroy_extensions: done_destroy_extensions:
getdns_dict_destroy(extensions); getdns_dict_destroy(extensions);
@ -479,6 +554,9 @@ done_destroy_context:
if (response) getdns_dict_destroy(response); if (response) getdns_dict_destroy(response);
getdns_context_destroy(context); getdns_context_destroy(context);
if (fp)
fclose(fp);
if (r == CONTINUE) if (r == CONTINUE)
return 0; return 0;
if (r) if (r)

View File

@ -44,6 +44,8 @@
#define TRANSPORT_UDP "udp" #define TRANSPORT_UDP "udp"
#define TRANSPORT_TCP "tcp" #define TRANSPORT_TCP "tcp"
#define TRANSPORT_PIPELINE "pipeline" #define TRANSPORT_PIPELINE "pipeline"
#define TRANSPORT_TLS_KEEPOPEN "tls"
#define TRANSPORT_TLS_TCP_KEEPOPEN "dns-over-tls"
#define RESOLUTION_STUB "stub" #define RESOLUTION_STUB "stub"
#define RESOLUTION_REC "rec" #define RESOLUTION_REC "rec"
@ -98,6 +100,10 @@ main(int argc, char** argv)
getdns_context_set_dns_transport(this_context, GETDNS_TRANSPORT_TCP_ONLY); getdns_context_set_dns_transport(this_context, GETDNS_TRANSPORT_TCP_ONLY);
else if (strncmp(transport, TRANSPORT_PIPELINE, 8) == 0) else if (strncmp(transport, TRANSPORT_PIPELINE, 8) == 0)
getdns_context_set_dns_transport(this_context, GETDNS_TRANSPORT_TCP_ONLY_KEEP_CONNECTIONS_OPEN); 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) { else if (strncmp(transport, TRANSPORT_UDP, 3) != 0) {
fprintf(stderr, "Invalid transport %s, must be one of udp, tcp or pipeline\n", transport); fprintf(stderr, "Invalid transport %s, must be one of udp, tcp or pipeline\n", transport);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);

View File

@ -41,6 +41,8 @@
#define TRANSPORT_UDP "udp" #define TRANSPORT_UDP "udp"
#define TRANSPORT_TCP "tcp" #define TRANSPORT_TCP "tcp"
#define TRANSPORT_PIPELINE "pipeline" #define TRANSPORT_PIPELINE "pipeline"
#define TRANSPORT_TLS_KEEPOPEN "tls"
#define TRANSPORT_TLS_TCP_KEEPOPEN "dns-over-tls"
#define RESOLUTION_STUB "stub" #define RESOLUTION_STUB "stub"
#define RESOLUTION_REC "rec" #define RESOLUTION_REC "rec"
@ -82,6 +84,10 @@ main(int argc, char** argv)
getdns_context_set_dns_transport(this_context, GETDNS_TRANSPORT_TCP_ONLY); getdns_context_set_dns_transport(this_context, GETDNS_TRANSPORT_TCP_ONLY);
else if (strncmp(transport, TRANSPORT_PIPELINE, 8) == 0) else if (strncmp(transport, TRANSPORT_PIPELINE, 8) == 0)
getdns_context_set_dns_transport(this_context, GETDNS_TRANSPORT_TCP_ONLY_KEEP_CONNECTIONS_OPEN); 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) { else if (strncmp(transport, TRANSPORT_UDP, 3) != 0) {
fprintf(stderr, "Invalid transport %s, must be one of udp, tcp or pipeline\n", transport); fprintf(stderr, "Invalid transport %s, must be one of udp, tcp or pipeline\n", transport);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);

View File

@ -37,6 +37,7 @@
#define TYPES_INTERNAL_H_ #define TYPES_INTERNAL_H_
#include <netinet/in.h> #include <netinet/in.h>
#include <openssl/ssl.h>
#include "getdns/getdns.h" #include "getdns/getdns.h"
#include "getdns/getdns_extra.h" #include "getdns/getdns_extra.h"
#include "util/rbtree.h" #include "util/rbtree.h"
@ -211,6 +212,7 @@ typedef struct getdns_network_req
uint8_t *opt; /* offset of OPT RR in query */ uint8_t *opt; /* offset of OPT RR in query */
size_t response_len; size_t response_len;
uint8_t *response; uint8_t *response;
SSL* tls_obj;
size_t wire_data_sz; size_t wire_data_sz;
uint8_t wire_data[]; uint8_t wire_data[];