Merge pull request #66 from saradickinson/ldns_stub_sync

Thank you Sara.
Travis fails because it uses a version of ldns that doesn't include ldns_resolver_query_status yet (introduced on 19 march 2013, so they have a version before 1.6.17).  I guess we could simply replace with ldns_resolver_query for the time being...
This commit is contained in:
wtoorop 2014-09-17 22:41:21 +02:00
commit d492e388e2
5 changed files with 263 additions and 25 deletions

180
src/context.c Normal file → Executable file
View File

@ -78,6 +78,13 @@ static void set_ub_dnssec_allowed_skew(struct getdns_context*, uint32_t);
static void set_ub_edns_maximum_udp_payload_size(struct getdns_context*,
uint16_t);
/* ldns helpers */
static getdns_return_t set_ldns_dns_transport(struct getdns_context* context,
getdns_transport_t value);
static void set_ldns_edns_maximum_udp_payload_size(struct getdns_context*,
uint16_t);
static getdns_return_t set_ldns_nameservers(struct getdns_context*,
struct getdns_list * upstreams);
/* Stuff to make it compile pedantically */
#define RETURN_IF_NULL(ptr, code) if(ptr == NULL) return code;
@ -487,6 +494,8 @@ getdns_context_create_with_extended_memory_functions(
getdns_context_destroy(result);
return GETDNS_RETURN_GENERIC_ERROR;
}
/* ldns context is initialised to NULL here and rebuilt later if needed */
result->ldns_res = NULL;
*context = result;
@ -570,9 +579,11 @@ getdns_context_destroy(struct getdns_context *context)
getdns_list_destroy(context->dnssec_trust_anchors);
getdns_list_destroy(context->upstream_list);
/* destroy the ub context */
/* destroy the contexts */
if (context->unbound_ctx)
ub_ctx_delete(context->unbound_ctx);
if (context->ldns_res)
ldns_resolver_deep_free(context->ldns_res);
if (context->outbound_requests)
GETDNS_FREE(context->my_mf, context->outbound_requests);
@ -645,6 +656,41 @@ rebuild_ub_ctx(struct getdns_context* context) {
return GETDNS_RETURN_GOOD;
}
static getdns_return_t
rebuild_ldns_res(struct getdns_context* context) {
getdns_return_t result;
if (context->ldns_res != NULL) {
/* cancel all requests and delete */
cancel_outstanding_requests(context, 1);
ldns_resolver_deep_free(context->ldns_res);
context->ldns_res=NULL;
}
/*Create LDNS resolver object. */
context->ldns_res = ldns_resolver_new();
if (context->ldns_res == NULL) {
return GETDNS_RETURN_MEMORY_ERROR;
}
/* TODO: ldns doesn't support this option so this will have to be taken
account expliticly during the ldns validation
* set_ldns_dnssec_allowed_skew();*/
/* This is all the settings required for stub operation in sync mode.
* Will need additional work here when supporting async mode.*/
set_ldns_edns_maximum_udp_payload_size(context,
context->edns_maximum_udp_payload_size);
result = set_ldns_dns_transport(context, context->dns_transport);
if (result != GETDNS_RETURN_GOOD)
return result;
/* We need to set up the upstream recursive servers from the context */
result = set_ldns_nameservers(context, context->upstream_list);
if (result != GETDNS_RETURN_GOOD)
return result;
return GETDNS_RETURN_GOOD;
}
/**
* Helper to dispatch the updated callback
*/
@ -742,6 +788,29 @@ set_ub_dns_transport(struct getdns_context* context,
}
return GETDNS_RETURN_GOOD;
}
static getdns_return_t
set_ldns_dns_transport(struct getdns_context* context,
getdns_transport_t value) {
switch (value) {
case GETDNS_TRANSPORT_UDP_FIRST_AND_FALL_BACK_TO_TCP:
/* ldns has fallback configured by default */
ldns_resolver_set_usevc(context->ldns_res, 0);
break;
case GETDNS_TRANSPORT_UDP_ONLY:
ldns_resolver_set_usevc(context->ldns_res, 0);
ldns_resolver_set_fallback(context->ldns_res, false);
break;
case GETDNS_TRANSPORT_TCP_ONLY:
ldns_resolver_set_usevc(context->ldns_res, 1);
break;
default:
/* TODO GETDNS_CONTEXT_TCP_ONLY_KEEP_CONNECTIONS_OPEN */
return GETDNS_RETURN_CONTEXT_UPDATE_FAIL;
}
return GETDNS_RETURN_GOOD;
}
/*
* getdns_context_set_dns_transport
*
@ -1029,6 +1098,14 @@ set_ub_edns_maximum_udp_payload_size(struct getdns_context* context,
/* max-udp-size */
set_ub_number_opt(context, "max-udp-size:", value);
}
static void
set_ldns_edns_maximum_udp_payload_size(struct getdns_context* context,
uint16_t value) {
/* max-udp-size */
ldns_resolver_set_edns_udp_size(context->ldns_res, value);
}
/*
* getdns_context_set_edns_maximum_udp_payload_size
*
@ -1313,15 +1390,108 @@ ub_setup_stub(struct ub_ctx *ctx, struct getdns_list * upstreams)
return r;
}
static getdns_return_t
set_ldns_nameservers(struct getdns_context *context,
struct getdns_list * upstreams)
{
size_t i;
size_t count;
struct getdns_dict *dict;
struct getdns_bindata *address_string;
char *address_type = NULL;
ldns_rdf* ns_rdf = NULL;
getdns_return_t r;
r = getdns_list_get_length(upstreams, &count);
if (r != GETDNS_RETURN_GOOD)
return r;
if (count == 0 || context->ldns_res == NULL)
return GETDNS_RETURN_BAD_CONTEXT;
/* remove current list of nameservers from resolver */
ldns_rdf *pop;
while((pop = ldns_resolver_pop_nameserver(context->ldns_res))) {
ldns_rdf_deep_free(pop);
}
for (i = 0; i < count; ++i) {
r = getdns_list_get_dict(upstreams, i, &dict);
if (r != GETDNS_RETURN_GOOD)
break;
r = getdns_dict_get_bindata(dict, GETDNS_STR_ADDRESS_STRING,
&address_string);
if (r != GETDNS_RETURN_GOOD)
break;
r = getdns_dict_util_get_string(dict, GETDNS_STR_ADDRESS_TYPE,
&address_type);
if (r != GETDNS_RETURN_GOOD)
break;
/* TODO: PROBLEM! The upstream list is implemented such that there is both
* an IP address and a port in the bindata for each nameserver. Unbound
* can handle this but ldns cannot. ldns has a list of nameservers which
* must be A or AAAA records and it has one port setting on the resolver.
* TEMP SOLUTION: strip off any port and use the port of the last
* nameserver in the list. Wrong, but this will support the test scripts
* in the short term which rely on being able to set a port for a single
* nameserver. */
char *address_char = (char *)address_string->data;
char *port_char = NULL;
char *at_symbol_position = strchr(address_char, '@');
if (at_symbol_position != NULL) {
int ip_length = at_symbol_position - address_char;
int port_length = strlen(address_char) - ip_length;
address_char = (char*) malloc(ip_length + 1);
memcpy(address_char, (char *)address_string->data, ip_length);
address_char[ip_length] = '\0';
port_char = (char*) malloc(port_length);
memcpy(port_char, (char *)(address_string->data + ip_length + 1), port_length);
port_char[port_length] = '\0';
}
if (strncmp(GETDNS_STR_IPV4, address_type,
strlen(GETDNS_STR_IPV4)) == 0) {
ns_rdf = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_A,
address_char);
} else if (strncmp(GETDNS_STR_IPV6, address_type,
strlen(GETDNS_STR_IPV6)) == 0) {
ns_rdf = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_AAAA,
address_char);
}
if (ns_rdf == NULL)
return GETDNS_RETURN_GENERIC_ERROR;
ldns_resolver_push_nameserver(context->ldns_res, ns_rdf);
ldns_rdf_deep_free(ns_rdf);
if (at_symbol_position != NULL) {
ldns_resolver_set_port(context->ldns_res, atoi(port_char));
free(port_char);
free(address_char);
}
}
return GETDNS_RETURN_GOOD;
}
static getdns_return_t
priv_getdns_ns_dns_setup(struct getdns_context *context)
{
assert(context);
getdns_return_t r;
switch (context->resolution_type) {
case GETDNS_RESOLUTION_STUB:
return ub_setup_stub(context->unbound_ctx,
context->upstream_list);
case GETDNS_RESOLUTION_STUB:
/* Since we don't know if the resolution will be sync or async at this
* point and we only support ldns in sync mode then we must set _both_
* contexts up */
/* We get away with just setting up ldns here here because sync mode
* always hits this method because at the moment all sync calls use DNS
* namespace */
r = ub_setup_stub(context->unbound_ctx, context->upstream_list);
if (r != GETDNS_RETURN_GOOD)
return r;
return rebuild_ldns_res(context);
case GETDNS_RESOLUTION_RECURSING:
/* TODO: use the root servers via root hints file */
@ -1365,6 +1535,8 @@ getdns_context_prepare_for_resolution(struct getdns_context *context,
for (i = 0; i < context->namespace_count; i++) {
switch (context->namespaces[i]) {
case GETDNS_NAMESPACE_LOCALNAMES:
/* TODO: Note to self! This must change once we have
* proper namespace hanlding or asynch stub mode using ldns.*/
(void) ub_ctx_hosts(context->unbound_ctx, NULL);
break;

4
src/context.h Normal file → Executable file
View File

@ -96,9 +96,9 @@ struct getdns_context {
struct mem_funcs mf;
struct mem_funcs my_mf;
/* The underlying unbound contexts that do
* the real work */
/* The underlying contexts that do the real work */
struct ub_ctx *unbound_ctx;
ldns_resolver *ldns_res;
int has_ta; /* No DNSSEC without trust anchor */
int return_dnssec_status;

47
src/sync.c Normal file → Executable file
View File

@ -48,7 +48,7 @@
#define UNUSED_PARAM(x) ((void)(x))
#define RETURN_IF_NULL(ptr, code) if(ptr == NULL) return code;
static getdns_return_t submit_request_sync(
static getdns_return_t submit_request_sync_rec(
getdns_dns_req* req, uint64_t *timeout)
{
struct ub_result* ub_res = NULL;
@ -76,6 +76,49 @@ static getdns_return_t submit_request_sync(
return gr;
}
static getdns_return_t submit_request_sync_stub(
getdns_dns_req* req, uint64_t *timeout)
{
ldns_status status;
ldns_rdf *qname;
getdns_network_req *netreq = req->first_req;
uint16_t qflags = 0;
struct timeval tv;
while (netreq) {
qname = ldns_dname_new_frm_str(req->name);
qflags = qflags | LDNS_RD;
/* TODO: Use timeout properly - create a ldns_timed_resolve function */
/* timeout is in miliseconds, so map to seconds and microseconds */
tv.tv_sec = *timeout / 1000;
tv.tv_usec = (*timeout % 1000) * 1000;
ldns_resolver_set_timeout(req->context->ldns_res, tv);
status = ldns_resolver_query_status(
&(netreq->result), req->context->ldns_res, qname,
netreq->request_type, netreq->request_class, qflags);
ldns_rdf_deep_free(qname);
qname = NULL;
if (status != LDNS_STATUS_OK) {
/* TODO: use better errors */
return GETDNS_RETURN_GENERIC_ERROR;
}
netreq = netreq->next;
}
return GETDNS_RETURN_GOOD;
}
static getdns_return_t submit_request_sync(
getdns_dns_req* req, struct getdns_context *context)
{
if (context->resolution_type == GETDNS_RESOLUTION_STUB) {
return submit_request_sync_stub(req, &(context->timeout));
} else {
return submit_request_sync_rec(req, &(context->timeout));
}
}
getdns_return_t
getdns_general_sync(struct getdns_context *context,
const char *name,
@ -111,7 +154,7 @@ getdns_general_sync(struct getdns_context *context,
if (!req)
return GETDNS_RETURN_MEMORY_ERROR;
response_status = submit_request_sync(req, &timeout);
response_status = submit_request_sync(req, context);
if (response_status == GETDNS_RETURN_GOOD) {
if (is_extension_set(req->extensions,
"dnssec_return_validation_chain"))

43
src/test/check_getdns_transport.c Normal file → Executable file
View File

@ -103,26 +103,32 @@ void* run_transport_server(void* data) {
n = read(conn, mesg, 65536);
tcp_count++;
}
ldns_wire2pkt(&query, mesg, n);
ldns_resolver_send_pkt(&pkt, resolver, query);
ldns_str2rdf_a(&answerfrom, "127.0.0.1");
ldns_pkt_set_answerfrom(pkt, answerfrom);
ldns_pkt_free(query);
uint8_t* pkt_data;
size_t pkt_len;
ldns_pkt* answer = pkt;
ldns_pkt2wire(&pkt_data, answer, &pkt_len);
ldns_buffer *send_buf;
send_buf = ldns_buffer_new(LDNS_MIN_BUFLEN);
ldns_pkt2buffer_wire(send_buf, pkt);
if (udp_count > 0) {
sendto(udp, pkt_data, pkt_len, 0, (struct sockaddr *) &client_addr, sizeof (client_addr));
sendto(udp, (void*)ldns_buffer_begin(send_buf), ldns_buffer_position(send_buf),
0, (struct sockaddr *) &client_addr, sizeof (client_addr));
} else if (conn > 0) {
int wcount = write(conn, pkt_data, pkt_len);
if (wcount != pkt_len) {
/* For now ignore this */
continue;
}
uint8_t *send_array;
/* add length of packet */
send_array = LDNS_XMALLOC(uint8_t, ldns_buffer_position(send_buf) + 2);
if(!send_array) return 0;
ldns_write_uint16(send_array, ldns_buffer_position(send_buf));
memcpy(send_array + 2, ldns_buffer_begin(send_buf), ldns_buffer_position(send_buf));
write(conn, (void*)send_array, ldns_buffer_position(send_buf) + 2);
LDNS_FREE(send_array);
}
free(pkt_data);
ldns_pkt_free(answer);
LDNS_FREE(send_buf);
ldns_pkt_free(pkt);
} /* End of if */
} /* end of while loop */
close(udp);
@ -210,7 +216,7 @@ START_TEST(getdns_transport_udp_sync) {
t_data.running = 0;
pthread_join(thread, NULL);
ck_assert_msg(t_data.udp_count == 1, "udp_count != 1");
ck_assert_msg(t_data.udp_count >= 1, "udp_count !>= 1");
ck_assert_msg(t_data.tcp_count == 0, "tcp_count != 0");
}
@ -285,7 +291,7 @@ START_TEST(getdns_transport_tcp_sync) {
t_data.running = 0;
pthread_join(thread, NULL);
ck_assert_msg(t_data.udp_count == 0, "udp_count != 0");
ck_assert_msg(t_data.tcp_count == 1, "tcp_count != 1");
ck_assert_msg(t_data.tcp_count >= 1, "tcp_count !>= 1");
}
@ -362,7 +368,7 @@ START_TEST(getdns_transport_udp_async) {
t_data.running = 0;
pthread_join(thread, NULL);
ck_assert_msg(t_data.udp_count == 1, "udp_count != 1");
ck_assert_msg(t_data.udp_count >= 1, "udp_count !>= 1");
ck_assert_msg(t_data.tcp_count == 0, "tcp_count != 0");
}
@ -441,7 +447,7 @@ START_TEST(getdns_transport_tcp_async) {
t_data.running = 0;
pthread_join(thread, NULL);
ck_assert_msg(t_data.udp_count == 0, "udp_count != 0");
ck_assert_msg(t_data.tcp_count == 1, "tcp_count != 1");
ck_assert_msg(t_data.tcp_count >= 1, "tcp_count !>= 1");
}
@ -454,6 +460,11 @@ Suite *
getdns_transport_suite(void) {
Suite *s = suite_create("getdns_transport()");
/* Note that the exact number of messages received depends on if a trust
* anchor is configured so these tests just check that no messages are
* received on the wrong transport and at least one is recieved on the
* expected transport */
/* Positive test cases */
TCase *tc_pos = tcase_create("Positive");
tcase_set_timeout(tc_pos, 15.0);

14
src/test/tests_stub_sync.c Normal file → Executable file
View File

@ -81,7 +81,19 @@ main()
}
print_response(response);
getdns_dict_destroy(response);
/* Now switch to TCP and make sure everything works */
getdns_context_set_dns_transport(this_context, GETDNS_TRANSPORT_TCP_ONLY);
ret = getdns_general_sync(this_context, "www.google.com", GETDNS_RRTYPE_A,
NULL, &response);
if (ret != GETDNS_RETURN_GOOD || response == NULL) {
fprintf(stderr, "General sync over TCP returned error.\n");
exit(EXIT_FAILURE);
}
print_response(response);
getdns_dict_destroy(response);
/* Clean up */
getdns_context_destroy(this_context);
/* Assuming we get here, leave gracefully */