mirror of https://github.com/getdnsapi/getdns.git
Merge branch 'feature/unbound' into develop
This commit is contained in:
commit
dfa5fc82c8
|
@ -40,6 +40,8 @@ External dependencies are linked outside the getdns API build tree (we rely on c
|
|||
|
||||
The project relies on [libldns from NL](https://www.nlnetlabs.nl/projects/ldns/) for parsing and constructing DNS packets. Version 1.6.16 (note that building ldns may require openssl headers and libraries)
|
||||
|
||||
The project also relies on [libunbound from NL](http://www.nlnetlabs.nl/projects/unbound/). Currently it relies on svn revision 2985. The unbound.2985.patch must be applied to the source tree as well. The ./configure must be run with the --with-libevent option (recommended to also use --with-libunbound-only)
|
||||
|
||||
Although [libevent](http://libevent.org) is used initially to implement the asynchronous model, future work may include a move to other mechanisms (epoll based etc.). Version 2.0.21 stable
|
||||
|
||||
Doxygen is used to generate documentation, while this is not technically necessary for the build it makes things a lot more pleasant.
|
||||
|
|
|
@ -26,10 +26,11 @@ AC_PROG_CPP
|
|||
|
||||
# Checks for libraries.
|
||||
found_all_libs=1
|
||||
AC_MSG_NOTICE([Checking for dependencies ldns and libevent])
|
||||
AC_MSG_NOTICE([Checking for dependencies libevent, ldns, pthread, and unbound])
|
||||
AC_CHECK_LIB([event_core], [event_base_new], [], [found_all_libs=0])
|
||||
AC_CHECK_LIB([ldns], [ldns_dname_new_frm_str], [], [found_all_libs=0])
|
||||
AC_CHECK_LIB([pthread], [pthread_create], [], [found_all_libs=0])
|
||||
AC_CHECK_LIB([unbound], [ub_ctx_set_event], [], [found_all_libs=0])
|
||||
if test $found_all_libs == 0
|
||||
then
|
||||
AC_MSG_ERROR([One more dependencies is missing])
|
||||
|
|
|
@ -17,8 +17,7 @@ VPATH = @srcdir@
|
|||
|
||||
CC=gcc
|
||||
CFLAGS=-Wall -g -fPIC -I$(srcdir)/ -I /usr/local/include -std=c99
|
||||
# LDFLAGS=-L. -L/usr/local/lib
|
||||
# LDLIBS=-levent_core -lldns -lgetdns
|
||||
LDFLAGS=-levent_core -lldns -lunbound
|
||||
# PROGRAMS=example-simple-answers example-tree example-all-functions example-synchronous
|
||||
|
||||
.SUFFIXES: .c .o .a .lo .h
|
||||
|
@ -32,9 +31,9 @@ all: libgetdns
|
|||
cd test && $(MAKE) $@
|
||||
cd example && $(MAKE) $@
|
||||
|
||||
libgetdns: sync.o context.o list.o dict.o convert.o general.o hostname.o service.o \
|
||||
validate_dnssec.o nameserver-internal.o request-internal.o util-internal.o getdns_error.o
|
||||
$(CC) $(CFLAGS) -shared -o libgetdns.so $?
|
||||
libgetdns: sync.o context.o list.o dict.o convert.o general.o hostname.o service.o request-internal.o \
|
||||
validate_dnssec.o util-internal.o getdns_error.o
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -shared -o libgetdns.so $?
|
||||
|
||||
clean:
|
||||
rm -f *.o $(PROGRAMS) libgetdns.so
|
||||
|
|
349
src/context.c
349
src/context.c
|
@ -29,9 +29,16 @@
|
|||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "context.h"
|
||||
#include "util-internal.h"
|
||||
#include "types-internal.h"
|
||||
#include <ldns/ldns.h>
|
||||
#include <event2/event.h>
|
||||
#include <unbound.h>
|
||||
#include <unbound-event.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
/* stuff to make it compile pedantically */
|
||||
#define UNUSED_PARAM(x) ((void)(x))
|
||||
|
@ -55,6 +62,34 @@ static struct getdns_list* create_default_root_servers() {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static getdns_return_t add_ip_str(getdns_dict* ip) {
|
||||
struct sockaddr_storage storage;
|
||||
char buff[256];
|
||||
getdns_return_t r = dict_to_sockaddr(ip, &storage);
|
||||
if (r != GETDNS_RETURN_GOOD) {
|
||||
return r;
|
||||
}
|
||||
if (storage.ss_family == AF_INET) {
|
||||
struct sockaddr_in* addr = (struct sockaddr_in*) &storage;
|
||||
const char* ipStr = inet_ntop(AF_INET, &(addr->sin_addr), buff, 256);
|
||||
if (!ipStr) {
|
||||
return GETDNS_RETURN_GENERIC_ERROR;
|
||||
}
|
||||
getdns_dict_util_set_string(ip, GETDNS_STR_ADDRESS_STRING, ipStr);
|
||||
} else if (storage.ss_family == AF_INET6) {
|
||||
struct sockaddr_in6* addr = (struct sockaddr_in6*) &storage;
|
||||
const char* ipStr = inet_ntop(AF_INET6, &(addr->sin6_addr), buff, 256);
|
||||
if (!ipStr) {
|
||||
return GETDNS_RETURN_GENERIC_ERROR;
|
||||
}
|
||||
getdns_dict_util_set_string(ip, GETDNS_STR_ADDRESS_STRING, ipStr);
|
||||
} else {
|
||||
/* unknown */
|
||||
return GETDNS_RETURN_GENERIC_ERROR;
|
||||
}
|
||||
return GETDNS_RETURN_GOOD;
|
||||
}
|
||||
|
||||
static struct getdns_dict* create_ipaddr_dict_from_rdf(ldns_rdf* rdf) {
|
||||
ldns_rdf_type rt = ldns_rdf_get_type(rdf);
|
||||
size_t sz = ldns_rdf_size(rdf);
|
||||
|
@ -68,6 +103,7 @@ static struct getdns_dict* create_ipaddr_dict_from_rdf(ldns_rdf* rdf) {
|
|||
/* set data */
|
||||
getdns_bindata data_bin = { sz, ldns_rdf_data(rdf) };
|
||||
getdns_dict_set_bindata(result, GETDNS_STR_ADDRESS_DATA, &data_bin);
|
||||
add_ip_str(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -127,6 +163,26 @@ static getdns_return_t set_os_defaults(getdns_context_t context) {
|
|||
return GETDNS_RETURN_GOOD;
|
||||
}
|
||||
|
||||
static int transaction_id_cmp(const void* id1, const void* id2) {
|
||||
if (id1 == NULL && id2 == NULL) {
|
||||
return 0;
|
||||
} else if (id1 == NULL && id2 != NULL) {
|
||||
return 1;
|
||||
} else if (id1 != NULL && id2 == NULL) {
|
||||
return -1;
|
||||
} else {
|
||||
getdns_transaction_t t1 = *((const getdns_transaction_t*) id1);
|
||||
getdns_transaction_t t2 = *((const getdns_transaction_t*) id2);
|
||||
if (t1 == t2) {
|
||||
return 0;
|
||||
} else if (t1 < t2) {
|
||||
return -1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* getdns_context_create
|
||||
*
|
||||
|
@ -143,17 +199,30 @@ getdns_return_t getdns_context_create(
|
|||
return GETDNS_RETURN_GENERIC_ERROR;
|
||||
}
|
||||
|
||||
getdns_dict *outbound_reqs = getdns_dict_create();
|
||||
if (outbound_reqs == NULL) {
|
||||
/** default init **/
|
||||
result = malloc(sizeof(struct getdns_context_t));
|
||||
if (!result) {
|
||||
return GETDNS_RETURN_GENERIC_ERROR;
|
||||
}
|
||||
|
||||
/** default init **/
|
||||
result = malloc(sizeof(struct getdns_context_t));
|
||||
result->update_callback = NULL;
|
||||
result->memory_allocator = malloc;
|
||||
result->memory_deallocator = free;
|
||||
result->memory_reallocator = realloc;
|
||||
|
||||
result->event_base_sync = event_base_new();
|
||||
result->unbound_sync = ub_ctx_create_event(result->event_base_sync);
|
||||
/* create the async one also so options are kept up to date */
|
||||
result->unbound_async = ub_ctx_create_event(result->event_base_sync);
|
||||
|
||||
result->async_set = 0;
|
||||
result->resolution_type_set = 0;
|
||||
|
||||
result->outbound_requests = ldns_rbtree_create(transaction_id_cmp);
|
||||
|
||||
result->resolution_type = GETDNS_CONTEXT_RECURSING;
|
||||
result->namespaces = create_default_namespaces();
|
||||
result->dns_transport = GETDNS_CONTEXT_UDP_FIRST_AND_FALL_BACK_TO_TCP;
|
||||
result->limit_outstanding_queries = 0;
|
||||
|
||||
result->timeout = 5000;
|
||||
result->follow_redirects = GETDNS_CONTEXT_FOLLOW_REDIRECTS;
|
||||
result->dns_root_servers = create_default_root_servers();
|
||||
|
@ -161,22 +230,12 @@ getdns_return_t getdns_context_create(
|
|||
result->suffix = NULL;
|
||||
|
||||
result->dnssec_trust_anchors = NULL;
|
||||
result->dnssec_allow_skew = 0;
|
||||
result->upstream_list = NULL;
|
||||
result->edns_maximum_udp_payload_size = 512;
|
||||
|
||||
result->edns_extended_rcode = 0;
|
||||
result->edns_version = 0;
|
||||
result->edns_do_bit = 0;
|
||||
|
||||
result->event_base = NULL;
|
||||
result->resolver_socket = 0;
|
||||
result->outbound_reqs = outbound_reqs;
|
||||
|
||||
result->update_callback = NULL;
|
||||
result->memory_allocator = malloc;
|
||||
result->memory_deallocator = free;
|
||||
result->memory_reallocator = realloc;
|
||||
|
||||
if (set_from_os) {
|
||||
if (GETDNS_RETURN_GOOD != set_os_defaults(result)) {
|
||||
getdns_context_destroy(result);
|
||||
|
@ -186,6 +245,12 @@ getdns_return_t getdns_context_create(
|
|||
|
||||
*context = result;
|
||||
|
||||
/* other opts */
|
||||
getdns_context_set_dnssec_allowed_skew(result, 0);
|
||||
getdns_context_set_edns_maximum_udp_payload_size(result, 512);
|
||||
getdns_context_set_dns_transport(result, GETDNS_CONTEXT_UDP_FIRST_AND_FALL_BACK_TO_TCP);
|
||||
|
||||
|
||||
return GETDNS_RETURN_GOOD;
|
||||
} /* getdns_context_create */
|
||||
|
||||
|
@ -211,13 +276,13 @@ getdns_context_destroy(
|
|||
getdns_list_destroy(context->dnssec_trust_anchors);
|
||||
getdns_list_destroy(context->upstream_list);
|
||||
|
||||
getdns_dict_destroy(context->outbound_reqs);
|
||||
/* destroy the ub context */
|
||||
ub_ctx_delete(context->unbound_async);
|
||||
ub_ctx_delete(context->unbound_sync);
|
||||
|
||||
/* TODO: cancel all events */
|
||||
event_base_free(context->event_base_sync);
|
||||
|
||||
if (context->resolver_socket != 0) {
|
||||
evutil_closesocket(context->resolver_socket);
|
||||
}
|
||||
ldns_rbtree_free(context->outbound_requests);
|
||||
|
||||
free(context);
|
||||
return;
|
||||
|
@ -237,6 +302,30 @@ getdns_context_set_context_update_callback(
|
|||
return GETDNS_RETURN_GOOD;
|
||||
} /* getdns_context_set_context_update_callback */
|
||||
|
||||
/*
|
||||
* Helpers to set options on the unbound ctx
|
||||
*/
|
||||
|
||||
static void set_ub_string_opt(getdns_context_t ctx, char* opt, char* value) {
|
||||
ub_ctx_set_option(ctx->unbound_sync, opt, value);
|
||||
ub_ctx_set_option(ctx->unbound_async, opt, value);
|
||||
}
|
||||
|
||||
static void set_ub_number_opt(getdns_context_t ctx, char* opt, uint16_t value) {
|
||||
char buffer[64];
|
||||
snprintf(buffer, 64, "%hu", value);
|
||||
set_ub_string_opt(ctx, opt, buffer);
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear the resolution type set flag if needed
|
||||
*/
|
||||
static inline void clear_resolution_type_set_flag(getdns_context_t context, uint16_t type) {
|
||||
if (context->resolution_type_set == type) {
|
||||
context->resolution_type_set = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* getdns_context_set_context_update
|
||||
*
|
||||
|
@ -323,15 +412,25 @@ getdns_context_set_dns_transport(
|
|||
uint16_t value
|
||||
)
|
||||
{
|
||||
if (value != GETDNS_CONTEXT_UDP_FIRST_AND_FALL_BACK_TO_TCP &&
|
||||
value != GETDNS_CONTEXT_UDP_ONLY &&
|
||||
value != GETDNS_CONTEXT_TCP_ONLY &&
|
||||
value != GETDNS_CONTEXT_TCP_ONLY_KEEP_CONNECTIONS_OPEN) {
|
||||
|
||||
switch (value) {
|
||||
case GETDNS_CONTEXT_UDP_FIRST_AND_FALL_BACK_TO_TCP:
|
||||
set_ub_string_opt(context, "do-udp", "yes");
|
||||
set_ub_string_opt(context, "do-tcp", "yes");
|
||||
break;
|
||||
case GETDNS_CONTEXT_UDP_ONLY:
|
||||
set_ub_string_opt(context, "do-udp", "yes");
|
||||
set_ub_string_opt(context, "do-tcp", "no");
|
||||
break;
|
||||
case GETDNS_CONTEXT_TCP_ONLY:
|
||||
set_ub_string_opt(context, "do-udp", "no");
|
||||
set_ub_string_opt(context, "do-tcp", "yes");
|
||||
break;
|
||||
default:
|
||||
/* TODO GETDNS_CONTEXT_TCP_ONLY_KEEP_CONNECTIONS_OPEN */
|
||||
return GETDNS_RETURN_CONTEXT_UPDATE_FAIL;
|
||||
}
|
||||
|
||||
context->dns_transport = value;
|
||||
|
||||
dispatch_updated(context, GETDNS_CONTEXT_CODE_DNS_TRANSPORT);
|
||||
|
||||
return GETDNS_RETURN_GOOD;
|
||||
|
@ -347,7 +446,8 @@ getdns_context_set_limit_outstanding_queries(
|
|||
uint16_t limit
|
||||
)
|
||||
{
|
||||
context->limit_outstanding_queries = limit;
|
||||
/* num-queries-per-thread */
|
||||
set_ub_number_opt(context, "num-queries-per-thread", limit);
|
||||
|
||||
dispatch_updated(context, GETDNS_CONTEXT_CODE_LIMIT_OUTSTANDING_QUERIES);
|
||||
|
||||
|
@ -383,6 +483,7 @@ getdns_context_set_follow_redirects(
|
|||
{
|
||||
context->follow_redirects = value;
|
||||
|
||||
clear_resolution_type_set_flag(context, GETDNS_CONTEXT_RECURSING);
|
||||
dispatch_updated(context, GETDNS_CONTEXT_CODE_FOLLOW_REDIRECTS);
|
||||
|
||||
return GETDNS_RETURN_GOOD;
|
||||
|
@ -399,15 +500,40 @@ getdns_context_set_dns_root_servers(
|
|||
)
|
||||
{
|
||||
getdns_list *copy = NULL;
|
||||
size_t count = 0;
|
||||
if (addresses != NULL) {
|
||||
if (getdns_list_copy(addresses, ©) != GETDNS_RETURN_GOOD) {
|
||||
return GETDNS_RETURN_CONTEXT_UPDATE_FAIL;
|
||||
}
|
||||
addresses = copy;
|
||||
getdns_list_get_length(addresses, &count);
|
||||
if (count == 0) {
|
||||
getdns_list_destroy(addresses);
|
||||
addresses = NULL;
|
||||
} else {
|
||||
size_t i = 0;
|
||||
getdns_return_t r = GETDNS_RETURN_GOOD;
|
||||
/* validate and add ip str */
|
||||
for (i = 0; i < count; ++i) {
|
||||
getdns_dict* dict = NULL;
|
||||
getdns_list_get_dict(addresses, i, &dict);
|
||||
r = add_ip_str(dict);
|
||||
if (r != GETDNS_RETURN_GOOD) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (r != GETDNS_RETURN_GOOD) {
|
||||
getdns_list_destroy(addresses);
|
||||
return GETDNS_RETURN_CONTEXT_UPDATE_FAIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getdns_list_destroy(context->dns_root_servers);
|
||||
context->dns_root_servers = addresses;
|
||||
|
||||
clear_resolution_type_set_flag(context, GETDNS_CONTEXT_RECURSING);
|
||||
|
||||
dispatch_updated(context, GETDNS_CONTEXT_CODE_DNS_ROOT_SERVERS);
|
||||
|
||||
return GETDNS_RETURN_GOOD;
|
||||
|
@ -427,7 +553,6 @@ getdns_context_set_append_name(
|
|||
value != GETDNS_CONTEXT_APPEND_NAME_ONLY_TO_SINGLE_LABEL_AFTER_FAILURE &&
|
||||
value != GETDNS_CONTEXT_APPEND_NAME_ONLY_TO_MULTIPLE_LABEL_NAME_AFTER_FAILURE &&
|
||||
value != GETDNS_CONTEXT_DO_NOT_APPEND_NAMES) {
|
||||
|
||||
return GETDNS_RETURN_CONTEXT_UPDATE_FAIL;
|
||||
}
|
||||
|
||||
|
@ -458,6 +583,8 @@ getdns_context_set_suffix(
|
|||
getdns_list_destroy(context->suffix);
|
||||
context->suffix = value;
|
||||
|
||||
clear_resolution_type_set_flag(context, GETDNS_CONTEXT_STUB);
|
||||
|
||||
dispatch_updated(context, GETDNS_CONTEXT_CODE_SUFFIX);
|
||||
|
||||
return GETDNS_RETURN_GOOD;
|
||||
|
@ -498,8 +625,8 @@ getdns_context_set_dnssec_allowed_skew(
|
|||
uint16_t value
|
||||
)
|
||||
{
|
||||
context->dnssec_allow_skew = value;
|
||||
|
||||
set_ub_number_opt(context, "val-sig-skew-min", value);
|
||||
set_ub_number_opt(context, "val-sig-skew-max", value);
|
||||
dispatch_updated(context, GETDNS_CONTEXT_CODE_DNSSEC_ALLOWED_SKEW);
|
||||
|
||||
return GETDNS_RETURN_GOOD;
|
||||
|
@ -515,7 +642,10 @@ getdns_context_set_stub_resolution(
|
|||
struct getdns_list *upstream_list
|
||||
)
|
||||
{
|
||||
if (upstream_list == NULL) {
|
||||
size_t count = 0;
|
||||
size_t i = 0;
|
||||
getdns_return_t r = getdns_list_get_length(upstream_list, &count);
|
||||
if (count == 0 || r != GETDNS_RETURN_GOOD) {
|
||||
return GETDNS_RETURN_CONTEXT_UPDATE_FAIL;
|
||||
}
|
||||
getdns_list *copy = NULL;
|
||||
|
@ -523,12 +653,26 @@ getdns_context_set_stub_resolution(
|
|||
return GETDNS_RETURN_CONTEXT_UPDATE_FAIL;
|
||||
}
|
||||
upstream_list = copy;
|
||||
/* validate and add ip str */
|
||||
for (i = 0; i < count; ++i) {
|
||||
getdns_dict* dict = NULL;
|
||||
getdns_list_get_dict(upstream_list, i, &dict);
|
||||
r = add_ip_str(dict);
|
||||
if (r != GETDNS_RETURN_GOOD) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
getdns_context_set_resolution_type(context, GETDNS_CONTEXT_STUB);
|
||||
if (r != GETDNS_RETURN_GOOD) {
|
||||
getdns_list_destroy(upstream_list);
|
||||
return GETDNS_RETURN_CONTEXT_UPDATE_FAIL;
|
||||
}
|
||||
|
||||
getdns_list_destroy(context->upstream_list);
|
||||
context->upstream_list = upstream_list;
|
||||
|
||||
clear_resolution_type_set_flag(context, GETDNS_CONTEXT_STUB);
|
||||
|
||||
dispatch_updated(context, GETDNS_CONTEXT_CODE_UPSTREAM_RECURSIVE_SERVERS);
|
||||
|
||||
return GETDNS_RETURN_GOOD;
|
||||
|
@ -549,7 +693,8 @@ getdns_context_set_edns_maximum_udp_payload_size(
|
|||
return GETDNS_RETURN_CONTEXT_UPDATE_FAIL;
|
||||
}
|
||||
|
||||
context->edns_maximum_udp_payload_size = value;
|
||||
/* max-udp-size */
|
||||
set_ub_number_opt(context, "max-udp-size", value);
|
||||
|
||||
dispatch_updated(context, GETDNS_CONTEXT_CODE_EDNS_MAXIMUM_UDP_PAYLOAD_SIZE);
|
||||
|
||||
|
@ -667,12 +812,33 @@ getdns_extension_set_libevent_base(
|
|||
struct event_base *this_event_base
|
||||
)
|
||||
{
|
||||
/* TODO: cancel anything on an existing event base */
|
||||
context->event_base = this_event_base;
|
||||
|
||||
if (this_event_base) {
|
||||
ub_ctx_set_event(context->unbound_async, this_event_base);
|
||||
context->async_set = 1;
|
||||
} else {
|
||||
ub_ctx_set_event(context->unbound_async, context->event_base_sync);
|
||||
context->async_set = 0;
|
||||
}
|
||||
return GETDNS_RETURN_GOOD;
|
||||
} /* getdns_extension_set_libevent_base */
|
||||
|
||||
/* cancel the request */
|
||||
static void cancel_dns_req(getdns_dns_req* req) {
|
||||
getdns_network_req* netreq = req->first_req;
|
||||
while (netreq) {
|
||||
if (netreq->state == NET_REQ_IN_FLIGHT) {
|
||||
/* for ev based ub, this should always prevent
|
||||
* the callback from firing */
|
||||
ub_cancel(req->unbound, netreq->unbound_id);
|
||||
netreq->state = NET_REQ_CANCELED;
|
||||
} else if (netreq->state == NET_REQ_NOT_SENT) {
|
||||
netreq->state = NET_REQ_CANCELED;
|
||||
}
|
||||
netreq = netreq->next;
|
||||
}
|
||||
req->canceled = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* getdns_cancel_callback
|
||||
*
|
||||
|
@ -683,9 +849,112 @@ getdns_cancel_callback(
|
|||
getdns_transaction_t transaction_id
|
||||
)
|
||||
{
|
||||
UNUSED_PARAM(context);
|
||||
UNUSED_PARAM(transaction_id);
|
||||
getdns_dns_req *req = NULL;
|
||||
getdns_callback_t cb = NULL;
|
||||
void* user_pointer = NULL;
|
||||
|
||||
/* delete the node from the tree */
|
||||
ldns_rbnode_t* node = ldns_rbtree_delete(context->outbound_requests,
|
||||
&transaction_id);
|
||||
|
||||
if (!node) {
|
||||
return GETDNS_RETURN_UNKNOWN_TRANSACTION;
|
||||
}
|
||||
req = (getdns_dns_req*) node->data;
|
||||
/* do the cancel */
|
||||
|
||||
cancel_dns_req(req);
|
||||
cb = req->user_callback;
|
||||
user_pointer = req->user_pointer;
|
||||
|
||||
/* clean up */
|
||||
context->memory_deallocator(node);
|
||||
dns_req_free(req);
|
||||
|
||||
/* fire callback */
|
||||
cb(context,
|
||||
GETDNS_CALLBACK_CANCEL,
|
||||
NULL,
|
||||
user_pointer,
|
||||
transaction_id);
|
||||
|
||||
return GETDNS_RETURN_GOOD;
|
||||
} /* getdns_cancel_callback */
|
||||
|
||||
static void ub_setup_stub(struct ub_ctx* ctx, getdns_list* upstreams, size_t count) {
|
||||
size_t i;
|
||||
/* reset forwarding servers */
|
||||
ub_ctx_set_fwd(ctx, NULL);
|
||||
for (i = 0 ; i < count; ++i) {
|
||||
getdns_dict* dict = NULL;
|
||||
char* ip_str = NULL;
|
||||
getdns_list_get_dict(upstreams, i, &dict);
|
||||
getdns_dict_util_get_string(dict, GETDNS_STR_ADDRESS_STRING, &ip_str);
|
||||
ub_ctx_set_fwd(ctx, ip_str);
|
||||
}
|
||||
}
|
||||
|
||||
getdns_return_t getdns_context_prepare_for_resolution(getdns_context_t context) {
|
||||
if (context->resolution_type_set == context->resolution_type) {
|
||||
/* already set and no config changes have caused this to be
|
||||
* bad.
|
||||
*/
|
||||
return GETDNS_RETURN_GOOD;
|
||||
}
|
||||
if (context->resolution_type == GETDNS_CONTEXT_STUB) {
|
||||
size_t upstream_len = 0;
|
||||
getdns_return_t r = getdns_list_get_length(context->upstream_list, &upstream_len);
|
||||
if (r != GETDNS_RETURN_GOOD || upstream_len == 0) {
|
||||
return GETDNS_RETURN_BAD_CONTEXT;
|
||||
}
|
||||
/* set upstreams */
|
||||
ub_setup_stub(context->unbound_async, context->upstream_list, upstream_len);
|
||||
ub_setup_stub(context->unbound_sync, context->upstream_list, upstream_len);
|
||||
|
||||
} else if (context->resolution_type == GETDNS_CONTEXT_RECURSING) {
|
||||
/* set recursive */
|
||||
/* TODO: use the root servers via root hints file */
|
||||
ub_ctx_set_fwd(context->unbound_async, NULL);
|
||||
ub_ctx_set_fwd(context->unbound_sync, NULL);
|
||||
|
||||
} else {
|
||||
/* bogus? */
|
||||
return GETDNS_RETURN_BAD_CONTEXT;
|
||||
}
|
||||
context->resolution_type_set = context->resolution_type;
|
||||
return GETDNS_RETURN_GOOD;
|
||||
}
|
||||
|
||||
getdns_return_t getdns_context_track_outbound_request(getdns_dns_req* req) {
|
||||
if (!req) {
|
||||
return GETDNS_RETURN_GENERIC_ERROR;
|
||||
}
|
||||
getdns_context_t context = req->context;
|
||||
ldns_rbnode_t* node = context->memory_allocator(sizeof(ldns_rbnode_t));
|
||||
if (!node) {
|
||||
return GETDNS_RETURN_GENERIC_ERROR;
|
||||
}
|
||||
node->key = &(req->trans_id);
|
||||
node->data = req;
|
||||
if (!ldns_rbtree_insert(context->outbound_requests, node)) {
|
||||
/* free the node */
|
||||
context->memory_deallocator(node);
|
||||
return GETDNS_RETURN_GENERIC_ERROR;
|
||||
}
|
||||
return GETDNS_RETURN_GOOD;
|
||||
}
|
||||
|
||||
getdns_return_t getdns_context_clear_outbound_request(getdns_dns_req* req) {
|
||||
if (!req) {
|
||||
return GETDNS_RETURN_GENERIC_ERROR;
|
||||
}
|
||||
getdns_context_t context = req->context;
|
||||
ldns_rbnode_t* node = ldns_rbtree_delete(context->outbound_requests,
|
||||
&(req->trans_id));
|
||||
if (node) {
|
||||
context->memory_deallocator(node);
|
||||
}
|
||||
return GETDNS_RETURN_GOOD;
|
||||
}
|
||||
|
||||
/* getdns_context.c */
|
||||
|
|
|
@ -32,6 +32,11 @@
|
|||
|
||||
#include <getdns/getdns.h>
|
||||
|
||||
struct event_base;
|
||||
struct ub_ctx;
|
||||
struct ldns_rbtree_t;
|
||||
struct getdns_dns_req;
|
||||
|
||||
/** function pointer typedefs */
|
||||
typedef void (*getdns_update_callback)(getdns_context_t context, uint16_t changed_item);
|
||||
typedef void* (*getdns_memory_allocator)(size_t size);
|
||||
|
@ -43,17 +48,14 @@ struct getdns_context_t {
|
|||
/* Context values */
|
||||
uint16_t resolution_type;
|
||||
uint16_t *namespaces;
|
||||
uint16_t dns_transport;
|
||||
uint16_t limit_outstanding_queries;
|
||||
uint16_t timeout;
|
||||
uint16_t follow_redirects;
|
||||
struct getdns_list *dns_root_servers;
|
||||
uint16_t append_name;
|
||||
struct getdns_list *suffix;
|
||||
struct getdns_list *dnssec_trust_anchors;
|
||||
uint16_t dnssec_allow_skew;
|
||||
struct getdns_list *upstream_list;
|
||||
uint16_t edns_maximum_udp_payload_size;
|
||||
|
||||
uint8_t edns_extended_rcode;
|
||||
uint8_t edns_version;
|
||||
uint8_t edns_do_bit;
|
||||
|
@ -63,15 +65,38 @@ struct getdns_context_t {
|
|||
getdns_memory_deallocator memory_deallocator;
|
||||
getdns_memory_reallocator memory_reallocator;
|
||||
|
||||
/* Event loop */
|
||||
struct event_base* event_base;
|
||||
/* Event loop for sync requests */
|
||||
struct event_base* event_base_sync;
|
||||
|
||||
/* outbound request dict (transaction -> req struct) */
|
||||
getdns_dict *outbound_reqs;
|
||||
/* The underlying unbound contexts that do
|
||||
the real work */
|
||||
struct ub_ctx *unbound_sync;
|
||||
struct ub_ctx *unbound_async;
|
||||
/* whether an async event base was set */
|
||||
uint8_t async_set;
|
||||
|
||||
/* socket */
|
||||
evutil_socket_t resolver_socket;
|
||||
/* which resolution type the contexts are configured for
|
||||
* 0 means nothing set
|
||||
*/
|
||||
uint8_t resolution_type_set;
|
||||
|
||||
/*
|
||||
* outbound requests -> transaction to getdns_dns_req
|
||||
*/
|
||||
struct ldns_rbtree_t* outbound_requests;
|
||||
} ;
|
||||
|
||||
/** internal functions **/
|
||||
/**
|
||||
* Sets up the unbound contexts with stub or recursive behavior
|
||||
* if needed.
|
||||
*/
|
||||
getdns_return_t getdns_context_prepare_for_resolution(getdns_context_t context);
|
||||
|
||||
/* track an outbound request */
|
||||
getdns_return_t getdns_context_track_outbound_request(struct getdns_dns_req* req);
|
||||
/* clear the outbound request from being tracked - does not cancel it */
|
||||
getdns_return_t getdns_context_clear_outbound_request(struct getdns_dns_req* req);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
|
||||
#include <getdns/getdns.h>
|
||||
#include <stdio.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <util-internal.h>
|
||||
|
||||
/* stuff to make it compile pedantically */
|
||||
#define UNUSED_PARAM(x) ((void)(x))
|
||||
|
@ -62,7 +64,32 @@ char *
|
|||
getdns_display_ip_address(
|
||||
struct getdns_bindata *bindata_of_ipv4_or_ipv6_address
|
||||
)
|
||||
{ UNUSED_PARAM(bindata_of_ipv4_or_ipv6_address); return NULL; }
|
||||
{
|
||||
char buff[256];
|
||||
if (!bindata_of_ipv4_or_ipv6_address ||
|
||||
bindata_of_ipv4_or_ipv6_address->size == 0 ||
|
||||
!bindata_of_ipv4_or_ipv6_address->data) {
|
||||
return NULL;
|
||||
}
|
||||
if (bindata_of_ipv4_or_ipv6_address->size == 4) {
|
||||
const char* ipStr = inet_ntop(AF_INET,
|
||||
bindata_of_ipv4_or_ipv6_address->data,
|
||||
buff,
|
||||
256);
|
||||
if (ipStr) {
|
||||
return strdup(ipStr);
|
||||
}
|
||||
} else if (bindata_of_ipv4_or_ipv6_address->size == 16) {
|
||||
const char* ipStr = inet_ntop(AF_INET6,
|
||||
bindata_of_ipv4_or_ipv6_address->data,
|
||||
buff,
|
||||
256);
|
||||
if (ipStr) {
|
||||
return strdup(ipStr);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
getdns_return_t
|
||||
getdns_strerror(getdns_return_t err, char *buf, size_t buflen)
|
||||
|
|
|
@ -17,7 +17,7 @@ VPATH = @srcdir@
|
|||
CC=gcc
|
||||
CFLAGS=-Wall -g -fPIC -I$(srcdir)/ -I$(srcdir)/../ -I/usr/local/include -std=c99
|
||||
LDFLAGS=-L. -L$(srcdir)/.. -L/usr/local/lib
|
||||
LDLIBS=-lgetdns -lldns -levent_core
|
||||
LDLIBS=-lgetdns -lldns -levent_core -lunbound
|
||||
PROGRAMS=example_simple_answers example_tree example_all_functions example_synchronous
|
||||
|
||||
.SUFFIXES: .c .o .a .lo .h
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include <getdns/getdns.h>
|
||||
#include <event2/event.h>
|
||||
|
||||
#define UNUSED_PARAM(x) ((void)(x))
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include <getdns/getdns.h>
|
||||
#include <event2/event.h>
|
||||
|
||||
#define UNUSED_PARAM(x) ((void)(x))
|
||||
|
||||
|
|
267
src/general.c
267
src/general.c
|
@ -58,138 +58,190 @@
|
|||
* Version: 0.1b
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <unbound.h>
|
||||
#include <unbound-event.h>
|
||||
#include <ldns/ldns.h>
|
||||
#include "context.h"
|
||||
#include "types-internal.h"
|
||||
#include "util-internal.h"
|
||||
|
||||
/* stuff to make it compile pedantically */
|
||||
#define UNUSED_PARAM(x) ((void)(x))
|
||||
|
||||
/* libevent callback for a network request */
|
||||
static void dns_req_callback(int fd, short events, void *arg) {
|
||||
getdns_dns_req *request = (getdns_dns_req*) arg;
|
||||
uint8_t data[1500];
|
||||
if (events & EV_READ) {
|
||||
while (1) {
|
||||
ssize_t r = recv(fd, data, sizeof(data), MSG_DONTWAIT);
|
||||
if (r < 0) {
|
||||
if (errno == EAGAIN) return;
|
||||
/* otherwise failed */
|
||||
request->user_callback(request->context,
|
||||
/* declarations */
|
||||
static void ub_resolve_callback(void* arg, int err, ldns_buffer* result, int sec, char* bogus);
|
||||
static void handle_network_request_error(getdns_network_req* netreq, int err);
|
||||
static void handle_dns_request_complete(getdns_dns_req* dns_req);
|
||||
static int submit_network_request(getdns_network_req* netreq);
|
||||
|
||||
/* cleanup and send an error to the user callback */
|
||||
static void handle_network_request_error(getdns_network_req* netreq, int err) {
|
||||
getdns_dns_req *dns_req = netreq->owner;
|
||||
getdns_context_t context = dns_req->context;
|
||||
getdns_transaction_t trans_id = dns_req->trans_id;
|
||||
getdns_callback_t cb = dns_req->user_callback;
|
||||
void* user_arg = dns_req->user_pointer;
|
||||
|
||||
/* clean up */
|
||||
getdns_context_clear_outbound_request(dns_req);
|
||||
dns_req_free(dns_req);
|
||||
|
||||
cb(context,
|
||||
GETDNS_CALLBACK_ERROR,
|
||||
NULL, request->user_pointer,
|
||||
request->trans_id);
|
||||
}
|
||||
/* parse a packet */
|
||||
ldns_pkt* pkt = NULL;
|
||||
ldns_wire2pkt(&pkt, data, r);
|
||||
if (pkt == NULL) {
|
||||
/* otherwise failed */
|
||||
request->user_callback(request->context,
|
||||
GETDNS_CALLBACK_ERROR,
|
||||
NULL, request->user_pointer,
|
||||
request->trans_id);
|
||||
NULL,
|
||||
user_arg,
|
||||
trans_id);
|
||||
}
|
||||
|
||||
/* cleanup and send the response to the user callback */
|
||||
static void handle_dns_request_complete(getdns_dns_req* dns_req) {
|
||||
getdns_dict* response = create_getdns_response(dns_req);
|
||||
|
||||
getdns_context_t context = dns_req->context;
|
||||
getdns_transaction_t trans_id = dns_req->trans_id;
|
||||
getdns_callback_t cb = dns_req->user_callback;
|
||||
void* user_arg = dns_req->user_pointer;
|
||||
|
||||
/* clean up the request */
|
||||
getdns_context_clear_outbound_request(dns_req);
|
||||
dns_req_free(dns_req);
|
||||
if (response) {
|
||||
cb(context,
|
||||
GETDNS_CALLBACK_COMPLETE,
|
||||
response,
|
||||
user_arg,
|
||||
trans_id);
|
||||
} else {
|
||||
/* success */
|
||||
getdns_dict* response = create_getdns_response(pkt);
|
||||
ldns_pkt_free(pkt);
|
||||
request->user_callback(request->context, GETDNS_CALLBACK_COMPLETE,
|
||||
response, request->user_pointer,
|
||||
request->trans_id);
|
||||
cb(context,
|
||||
GETDNS_CALLBACK_ERROR,
|
||||
NULL,
|
||||
user_arg,
|
||||
trans_id);
|
||||
}
|
||||
}
|
||||
} else if (events & EV_TIMEOUT) {
|
||||
request->user_callback(request->context, GETDNS_CALLBACK_TIMEOUT,
|
||||
NULL, request->user_pointer, request->trans_id);
|
||||
}
|
||||
/* clean up ns since right now it's 1:1 with the request */
|
||||
nameserver_free(request->current_req->ns);
|
||||
/* cleanup the request */
|
||||
dns_req_free(request);
|
||||
|
||||
}
|
||||
|
||||
/* submit a new request to the event loop */
|
||||
static getdns_return_t submit_new_dns_req(getdns_dns_req *request) {
|
||||
getdns_dict *ip_dict = NULL;
|
||||
getdns_context_t context = request->context;
|
||||
uint8_t* data = NULL;
|
||||
size_t data_len = 0;
|
||||
struct timeval timeout = { 5, 0 };
|
||||
|
||||
/* get first upstream server */
|
||||
getdns_list_get_dict(context->upstream_list, 0, &ip_dict);
|
||||
if (!ip_dict) {
|
||||
return GETDNS_RETURN_GENERIC_ERROR;
|
||||
}
|
||||
|
||||
/* get the nameserver */
|
||||
getdns_nameserver *ns = nameserver_new_from_ip_dict(context, ip_dict);
|
||||
if (!ns) {
|
||||
return GETDNS_RETURN_GENERIC_ERROR;
|
||||
}
|
||||
|
||||
request->current_req->ns = ns;
|
||||
|
||||
/* schedule on the loop */
|
||||
ns->event = event_new(context->event_base, request->current_req->ns->socket,
|
||||
EV_READ | EV_TIMEOUT,
|
||||
dns_req_callback, request);
|
||||
|
||||
event_add(ns->event, &timeout);
|
||||
|
||||
/* send data */
|
||||
ldns_pkt *pkt = request->current_req->pkt;
|
||||
ldns_pkt2wire(&data, pkt, &data_len);
|
||||
send(ns->socket, data, data_len, MSG_DONTWAIT);
|
||||
free(data);
|
||||
|
||||
return GETDNS_RETURN_GOOD;
|
||||
static int submit_network_request(getdns_network_req* netreq) {
|
||||
getdns_dns_req *dns_req = netreq->owner;
|
||||
int r = ub_resolve_event(dns_req->unbound,
|
||||
dns_req->name,
|
||||
netreq->request_type,
|
||||
netreq->request_class,
|
||||
netreq,
|
||||
ub_resolve_callback,
|
||||
&(netreq->unbound_id));
|
||||
netreq->state = NET_REQ_IN_FLIGHT;
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* getdns_general
|
||||
static void ub_resolve_callback(void* arg, int err, ldns_buffer* result, int sec, char* bogus) {
|
||||
getdns_network_req* netreq = (getdns_network_req*) arg;
|
||||
/* if netreq->state == NET_REQ_NOT_SENT here, that implies
|
||||
* that ub called us back immediately - probably from a local file.
|
||||
* This most likely means that getdns_general has not returned
|
||||
*/
|
||||
netreq->state = NET_REQ_FINISHED;
|
||||
if (err) {
|
||||
handle_network_request_error(netreq, err);
|
||||
} else {
|
||||
/* parse */
|
||||
ldns_status r = ldns_buffer2pkt_wire(&(netreq->result), result);
|
||||
if (r != LDNS_STATUS_OK) {
|
||||
handle_network_request_error(netreq, r);
|
||||
} else {
|
||||
/* is this the last request */
|
||||
if (!netreq->next) {
|
||||
/* finished */
|
||||
handle_dns_request_complete(netreq->owner);
|
||||
} else {
|
||||
/* not finished - update to next request and ship it */
|
||||
getdns_dns_req* dns_req = netreq->owner;
|
||||
dns_req->current_req = netreq->next;
|
||||
submit_network_request(netreq->next);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getdns_return_t
|
||||
getdns_general(
|
||||
getdns_general_ub(struct ub_ctx* unbound,
|
||||
getdns_context_t context,
|
||||
const char *name,
|
||||
uint16_t request_type,
|
||||
struct getdns_dict *extensions,
|
||||
void *userarg,
|
||||
getdns_transaction_t *transaction_id,
|
||||
getdns_callback_t callback
|
||||
)
|
||||
{
|
||||
/* Default to zero */
|
||||
if (transaction_id != NULL) {
|
||||
*transaction_id = 0;
|
||||
getdns_callback_t callbackfn) {
|
||||
|
||||
getdns_return_t gr;
|
||||
int r;
|
||||
|
||||
gr = getdns_context_prepare_for_resolution(context);
|
||||
if (gr != GETDNS_RETURN_GOOD) {
|
||||
return GETDNS_RETURN_BAD_CONTEXT;
|
||||
}
|
||||
if (!context || context->event_base == NULL ||
|
||||
callback == NULL ||
|
||||
context->resolution_type != GETDNS_CONTEXT_STUB) {
|
||||
|
||||
/* request state */
|
||||
getdns_dns_req* req = dns_req_new(context,
|
||||
unbound,
|
||||
name,
|
||||
request_type,
|
||||
extensions);
|
||||
if (!req) {
|
||||
return GETDNS_RETURN_GENERIC_ERROR;
|
||||
}
|
||||
|
||||
req->user_pointer = userarg;
|
||||
req->user_callback = callbackfn;
|
||||
|
||||
if (transaction_id) {
|
||||
*transaction_id = req->trans_id;
|
||||
}
|
||||
|
||||
getdns_context_track_outbound_request(req);
|
||||
|
||||
/* issue the first network req */
|
||||
r = submit_network_request(req->first_req);
|
||||
|
||||
if (r != 0) {
|
||||
/* clean up the request */
|
||||
getdns_context_clear_outbound_request(req);
|
||||
dns_req_free(req);
|
||||
return GETDNS_RETURN_GENERIC_ERROR;
|
||||
}
|
||||
return GETDNS_RETURN_GOOD;
|
||||
}
|
||||
|
||||
/*
|
||||
* getdns_general
|
||||
*/
|
||||
getdns_return_t
|
||||
getdns_general(getdns_context_t context,
|
||||
const char *name,
|
||||
uint16_t request_type,
|
||||
struct getdns_dict *extensions,
|
||||
void *userarg,
|
||||
getdns_transaction_t *transaction_id,
|
||||
getdns_callback_t callback) {
|
||||
|
||||
if (!context || context->async_set == 0 ||
|
||||
callback == NULL) {
|
||||
/* Can't do async without an event loop
|
||||
* or callback
|
||||
*
|
||||
* Only supports stub right now.
|
||||
*/
|
||||
return GETDNS_RETURN_BAD_CONTEXT;
|
||||
}
|
||||
|
||||
return getdns_general_ub(context->unbound_async,
|
||||
context,
|
||||
name,
|
||||
request_type,
|
||||
extensions,
|
||||
userarg,
|
||||
transaction_id,
|
||||
callback);
|
||||
|
||||
/* create a req */
|
||||
getdns_dns_req *dns_req = dns_req_new(context, name, request_type,
|
||||
extensions, transaction_id);
|
||||
if (dns_req == NULL) {
|
||||
return GETDNS_RETURN_GENERIC_ERROR;
|
||||
}
|
||||
|
||||
dns_req->user_callback = callback;
|
||||
dns_req->user_pointer = userarg;
|
||||
|
||||
/* submit it */
|
||||
submit_new_dns_req(dns_req);
|
||||
|
||||
return GETDNS_RETURN_GOOD;
|
||||
} /* getdns_general */
|
||||
|
||||
|
||||
|
@ -197,16 +249,13 @@ getdns_general(
|
|||
* getdns_address
|
||||
*
|
||||
*/
|
||||
getdns_return_t
|
||||
getdns_address(
|
||||
getdns_context_t context,
|
||||
getdns_return_t
|
||||
getdns_address(getdns_context_t context,
|
||||
const char *name,
|
||||
struct getdns_dict *extensions,
|
||||
void *userarg,
|
||||
getdns_transaction_t *transaction_id,
|
||||
getdns_callback_t callback
|
||||
)
|
||||
{
|
||||
getdns_callback_t callback) {
|
||||
int cleanup_extensions = 0;
|
||||
if (!extensions) {
|
||||
extensions = getdns_dict_create();
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
/**
|
||||
*
|
||||
* /brief getdns contect management functions
|
||||
*
|
||||
* This is the meat of the API
|
||||
* Originally taken from the getdns API description pseudo implementation.
|
||||
*
|
||||
*/
|
||||
/* The MIT License (MIT)
|
||||
* Copyright (c) 2013 Verisign, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef _GETDNS_GENERAL_H_
|
||||
#define _GETDNS_GENERAL_H_
|
||||
|
||||
#include <getdns/getdns.h>
|
||||
|
||||
struct ub_ctx;
|
||||
|
||||
getdns_return_t
|
||||
getdns_general_ub(
|
||||
struct ub_ctx* unbound,
|
||||
getdns_context_t context,
|
||||
const char *name,
|
||||
uint16_t request_type,
|
||||
struct getdns_dict *extensions,
|
||||
void *userarg,
|
||||
getdns_transaction_t *transaction_id,
|
||||
getdns_callback_t callbackfn
|
||||
);
|
||||
|
||||
#endif
|
|
@ -34,7 +34,8 @@
|
|||
#include <stdlib.h>
|
||||
#include <netinet/in.h>
|
||||
#include <stdbool.h>
|
||||
#include <event2/event.h>
|
||||
|
||||
struct event_base;
|
||||
|
||||
#define GETDNS_COMPILATION_COMMENT The API implementation should fill in something here, such as a compilation version string and date, and change it each time the API is compiled.
|
||||
|
||||
|
@ -247,9 +248,42 @@
|
|||
#define GETDNS_STR_IPV6 "IPv6"
|
||||
#define GETDNS_STR_ADDRESS_TYPE "address_type"
|
||||
#define GETDNS_STR_ADDRESS_DATA "address_data"
|
||||
#define GETDNS_STR_ADDRESS_STRING "address_string"
|
||||
#define GETDNS_STR_PORT "port"
|
||||
#define GETDNS_STR_EXTENSION_RETURN_BOTH_V4_AND_V6 "return_both_v4_and_v6"
|
||||
|
||||
#define GETDNS_STR_KEY_STATUS "status"
|
||||
#define GETDNS_STR_KEY_REPLIES_TREE "replies_tree"
|
||||
#define GETDNS_STR_KEY_REPLIES_FULL "replies_full"
|
||||
#define GETDNS_STR_KEY_JUST_ADDRS "just_address_answers"
|
||||
#define GETDNS_STR_KEY_CANONICAL_NM "canonical_name"
|
||||
#define GETDNS_STR_KEY_ANSWER_TYPE "answer_type"
|
||||
#define GETDNS_STR_KEY_INTERM_ALIASES "intermediate_aliases"
|
||||
#define GETDNS_STR_KEY_NAME "name"
|
||||
#define GETDNS_STR_KEY_HEADER "header"
|
||||
#define GETDNS_STR_KEY_QUESTION "question"
|
||||
#define GETDNS_STR_KEY_ANSWER "answer"
|
||||
#define GETDNS_STR_KEY_ID "id"
|
||||
#define GETDNS_STR_KEY_QR "qr"
|
||||
#define GETDNS_STR_KEY_OPC "opcode"
|
||||
#define GETDNS_STR_KEY_TYPE "type"
|
||||
#define GETDNS_STR_KEY_CLASS "class"
|
||||
#define GETDNS_STR_KEY_TTL "ttl"
|
||||
#define GETDNS_STR_KEY_RDATA "rdata"
|
||||
#define GETDNS_STR_KEY_V4_ADDR "ipv4_address"
|
||||
#define GETDNS_STR_KEY_V6_ADDR "ipv6_address"
|
||||
#define GETDNS_STR_KEY_RDATA_RAW "rdata_raw"
|
||||
#define GETDNS_STR_KEY_AUTHORITY "authority"
|
||||
#define GETDNS_STR_KEY_ADDITIONAL "additional"
|
||||
#define GETDNS_STR_KEY_QTYPE "qtype"
|
||||
#define GETDNS_STR_KEY_QCLASS "qclass"
|
||||
#define GETDNS_STR_KEY_QNAME "qname"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** @}
|
||||
*/
|
||||
|
||||
|
|
|
@ -214,6 +214,11 @@ getdns_list_copy(struct getdns_list *srclist, struct getdns_list **dstlist)
|
|||
else
|
||||
retval = GETDNS_RETURN_GENERIC_ERROR;
|
||||
}
|
||||
else if (srclist->items[i].dtype == t_dict)
|
||||
{
|
||||
retval = getdns_dict_copy(srclist->items[index].data.dict,
|
||||
&((*dstlist)->items[i].data.dict));
|
||||
}
|
||||
}
|
||||
else
|
||||
retval = GETDNS_RETURN_GENERIC_ERROR;
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
#include "types-internal.h"
|
||||
#include "util-internal.h"
|
||||
#include <unbound.h>
|
||||
|
||||
/* useful macros */
|
||||
#define gd_malloc(sz) context->memory_allocator(sz)
|
||||
|
@ -39,36 +40,33 @@ void network_req_free(getdns_network_req* net_req) {
|
|||
if (!net_req) {
|
||||
return;
|
||||
}
|
||||
getdns_context_t context = net_req->context;
|
||||
if (net_req->pkt) {
|
||||
ldns_pkt_free(net_req->pkt);
|
||||
getdns_context_t context = net_req->owner->context;
|
||||
if (net_req->result) {
|
||||
ldns_pkt_free(net_req->result);
|
||||
}
|
||||
gd_free(net_req);
|
||||
}
|
||||
|
||||
getdns_network_req* network_req_new(getdns_context_t context,
|
||||
const char* name,
|
||||
getdns_network_req* network_req_new(getdns_dns_req* owner,
|
||||
uint16_t request_type,
|
||||
uint16_t request_class,
|
||||
struct getdns_dict* extensions) {
|
||||
getdns_network_req *net_req = NULL;
|
||||
ldns_pkt *pkt = NULL;
|
||||
net_req = gd_malloc(sizeof(getdns_network_req));
|
||||
|
||||
getdns_context_t context = owner->context;
|
||||
getdns_network_req* net_req = gd_malloc(sizeof(getdns_network_req));
|
||||
if (!net_req) {
|
||||
return NULL;
|
||||
}
|
||||
net_req->ns = NULL;
|
||||
net_req->pkt = NULL;
|
||||
net_req->context = context;
|
||||
net_req->request_type = request_type;
|
||||
net_req->result = NULL;
|
||||
net_req->next = NULL;
|
||||
|
||||
/* create ldns packet */
|
||||
pkt = create_new_pkt(context, name, request_type, extensions);
|
||||
if (!pkt) {
|
||||
/* free up the req */
|
||||
network_req_free(net_req);
|
||||
return NULL;
|
||||
}
|
||||
net_req->pkt = pkt;
|
||||
net_req->request_type = request_type;
|
||||
net_req->request_class = request_class;
|
||||
net_req->unbound_id = -1;
|
||||
net_req->state = NET_REQ_NOT_SENT;
|
||||
net_req->owner = owner;
|
||||
|
||||
/* TODO: records and other extensions */
|
||||
|
||||
return net_req;
|
||||
}
|
||||
|
@ -77,43 +75,87 @@ void dns_req_free(getdns_dns_req* req) {
|
|||
if (!req) {
|
||||
return;
|
||||
}
|
||||
getdns_network_req *net_req = NULL;
|
||||
getdns_context_t context = req->context;
|
||||
network_req_free(req->current_req);
|
||||
|
||||
/* free network requests */
|
||||
net_req = req->first_req;
|
||||
while (net_req) {
|
||||
getdns_network_req *next = net_req->next;
|
||||
network_req_free(net_req);
|
||||
net_req = next;
|
||||
}
|
||||
|
||||
/* free strduped name */
|
||||
free(req->name);
|
||||
|
||||
gd_free(req);
|
||||
}
|
||||
|
||||
/* create a new dns req to be submitted */
|
||||
getdns_dns_req* dns_req_new(getdns_context_t context,
|
||||
struct ub_ctx* unbound,
|
||||
const char* name,
|
||||
uint16_t request_type,
|
||||
struct getdns_dict *extensions,
|
||||
getdns_transaction_t *transaction_id) {
|
||||
struct getdns_dict *extensions) {
|
||||
|
||||
getdns_dns_req *result = NULL;
|
||||
getdns_network_req *net_req = NULL;
|
||||
getdns_network_req *req = NULL;
|
||||
getdns_return_t r;
|
||||
uint32_t both = GETDNS_EXTENSION_FALSE;
|
||||
|
||||
result = gd_malloc(sizeof(getdns_dns_req));
|
||||
if (result == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
result->name = strdup(name);
|
||||
result->context = context;
|
||||
result->unbound = unbound;
|
||||
result->canceled = 0;
|
||||
result->current_req = NULL;
|
||||
result->pending_cb = 0;
|
||||
result->resolver_type = context->resolution_type;
|
||||
result->first_req = NULL;
|
||||
result->trans_id = ldns_get_random();
|
||||
|
||||
/* create the initial network request */
|
||||
net_req = network_req_new(context, name, request_type,
|
||||
getdns_dict_copy(extensions, &result->extensions);
|
||||
|
||||
/* will be set by caller */
|
||||
result->user_pointer = NULL;
|
||||
result->user_callback = NULL;
|
||||
|
||||
/* create the requests */
|
||||
req = network_req_new(result,
|
||||
request_type,
|
||||
LDNS_RR_CLASS_IN,
|
||||
extensions);
|
||||
if (!net_req) {
|
||||
if (!req) {
|
||||
dns_req_free(result);
|
||||
result = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
result->trans_id = ldns_pkt_id(net_req->pkt);
|
||||
if (transaction_id) {
|
||||
*transaction_id = result->trans_id;
|
||||
}
|
||||
result->current_req = req;
|
||||
result->first_req = req;
|
||||
|
||||
result->current_req = net_req;
|
||||
net_req->owner = result;
|
||||
/* tack on A or AAAA if needed */
|
||||
r = getdns_dict_get_int(extensions,
|
||||
GETDNS_STR_EXTENSION_RETURN_BOTH_V4_AND_V6,
|
||||
&both);
|
||||
if (r == GETDNS_RETURN_GOOD &&
|
||||
both == GETDNS_EXTENSION_TRUE &&
|
||||
(request_type == GETDNS_RRTYPE_A || request_type == GETDNS_RRTYPE_AAAA)) {
|
||||
|
||||
uint16_t next_req_type = (request_type == GETDNS_RRTYPE_A) ? GETDNS_RRTYPE_AAAA : GETDNS_RRTYPE_A;
|
||||
getdns_network_req* next_req =
|
||||
network_req_new(result,
|
||||
next_req_type,
|
||||
LDNS_RR_CLASS_IN,
|
||||
extensions);
|
||||
if (!next_req) {
|
||||
dns_req_free(result);
|
||||
return NULL;
|
||||
}
|
||||
req->next = next_req;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
19
src/sync.c
19
src/sync.c
|
@ -29,7 +29,10 @@
|
|||
|
||||
#include <getdns/getdns.h>
|
||||
#include <pthread.h>
|
||||
#include "types-internal.h"
|
||||
#include <event2/event.h>
|
||||
#include <unbound-event.h>
|
||||
#include "context.h"
|
||||
#include "general.h"
|
||||
|
||||
/* stuff to make it compile pedantically */
|
||||
#define UNUSED_PARAM(x) ((void)(x))
|
||||
|
@ -55,10 +58,9 @@ static void sync_callback_func(getdns_context_t context,
|
|||
|
||||
static void * request_thread_start(void *arg) {
|
||||
struct sync_request_data *req_data = arg;
|
||||
struct event_base *event_base = event_base_new();
|
||||
|
||||
getdns_extension_set_libevent_base(req_data->context, event_base);
|
||||
req_data->response_status = getdns_general(req_data->context,
|
||||
req_data->response_status = getdns_general_ub(req_data->context->unbound_sync,
|
||||
req_data->context,
|
||||
req_data->name,
|
||||
req_data->request_type,
|
||||
req_data->extensions,
|
||||
|
@ -66,7 +68,7 @@ static void * request_thread_start(void *arg) {
|
|||
NULL,
|
||||
sync_callback_func);
|
||||
|
||||
event_base_dispatch(event_base);
|
||||
event_base_dispatch(req_data->context->event_base_sync);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -82,8 +84,8 @@ getdns_general_sync(
|
|||
)
|
||||
{
|
||||
/* we will cheat and spawn a thread */
|
||||
/* get the old event base */
|
||||
struct event_base* orig_base = context->event_base;
|
||||
/* set up for sync resolution */
|
||||
|
||||
pthread_t thread;
|
||||
pthread_attr_t attr;
|
||||
sync_request_data req_data = {
|
||||
|
@ -111,9 +113,6 @@ getdns_general_sync(
|
|||
return GETDNS_RETURN_GENERIC_ERROR;
|
||||
}
|
||||
|
||||
/* set the old event loop */
|
||||
getdns_extension_set_libevent_base(context, orig_base);
|
||||
|
||||
return req_data.response_status;
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include <string.h>
|
||||
#include "testmessages.h"
|
||||
#include <getdns/getdns.h>
|
||||
#include <event2/event.h>
|
||||
|
||||
/* Set up the callback function, which will also do the processing of the results */
|
||||
void this_callbackfn(struct getdns_context_t *this_context,
|
||||
|
@ -41,14 +42,10 @@ void this_callbackfn(struct getdns_context_t *this_context,
|
|||
{
|
||||
if (this_callback_type == GETDNS_CALLBACK_COMPLETE) /* This is a callback with data */
|
||||
{
|
||||
getdns_bindata* bindata = NULL;
|
||||
getdns_dict_get_bindata(this_response, "pkt", &bindata);
|
||||
if (bindata) {
|
||||
char* data = (char*) bindata->data;
|
||||
data[bindata->size] = 0;
|
||||
memcpy(data, bindata->data, bindata->size);
|
||||
fprintf(stdout, "The packet %s\n", data);
|
||||
}
|
||||
char* res = getdns_pretty_print_dict(this_response);
|
||||
fprintf(stdout, "%s", res);
|
||||
getdns_dict_destroy(this_response);
|
||||
|
||||
}
|
||||
else if (this_callback_type == GETDNS_CALLBACK_CANCEL)
|
||||
fprintf(stderr, "The callback with ID %lld was cancelled. Exiting.", this_transaction_id);
|
||||
|
@ -90,13 +87,13 @@ main()
|
|||
fprintf(stderr, "A bad domain name was used: %s. Exiting.", this_name);
|
||||
return(GETDNS_RETURN_GENERIC_ERROR);
|
||||
}
|
||||
dns_request_return = getdns_service(this_context, this_name, NULL, this_userarg, &this_transaction_id,
|
||||
this_callbackfn);
|
||||
if (dns_request_return == GETDNS_RETURN_BAD_DOMAIN_NAME)
|
||||
{
|
||||
fprintf(stderr, "A bad domain name was used: %s. Exiting.", this_name);
|
||||
return(GETDNS_RETURN_GENERIC_ERROR);
|
||||
}
|
||||
// dns_request_return = getdns_service(this_context, this_name, NULL, this_userarg, &this_transaction_id,
|
||||
// this_callbackfn);
|
||||
// if (dns_request_return == GETDNS_RETURN_BAD_DOMAIN_NAME)
|
||||
// {
|
||||
// fprintf(stderr, "A bad domain name was used: %s. Exiting.", this_name);
|
||||
// return(GETDNS_RETURN_GENERIC_ERROR);
|
||||
// }
|
||||
else
|
||||
{
|
||||
/* Call the event loop */
|
||||
|
|
|
@ -36,75 +36,72 @@
|
|||
|
||||
/* declarations */
|
||||
struct getdns_dns_req;
|
||||
struct getdns_nameserver;
|
||||
struct getdns_network_req;
|
||||
struct ub_ctx;
|
||||
|
||||
typedef struct getdns_nameserver {
|
||||
evutil_socket_t socket; /* a connected UDP socket */
|
||||
struct sockaddr_storage address;
|
||||
ev_socklen_t addrlen;
|
||||
typedef enum network_req_state_enum {
|
||||
NET_REQ_NOT_SENT,
|
||||
NET_REQ_IN_FLIGHT,
|
||||
NET_REQ_FINISHED,
|
||||
NET_REQ_CANCELED
|
||||
} network_req_state;
|
||||
|
||||
int failed_times; /* number of times which we have given this server a chance */
|
||||
int timedout; /* number of times in a row a request has timed out */
|
||||
|
||||
struct event* event;
|
||||
|
||||
/* when we next probe this server. */
|
||||
/* Valid if state == 0 */
|
||||
/* Outstanding probe request for this nameserver, if any */
|
||||
struct getdns_dns_req *probe_request;
|
||||
char state; /* zero if we think that this server is down */
|
||||
char choked; /* true if we have an EAGAIN from this server's socket */
|
||||
char write_waiting; /* true if we are waiting for EV_WRITE events */
|
||||
|
||||
/* getdns context */
|
||||
getdns_context_t context;
|
||||
|
||||
/* Number of currently inflight requests: used
|
||||
* to track when we should add/del the event. */
|
||||
int requests_inflight;
|
||||
} getdns_nameserver;
|
||||
|
||||
/* network request - state for a single network request and referenced
|
||||
* by the the outbound_req
|
||||
*/
|
||||
/**
|
||||
* Request data for unbound
|
||||
**/
|
||||
typedef struct getdns_network_req {
|
||||
ldns_pkt *pkt; /* the dns packet data */
|
||||
uint16_t request_type; /* query type */
|
||||
/* the async_id from unbound */
|
||||
int unbound_id;
|
||||
/* state var */
|
||||
network_req_state state;
|
||||
/* owner request (contains name) */
|
||||
struct getdns_dns_req* owner;
|
||||
|
||||
int reissue_count;
|
||||
int tx_count; /* the number of times that this packet has been sent */
|
||||
/* request type */
|
||||
uint16_t request_type;
|
||||
|
||||
/* not owned */
|
||||
getdns_nameserver *ns; /* the server which we sent to */
|
||||
/* request class */
|
||||
uint16_t request_class;
|
||||
|
||||
unsigned transmit_me :1; /* needs to be transmitted */
|
||||
|
||||
getdns_context_t context;
|
||||
|
||||
struct getdns_dns_req *owner;
|
||||
/* result */
|
||||
ldns_pkt* result;
|
||||
|
||||
/* next request to issue after this one */
|
||||
struct getdns_network_req* next;
|
||||
} getdns_network_req;
|
||||
|
||||
/* outbound request - manages recursion and stub reqs */
|
||||
/* dns request - manages a number of network requests and
|
||||
* the initial data passed to getdns_general
|
||||
*/
|
||||
typedef struct getdns_dns_req {
|
||||
|
||||
/* name */
|
||||
char *name;
|
||||
|
||||
/* canceled flag */
|
||||
int canceled;
|
||||
|
||||
/* current network request */
|
||||
struct getdns_network_req *current_req;
|
||||
|
||||
/* first request in list */
|
||||
struct getdns_network_req *first_req;
|
||||
|
||||
/* context that owns the request */
|
||||
getdns_context_t context;
|
||||
|
||||
uint16_t resolver_type;
|
||||
/* ub_ctx issuing the request */
|
||||
struct ub_ctx* unbound;
|
||||
|
||||
/* request extensions */
|
||||
getdns_dict *extensions;
|
||||
|
||||
/* callback data */
|
||||
getdns_callback_t user_callback;
|
||||
void *user_pointer;
|
||||
|
||||
getdns_transaction_t trans_id; /* the transaction id */
|
||||
|
||||
|
||||
int pending_cb; /* Waiting for its callback to be invoked; not
|
||||
* owned by event base any more. */
|
||||
|
||||
/* search not supported.. yet */
|
||||
/* the transaction id */
|
||||
getdns_transaction_t trans_id;
|
||||
|
||||
} getdns_dns_req;
|
||||
|
||||
|
@ -113,29 +110,20 @@ typedef struct getdns_dns_req {
|
|||
/* network request utilities */
|
||||
void network_req_free(getdns_network_req* net_req);
|
||||
|
||||
getdns_network_req* network_req_new(getdns_context_t context,
|
||||
const char* name,
|
||||
getdns_network_req* network_req_new(getdns_dns_req* owner,
|
||||
uint16_t request_type,
|
||||
uint16_t request_class,
|
||||
struct getdns_dict* extensions);
|
||||
|
||||
|
||||
/* dns request utils */
|
||||
getdns_dns_req* dns_req_new(getdns_context_t context,
|
||||
struct ub_ctx* unbound,
|
||||
const char* name,
|
||||
uint16_t request_type,
|
||||
struct getdns_dict *extensions,
|
||||
getdns_transaction_t *transaction_id);
|
||||
struct getdns_dict *extensions);
|
||||
|
||||
|
||||
void dns_req_free(getdns_dns_req* req);
|
||||
|
||||
/* nameserver utils */
|
||||
getdns_nameserver* nameserver_new_from_ip_dict(getdns_context_t context,
|
||||
getdns_dict* ip_dict);
|
||||
|
||||
void nameserver_free(getdns_nameserver* nameserver);
|
||||
|
||||
getdns_dict* nameserver_to_dict(getdns_nameserver* nameserver);
|
||||
|
||||
|
||||
#endif
|
||||
|
|
|
@ -29,10 +29,14 @@
|
|||
*/
|
||||
|
||||
#include "util-internal.h"
|
||||
#include "types-internal.h"
|
||||
|
||||
getdns_return_t getdns_dict_util_set_string(getdns_dict* dict, char* name,
|
||||
char* value) {
|
||||
const char* value) {
|
||||
/* account for the null term */
|
||||
if (value == NULL) {
|
||||
return GETDNS_RETURN_WRONG_TYPE_REQUESTED;
|
||||
}
|
||||
getdns_bindata type_bin = { strlen(value) + 1, (uint8_t*) value };
|
||||
return getdns_dict_set_bindata(dict, name, &type_bin);
|
||||
}
|
||||
|
@ -113,35 +117,336 @@ getdns_return_t sockaddr_to_dict(struct sockaddr_storage* address, getdns_dict**
|
|||
return GETDNS_RETURN_GOOD;
|
||||
}
|
||||
|
||||
/* TODO: flags */
|
||||
ldns_pkt *create_new_pkt(getdns_context_t context,
|
||||
const char* name,
|
||||
uint16_t request_type,
|
||||
struct getdns_dict* extensions) {
|
||||
ldns_pkt *pkt = NULL;
|
||||
ldns_rr_type type = (ldns_rr_type) request_type;
|
||||
uint16_t flags = 0;
|
||||
if (context->resolution_type == GETDNS_CONTEXT_STUB) {
|
||||
flags |= LDNS_RD;
|
||||
/* result must be freed */
|
||||
static char* convert_rdf_to_str(ldns_rdf* rdf) {
|
||||
if (ldns_rdf_get_type(rdf) == LDNS_RDF_TYPE_DNAME) {
|
||||
ldns_dname2canonical(rdf);
|
||||
}
|
||||
ldns_pkt_query_new_frm_str(&pkt, name,
|
||||
type,
|
||||
LDNS_RR_CLASS_IN, flags);
|
||||
if (pkt) {
|
||||
/* id */
|
||||
ldns_pkt_set_id(pkt, ldns_get_random());
|
||||
}
|
||||
return pkt;
|
||||
return ldns_rdf2str(rdf);
|
||||
}
|
||||
|
||||
getdns_dict *create_getdns_response(ldns_pkt* pkt) {
|
||||
char* data = ldns_pkt2str(pkt);
|
||||
/* create the header dict */
|
||||
static getdns_dict *create_reply_header_dict(ldns_pkt* reply) {
|
||||
/* { "id": 23456, "qr": 1, "opcode": 0, ... }, */
|
||||
int r = 0;
|
||||
getdns_dict* result = getdns_dict_create();
|
||||
getdns_bindata bindata = {
|
||||
strlen(data), (uint8_t*) data
|
||||
};
|
||||
getdns_dict_set_bindata(result, "pkt", &bindata);
|
||||
free(data);
|
||||
if (!result) {
|
||||
return NULL;
|
||||
}
|
||||
/* cheat since we know GETDNS_RETURN_GOOD == 0 */
|
||||
r |= getdns_dict_set_int(result, GETDNS_STR_KEY_ID, ldns_pkt_id(reply));
|
||||
r |= getdns_dict_set_int(result, GETDNS_STR_KEY_QR, ldns_pkt_qr(reply));
|
||||
r |= getdns_dict_set_int(result, GETDNS_STR_KEY_OPC, (int) ldns_pkt_get_opcode(reply));
|
||||
|
||||
if (r != 0) {
|
||||
getdns_dict_destroy(result);
|
||||
result = NULL;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static getdns_dict *create_reply_question_dict(ldns_pkt* reply) {
|
||||
/* { "qname": <bindata for "www.example.com">, "qtype": 1, "qclass": 1 } */
|
||||
int r = 0;
|
||||
ldns_rr *question = NULL;
|
||||
char* qname;
|
||||
getdns_dict* result = getdns_dict_create();
|
||||
if (!result) {
|
||||
return NULL;
|
||||
}
|
||||
question = ldns_rr_list_rr(ldns_pkt_question(reply), 0);
|
||||
r |= getdns_dict_set_int(result, GETDNS_STR_KEY_QTYPE, (int) ldns_rr_get_type(question));
|
||||
r |= getdns_dict_set_int(result, GETDNS_STR_KEY_QCLASS, (int) ldns_rr_get_class(question));
|
||||
qname = convert_rdf_to_str(ldns_rr_owner(question));
|
||||
if (qname) {
|
||||
r |= getdns_dict_util_set_string(result, GETDNS_STR_KEY_QNAME, qname);
|
||||
free(qname);
|
||||
} else {
|
||||
r = 1;
|
||||
}
|
||||
if (r != 0) {
|
||||
getdns_dict_destroy(result);
|
||||
result = NULL;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static getdns_dict *create_dict_from_rdf(ldns_rdf* rdf) {
|
||||
/*
|
||||
create a dict w/ rdata_raw and special fields if needed
|
||||
i.e.
|
||||
{
|
||||
"ipv4_address": <bindata of 0x0a0b0c01>
|
||||
"rdata_raw": <bindata of 0x0a0b0c01>
|
||||
}
|
||||
*/
|
||||
int r = 0;
|
||||
getdns_bindata rbin = { ldns_rdf_size(rdf), ldns_rdf_data(rdf) };
|
||||
getdns_dict* result = getdns_dict_create();
|
||||
r |= getdns_dict_set_bindata(result, GETDNS_STR_KEY_RDATA_RAW, &rbin);
|
||||
if (ldns_rdf_get_type(rdf) == LDNS_RDF_TYPE_AAAA) {
|
||||
r |= getdns_dict_set_bindata(result, GETDNS_STR_KEY_V6_ADDR, &rbin);
|
||||
} else if (ldns_rdf_get_type(rdf) == LDNS_RDF_TYPE_A) {
|
||||
r |= getdns_dict_set_bindata(result, GETDNS_STR_KEY_V4_ADDR, &rbin);
|
||||
}
|
||||
if (r != 0) {
|
||||
getdns_dict_destroy(result);
|
||||
result = NULL;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static getdns_dict *create_dict_from_rr(ldns_rr* rr) {
|
||||
/*
|
||||
{
|
||||
"name": <bindata for "www.example.com">,
|
||||
"type": 1,
|
||||
"class": 1,
|
||||
"ttl": 33000,
|
||||
"rdata":
|
||||
{
|
||||
"ipv4_address": <bindata of 0x0a0b0c01>
|
||||
"rdata_raw": <bindata of 0x0a0b0c01>
|
||||
}
|
||||
}
|
||||
*/
|
||||
int r = 0;
|
||||
char * name = NULL;
|
||||
getdns_dict *result = getdns_dict_create();
|
||||
size_t rd_count = ldns_rr_rd_count(rr);
|
||||
ldns_rdf* owner = ldns_rr_owner(rr);
|
||||
r |= getdns_dict_set_int(result, GETDNS_STR_KEY_TYPE, (int) ldns_rr_get_type(rr));
|
||||
r |= getdns_dict_set_int(result, GETDNS_STR_KEY_CLASS, (int) ldns_rr_get_class(rr));
|
||||
r |= getdns_dict_set_int(result, GETDNS_STR_KEY_TTL, ldns_rr_ttl(rr));
|
||||
if (owner) {
|
||||
name = convert_rdf_to_str(owner);
|
||||
if (name) {
|
||||
r |= getdns_dict_util_set_string(result, GETDNS_STR_KEY_NAME, name);
|
||||
free(name);
|
||||
} else {
|
||||
r = 1;
|
||||
}
|
||||
}
|
||||
/* create rdatas */
|
||||
if (rd_count >= 1) {
|
||||
getdns_dict* rdata = create_dict_from_rdf(ldns_rr_rdf(rr, 0));
|
||||
r |= getdns_dict_set_dict(result, GETDNS_STR_KEY_RDATA, rdata);
|
||||
}
|
||||
/* TODO - if more than one, is rdata a list? */
|
||||
|
||||
if (r != 0) {
|
||||
getdns_dict_destroy(result);
|
||||
result = NULL;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* helper to convert an rr_list to getdns_list.
|
||||
returns a list of objects where each object
|
||||
is a result from create_dict_from_rr */
|
||||
static getdns_list *create_list_from_rr_list(ldns_rr_list* rr_list) {
|
||||
size_t i = 0;
|
||||
size_t idx = 0;
|
||||
int r = 0;
|
||||
getdns_list *result = getdns_list_create();
|
||||
for (i = 0; i < ldns_rr_list_rr_count(rr_list); ++i) {
|
||||
ldns_rr *rr = ldns_rr_list_rr(rr_list, i);
|
||||
r |= getdns_list_add_item(result, &idx);
|
||||
r |= getdns_list_set_dict(result, idx, create_dict_from_rr(rr));
|
||||
}
|
||||
if (r != 0) {
|
||||
getdns_list_destroy(result);
|
||||
result = NULL;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* helper to add the ipv4 or ipv6 bin data to the list of addrs */
|
||||
static getdns_return_t add_only_addresses(getdns_list* addrs, ldns_rr_list* rr_list) {
|
||||
int r = 0;
|
||||
size_t i = 0;
|
||||
for (i = 0; i < ldns_rr_list_rr_count(rr_list); ++i) {
|
||||
ldns_rr *rr = ldns_rr_list_rr(rr_list, i);
|
||||
size_t j = 0;
|
||||
size_t rd_count = ldns_rr_rd_count(rr);
|
||||
for (j = 0; j < rd_count; ++j) {
|
||||
size_t item_idx = 0;
|
||||
ldns_rdf* rdf = ldns_rr_rdf(rr, j);
|
||||
getdns_bindata rbin = { ldns_rdf_size(rdf), ldns_rdf_data(rdf) };
|
||||
r |= getdns_list_add_item(addrs, &item_idx);
|
||||
r |= getdns_list_set_bindata(addrs, item_idx, &rbin);
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
static getdns_dict *create_reply_dict(getdns_network_req* req,
|
||||
getdns_list *just_addrs) {
|
||||
/* turn a packet into this glorious structure
|
||||
|
||||
{ # This is the first reply
|
||||
"header": { "id": 23456, "qr": 1, "opcode": 0, ... },
|
||||
"question": { "qname": <bindata for "www.example.com">, "qtype": 1, "qclass": 1 },
|
||||
"answer":
|
||||
[
|
||||
{
|
||||
"name": <bindata for "www.example.com">,
|
||||
"type": 1,
|
||||
"class": 1,
|
||||
"ttl": 33000,
|
||||
"rdata":
|
||||
{
|
||||
"ipv4_address": <bindata of 0x0a0b0c01>
|
||||
"rdata_raw": <bindata of 0x0a0b0c01>
|
||||
}
|
||||
}
|
||||
],
|
||||
"authority":
|
||||
[
|
||||
{
|
||||
"name": <bindata for "ns1.example.com">,
|
||||
"type": 1,
|
||||
"class": 1,
|
||||
"ttl": 600,
|
||||
"rdata":
|
||||
{
|
||||
"ipv4_address": <bindata of 0x65439876>
|
||||
"rdata_raw": <bindata of 0x65439876>
|
||||
}
|
||||
}
|
||||
]
|
||||
"additional": [],
|
||||
"canonical_name": <bindata for "www.example.com">,
|
||||
"answer_type": GETDNS_NAMETYPE_DNS
|
||||
}
|
||||
|
||||
*/
|
||||
int r = 0;
|
||||
ldns_pkt *reply = req->result;
|
||||
ldns_rr_list *rr_list = NULL;
|
||||
ldns_rr* question = NULL;
|
||||
getdns_dict *subdict = NULL;
|
||||
getdns_list *sublist = NULL;
|
||||
char* name = NULL;
|
||||
|
||||
getdns_dict *result = getdns_dict_create();
|
||||
if (!result) {
|
||||
return NULL;
|
||||
}
|
||||
/* header */
|
||||
subdict = create_reply_header_dict(reply);
|
||||
r |= getdns_dict_set_dict(result, GETDNS_STR_KEY_HEADER, subdict);
|
||||
getdns_dict_destroy(subdict);
|
||||
|
||||
/* question */
|
||||
subdict = create_reply_question_dict(reply);
|
||||
r |= getdns_dict_set_dict(result, GETDNS_STR_KEY_QUESTION, subdict);
|
||||
getdns_dict_destroy(subdict);
|
||||
|
||||
/* answers */
|
||||
rr_list = ldns_pkt_answer(reply);
|
||||
sublist = create_list_from_rr_list(rr_list);
|
||||
r |= getdns_dict_set_list(result, GETDNS_STR_KEY_ANSWER, sublist);
|
||||
getdns_list_destroy(sublist);
|
||||
if ((req->request_type == GETDNS_RRTYPE_A ||
|
||||
req->request_type == GETDNS_RRTYPE_AAAA) &&
|
||||
just_addrs != NULL) {
|
||||
/* add to just addrs */
|
||||
r |= add_only_addresses(just_addrs, rr_list);
|
||||
}
|
||||
|
||||
/* authority */
|
||||
rr_list = ldns_pkt_authority(reply);
|
||||
sublist = create_list_from_rr_list(rr_list);
|
||||
r |= getdns_dict_set_list(result, GETDNS_STR_KEY_AUTHORITY, sublist);
|
||||
getdns_list_destroy(sublist);
|
||||
|
||||
/* additional */
|
||||
rr_list = ldns_pkt_additional(reply);
|
||||
sublist = create_list_from_rr_list(rr_list);
|
||||
r |= getdns_dict_set_list(result, GETDNS_STR_KEY_ADDITIONAL, sublist);
|
||||
getdns_list_destroy(sublist);
|
||||
|
||||
/* other stuff */
|
||||
r |= getdns_dict_set_int(result, GETDNS_STR_KEY_ANSWER_TYPE, GETDNS_NAMETYPE_DNS);
|
||||
question = ldns_rr_list_rr(ldns_pkt_question(reply), 0);
|
||||
name = convert_rdf_to_str(ldns_rr_owner(question));
|
||||
if (name) {
|
||||
r |= getdns_dict_util_set_string(result, GETDNS_STR_KEY_CANONICAL_NM, name);
|
||||
free(name);
|
||||
} else {
|
||||
r |= 1;
|
||||
}
|
||||
if (r != 0) {
|
||||
getdns_dict_destroy(result);
|
||||
result = NULL;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static char* get_canonical_name(const char* name) {
|
||||
ldns_rdf* rdf = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_DNAME, name);
|
||||
if (!rdf) {
|
||||
return NULL;
|
||||
}
|
||||
char* result = convert_rdf_to_str(rdf);
|
||||
ldns_rdf_free(rdf);
|
||||
return result;
|
||||
}
|
||||
|
||||
getdns_dict *create_getdns_response(struct getdns_dns_req* completed_request) {
|
||||
getdns_dict* result = getdns_dict_create();
|
||||
getdns_list* replies_full = getdns_list_create();
|
||||
getdns_list* just_addrs = NULL;
|
||||
getdns_list* replies_tree = getdns_list_create();
|
||||
getdns_network_req *netreq = completed_request->first_req;
|
||||
|
||||
|
||||
int r = 0;
|
||||
|
||||
if (completed_request->first_req->request_class == GETDNS_RRTYPE_A ||
|
||||
completed_request->first_req->request_class == GETDNS_RRTYPE_AAAA) {
|
||||
just_addrs = getdns_list_create();
|
||||
}
|
||||
|
||||
r |= getdns_dict_set_int(result, GETDNS_STR_KEY_STATUS, GETDNS_RESPSTATUS_GOOD);
|
||||
r |= getdns_dict_util_set_string(result, GETDNS_STR_KEY_CANONICAL_NM, get_canonical_name(completed_request->name));
|
||||
r |= getdns_dict_set_int(result, GETDNS_STR_KEY_ANSWER_TYPE, GETDNS_NAMETYPE_DNS);
|
||||
|
||||
while (netreq) {
|
||||
getdns_bindata full_data;
|
||||
full_data.data = NULL;
|
||||
full_data.size = 0;
|
||||
ldns_pkt *pkt = netreq->result;
|
||||
ldns_status s = ldns_pkt2wire(&(full_data.data), pkt, &(full_data.size));
|
||||
size_t idx = 0;
|
||||
/* reply tree */
|
||||
getdns_dict *reply = create_reply_dict(netreq, just_addrs);
|
||||
r |= getdns_list_add_item(replies_tree, &idx);
|
||||
r |= getdns_list_set_dict(replies_tree, idx, reply);
|
||||
/* buffer */
|
||||
if (s == LDNS_STATUS_OK) {
|
||||
r |= getdns_list_add_item(replies_full, &idx);
|
||||
r |= getdns_list_set_bindata(replies_full, idx, &full_data);
|
||||
free(full_data.data);
|
||||
} else {
|
||||
r = 1;
|
||||
break;
|
||||
}
|
||||
netreq = netreq->next;
|
||||
}
|
||||
|
||||
r |= getdns_dict_set_list(result, GETDNS_STR_KEY_REPLIES_TREE, replies_tree);
|
||||
r |= getdns_dict_set_list(result, GETDNS_STR_KEY_REPLIES_FULL, replies_full);
|
||||
if (just_addrs) {
|
||||
r |= getdns_dict_set_list(result, GETDNS_STR_KEY_JUST_ADDRS, just_addrs);
|
||||
}
|
||||
|
||||
if (r != 0) {
|
||||
getdns_dict_destroy(result);
|
||||
result = NULL;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -28,24 +28,25 @@
|
|||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
#include "types-internal.h"
|
||||
*/
|
||||
|
||||
#include <ldns/ldns.h>
|
||||
#include "context.h"
|
||||
|
||||
struct getdns_dns_req;
|
||||
|
||||
/* convert an ip address dict to a sock storage */
|
||||
getdns_return_t dict_to_sockaddr(getdns_dict* ns, struct sockaddr_storage* output);
|
||||
getdns_return_t sockaddr_to_dict(struct sockaddr_storage* sockaddr, getdns_dict** output);
|
||||
|
||||
/* create a dns packet for the given request type and extensions */
|
||||
ldns_pkt *create_new_pkt(getdns_context_t context,
|
||||
const char* name,
|
||||
uint16_t request_type,
|
||||
struct getdns_dict* extensions);
|
||||
|
||||
getdns_dict *create_getdns_response(ldns_pkt* pkt);
|
||||
getdns_dict *create_getdns_response(struct getdns_dns_req* completed_request);
|
||||
|
||||
/* dict util */
|
||||
/* set a string as bindata */
|
||||
getdns_return_t getdns_dict_util_set_string(getdns_dict* dict, char* name,
|
||||
char* value);
|
||||
const char* value);
|
||||
|
||||
/* get a string from a dict. result is valid as long as dict is valid */
|
||||
getdns_return_t getdns_dict_util_get_string(getdns_dict* dict, char* name,
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
Index: Makefile.in
|
||||
===================================================================
|
||||
--- Makefile.in (revision 2985)
|
||||
+++ Makefile.in (working copy)
|
||||
@@ -280,7 +280,7 @@
|
||||
longtest: tests
|
||||
if test -x "`which bash`"; then bash testcode/do-tests.sh; else sh testcode/do-tests.sh; fi
|
||||
|
||||
-lib: libunbound.la unbound.h
|
||||
+lib: libunbound.la unbound.h unbound-event.h
|
||||
|
||||
libunbound.la: $(LIBUNBOUND_OBJ_LINK)
|
||||
$(LINK_LIB) $(UBSYMS) -o $@ $(LIBUNBOUND_OBJ_LINK) -rpath $(libdir) -lssl $(LIBS)
|
||||
@@ -348,6 +348,9 @@
|
||||
unbound.h: $(srcdir)/libunbound/unbound.h
|
||||
sed -e 's/@''UNBOUND_VERSION_MAJOR@/$(UNBOUND_VERSION_MAJOR)/' -e 's/@''UNBOUND_VERSION_MINOR@/$(UNBOUND_VERSION_MINOR)/' -e 's/@''UNBOUND_VERSION_MICRO@/$(UNBOUND_VERSION_MICRO)/' < $(srcdir)/libunbound/unbound.h > $@
|
||||
|
||||
+unbound-event.h: $(srcdir)/libunbound/unbound-event.h
|
||||
+ sed -e 's/@''UNBOUND_VERSION_MAJOR@/$(UNBOUND_VERSION_MAJOR)/' -e 's/@''UNBOUND_VERSION_MINOR@/$(UNBOUND_VERSION_MINOR)/' -e 's/@''UNBOUND_VERSION_MICRO@/$(UNBOUND_VERSION_MICRO)/' < $(srcdir)/libunbound/unbound-event.h > $@
|
||||
+
|
||||
unbound-control-setup: $(srcdir)/smallapp/unbound-control-setup.sh
|
||||
sed -e 's:^DESTDIR=.*$$:DESTDIR=$(UNBOUND_RUN_DIR):' < $(srcdir)/smallapp/unbound-control-setup.sh > $@
|
||||
-chmod +x $@
|
||||
@@ -496,6 +499,7 @@
|
||||
$(INSTALL) -c -m 755 unbound-control-setup $(DESTDIR)$(sbindir)/unbound-control-setup
|
||||
if test ! -e $(DESTDIR)$(configfile); then $(INSTALL) -d `dirname $(DESTDIR)$(configfile)`; $(INSTALL) -c -m 644 doc/example.conf $(DESTDIR)$(configfile); fi
|
||||
$(LIBTOOL) --mode=install cp unbound.h $(DESTDIR)$(includedir)/unbound.h
|
||||
+ $(LIBTOOL) --mode=install cp unbound-event.h $(DESTDIR)$(includedir)/unbound-event.h
|
||||
$(LIBTOOL) --mode=install cp libunbound.la $(DESTDIR)$(libdir)
|
||||
$(LIBTOOL) --mode=finish $(DESTDIR)$(libdir)
|
||||
|
||||
Index: libunbound/ubsyms.def
|
||||
===================================================================
|
||||
--- libunbound/ubsyms.def (revision 2985)
|
||||
+++ libunbound/ubsyms.def (working copy)
|
||||
@@ -29,3 +29,4 @@
|
||||
ub_ctx_data_add
|
||||
ub_ctx_data_remove
|
||||
ub_version
|
||||
+ub_ctx_set_event
|
||||
Index: libunbound/unbound-event.h
|
||||
===================================================================
|
||||
--- libunbound/unbound-event.h (revision 2985)
|
||||
+++ libunbound/unbound-event.h (working copy)
|
||||
@@ -63,9 +63,9 @@
|
||||
struct ub_ctx;
|
||||
struct ub_result;
|
||||
struct event_base;
|
||||
-struct ldns_buffer;
|
||||
+struct ldns_struct_buffer;
|
||||
|
||||
-typedef void (*ub_event_callback_t)(void*, int, struct ldns_buffer*, int, char*);
|
||||
+typedef void (*ub_event_callback_t)(void*, int, struct ldns_struct_buffer*, int, char*);
|
||||
|
||||
/**
|
||||
* Create a resolving and validation context.
|
||||
@@ -82,6 +82,15 @@
|
||||
struct ub_ctx* ub_ctx_create_event(struct event_base* base);
|
||||
|
||||
/**
|
||||
+ * Set a new event_base on a context created with ub_ctx_create_event.
|
||||
+ * Any outbound queries will be canceled.
|
||||
+ * @param ctx the ub_ctx to update. Must have been created with ub_ctx_create_event
|
||||
+ * @param base the new event_base to attach to the ctx
|
||||
+ * @return 0 if OK, else error
|
||||
+ */
|
||||
+int ub_ctx_set_event(struct ub_ctx* ctx, struct event_base* base);
|
||||
+
|
||||
+/**
|
||||
* Perform resolution and validation of the target name.
|
||||
* Asynchronous, after a while, the callback will be called with your
|
||||
* data and the result. Uses the event_base user installed by creating the
|
||||
Index: libunbound/libunbound.c
|
||||
===================================================================
|
||||
--- libunbound/libunbound.c (revision 2985)
|
||||
+++ libunbound/libunbound.c (working copy)
|
||||
@@ -656,15 +656,14 @@
|
||||
return r;
|
||||
}
|
||||
}
|
||||
+ lock_basic_unlock(&ctx->cfglock);
|
||||
if(!ctx->event_worker) {
|
||||
ctx->event_worker = libworker_create_event(ctx,
|
||||
ctx->event_base);
|
||||
if(!ctx->event_worker) {
|
||||
- lock_basic_unlock(&ctx->cfglock);
|
||||
return UB_INITFAIL;
|
||||
}
|
||||
}
|
||||
- lock_basic_unlock(&ctx->cfglock);
|
||||
|
||||
/* create new ctx_query and attempt to add to the list */
|
||||
q = context_new(ctx, name, rrtype, rrclass, (ub_callback_t)callback,
|
||||
@@ -1212,3 +1211,24 @@
|
||||
{
|
||||
return PACKAGE_VERSION;
|
||||
}
|
||||
+
|
||||
+int
|
||||
+ub_ctx_set_event(struct ub_ctx* ctx, struct event_base* base) {
|
||||
+ if (!ctx || !ctx->event_base || !base) {
|
||||
+ return UB_INITFAIL;
|
||||
+ }
|
||||
+ if (ctx->event_base == base) {
|
||||
+ /* already set */
|
||||
+ return UB_NOERROR;
|
||||
+ }
|
||||
+
|
||||
+ lock_basic_lock(&ctx->cfglock);
|
||||
+ /* destroy the current worker - safe to pass in NULL */
|
||||
+ libworker_delete_event(ctx->event_worker);
|
||||
+ ctx->event_worker = NULL;
|
||||
+ ctx->event_base = base;
|
||||
+ ctx->created_bg = 0;
|
||||
+ ctx->dothread = 1;
|
||||
+ lock_basic_unlock(&ctx->cfglock);
|
||||
+ return UB_NOERROR;
|
||||
+}
|
Loading…
Reference in New Issue