diff --git a/README.md b/README.md index dacf291a..65d68676 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/configure.ac b/configure.ac index 798fa3a1..938fd88e 100644 --- a/configure.ac +++ b/configure.ac @@ -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]) diff --git a/src/Makefile.in b/src/Makefile.in index 19500e9c..b5abc5ce 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -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 @@ -28,13 +27,13 @@ CFLAGS=-Wall -g -fPIC -I$(srcdir)/ -I /usr/local/include -std=c99 default: all -all: libgetdns +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 diff --git a/src/context.c b/src/context.c index 55492176..6021d54b 100644 --- a/src/context.c +++ b/src/context.c @@ -1,7 +1,7 @@ /** * * /brief getdns contect management functions - * + * * This is the meat of the API * Originally taken from the getdns API description pseudo implementation. * @@ -15,10 +15,10 @@ * 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 @@ -29,9 +29,16 @@ */ #include +#include +#include #include "context.h" #include "util-internal.h" +#include "types-internal.h" #include +#include +#include +#include +#include /* 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; } @@ -87,7 +123,7 @@ static struct getdns_list* create_from_ldns_list(ldns_rdf** ldns_list, size_t co getdns_dict_destroy(ipaddr); } break; - + case LDNS_RDF_TYPE_DNAME: { getdns_bindata item; @@ -99,7 +135,7 @@ static struct getdns_list* create_from_ldns_list(ldns_rdf** ldns_list, size_t co free(srch); } break; - + default: break; } @@ -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,40 +199,43 @@ getdns_return_t getdns_context_create( return GETDNS_RETURN_GENERIC_ERROR; } - getdns_dict *outbound_reqs = getdns_dict_create(); - if (outbound_reqs == NULL) { - return GETDNS_RETURN_GENERIC_ERROR; - } - /** default init **/ result = malloc(sizeof(struct getdns_context_t)); - 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(); - result->append_name = GETDNS_CONTEXT_APPEND_NAME_ALWAYS; - 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; + if (!result) { + return GETDNS_RETURN_GENERIC_ERROR; + } 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->timeout = 5000; + result->follow_redirects = GETDNS_CONTEXT_FOLLOW_REDIRECTS; + result->dns_root_servers = create_default_root_servers(); + result->append_name = GETDNS_CONTEXT_APPEND_NAME_ALWAYS; + result->suffix = NULL; + + result->dnssec_trust_anchors = NULL; + result->upstream_list = NULL; + + result->edns_extended_rcode = 0; + result->edns_version = 0; + result->edns_do_bit = 0; + 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 */ @@ -210,14 +275,14 @@ getdns_context_destroy( getdns_list_destroy(context->suffix); getdns_list_destroy(context->dnssec_trust_anchors); getdns_list_destroy(context->upstream_list); - - getdns_dict_destroy(context->outbound_reqs); - /* TODO: cancel all events */ + /* destroy the ub context */ + ub_ctx_delete(context->unbound_async); + ub_ctx_delete(context->unbound_sync); - if (context->resolver_socket != 0) { - evutil_closesocket(context->resolver_socket); - } + event_base_free(context->event_base_sync); + + ldns_rbtree_free(context->outbound_requests); free(context); return; @@ -237,9 +302,33 @@ 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 - * + * */ getdns_return_t getdns_context_set_context_update( @@ -272,15 +361,15 @@ getdns_context_set_resolution_type( uint16_t value ) { - if (value != GETDNS_CONTEXT_STUB && + if (value != GETDNS_CONTEXT_STUB && value != GETDNS_CONTEXT_RECURSING) { return GETDNS_RETURN_CONTEXT_UPDATE_FAIL; } - + context->resolution_type = value; - + dispatch_updated(context, GETDNS_CONTEXT_CODE_RESOLUTION_TYPE); - + return GETDNS_RETURN_GOOD; } /* getdns_context_set_resolution_type */ @@ -299,17 +388,17 @@ getdns_context_set_namespaces( if (namespace_count == 0 || namespaces == NULL) { return GETDNS_RETURN_CONTEXT_UPDATE_FAIL; } - + /** clean up old namespaces **/ context->memory_deallocator(context->namespaces); - + /** duplicate **/ namespaces_size = namespace_count * sizeof(uint16_t); context->namespaces = context->memory_allocator(namespaces_size); memcpy(context->namespaces, namespaces, namespaces_size); - + dispatch_updated(context, GETDNS_CONTEXT_CODE_NAMESPACES); - + return GETDNS_RETURN_GOOD; } /* getdns_context_set_namespaces */ @@ -323,14 +412,24 @@ 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) { - return GETDNS_RETURN_CONTEXT_UPDATE_FAIL; + + 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); @@ -347,10 +446,11 @@ 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); - + return GETDNS_RETURN_GOOD; } /* getdns_context_set_limit_outstanding_queries */ @@ -383,8 +483,9 @@ 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; } /* getdns_context_set_follow_redirects */ @@ -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,8 +583,10 @@ 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; } /* getdns_context_set_suffix */ @@ -484,7 +611,7 @@ getdns_context_set_dnssec_trust_anchors( context->dnssec_trust_anchors = value; dispatch_updated(context, GETDNS_CONTEXT_CODE_DNSSEC_TRUST_ANCHORS); - + return GETDNS_RETURN_GOOD; } /* getdns_context_set_dnssec_trust_anchors */ @@ -498,10 +625,10 @@ 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; } /* getdns_context_set_dnssec_allowed_skew */ @@ -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,14 +653,28 @@ 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; + } + } + + if (r != GETDNS_RETURN_GOOD) { + getdns_list_destroy(upstream_list); + return GETDNS_RETURN_CONTEXT_UPDATE_FAIL; + } - getdns_context_set_resolution_type(context, GETDNS_CONTEXT_STUB); - 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; } /* getdns_context_set_stub_resolution */ @@ -549,10 +693,11 @@ 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); - + return GETDNS_RETURN_GOOD; } /* getdns_context_set_edns_maximum_udp_payload_size */ @@ -569,7 +714,7 @@ getdns_context_set_edns_extended_rcode( context->edns_extended_rcode = value; dispatch_updated(context, GETDNS_CONTEXT_CODE_EDNS_EXTENDED_RCODE); - + return GETDNS_RETURN_GOOD; } /* getdns_context_set_edns_extended_rcode */ @@ -586,7 +731,7 @@ getdns_context_set_edns_version( context->edns_version = value; dispatch_updated(context, GETDNS_CONTEXT_CODE_EDNS_VERSION); - + return GETDNS_RETURN_GOOD; } /* getdns_context_set_edns_version */ @@ -608,7 +753,7 @@ getdns_context_set_edns_do_bit( context->edns_do_bit = value; dispatch_updated(context, GETDNS_CONTEXT_CODE_EDNS_DO_BIT); - + return GETDNS_RETURN_GOOD; } /* getdns_context_set_edns_do_bit */ @@ -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 */ diff --git a/src/context.h b/src/context.h index e9c513b5..f9278896 100644 --- a/src/context.h +++ b/src/context.h @@ -32,6 +32,11 @@ #include +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,35 +48,55 @@ 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; - + getdns_update_callback update_callback; getdns_memory_allocator memory_allocator; getdns_memory_deallocator memory_deallocator; getdns_memory_reallocator memory_reallocator; - /* Event loop */ - struct event_base* event_base; - - /* outbound request dict (transaction -> req struct) */ - getdns_dict *outbound_reqs; + /* Event loop for sync requests */ + struct event_base* event_base_sync; - /* socket */ - evutil_socket_t resolver_socket; + /* 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; + + /* 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 diff --git a/src/convert.c b/src/convert.c index e3182b56..2e73e301 100644 --- a/src/convert.c +++ b/src/convert.c @@ -1,7 +1,7 @@ /** * * /brief getdns core functions - * + * * This is the meat of the API * Originally taken from the getdns API description pseudo implementation. * @@ -15,10 +15,10 @@ * 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 @@ -30,6 +30,8 @@ #include #include +#include +#include /* 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) diff --git a/src/example/Makefile.in b/src/example/Makefile.in index 1a5c2cf2..2bbf3ad6 100644 --- a/src/example/Makefile.in +++ b/src/example/Makefile.in @@ -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 diff --git a/src/example/example_simple_answers.c b/src/example/example_simple_answers.c index a4c038ef..0d78d7f0 100644 --- a/src/example/example_simple_answers.c +++ b/src/example/example_simple_answers.c @@ -32,6 +32,7 @@ #include #include #include +#include #define UNUSED_PARAM(x) ((void)(x)) diff --git a/src/example/example_tree.c b/src/example/example_tree.c index 6769baad..e51c5755 100644 --- a/src/example/example_tree.c +++ b/src/example/example_tree.c @@ -4,6 +4,7 @@ #include #include #include +#include #define UNUSED_PARAM(x) ((void)(x)) diff --git a/src/general.c b/src/general.c index f0c3eae0..2865e6b5 100644 --- a/src/general.c +++ b/src/general.c @@ -1,7 +1,7 @@ /** * * /brief getdns core functions - * + * * This is the meat of the API * Originally taken from the getdns API description pseudo implementation. * @@ -15,10 +15,10 @@ * 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 @@ -58,138 +58,190 @@ * Version: 0.1b */ +#include +#include +#include +#include +#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, - 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); +/* 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, + 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 { + cb(context, + GETDNS_CALLBACK_ERROR, + NULL, + user_arg, + trans_id); + } + +} + +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; +} + +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 { - /* 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); + /* 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); } } - } 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; +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) { - /* 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); - + getdns_return_t gr; + int r; + + gr = getdns_context_prepare_for_resolution(context); + if (gr != GETDNS_RETURN_GOOD) { + return GETDNS_RETURN_BAD_CONTEXT; + } + + /* 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 -) -{ - /* Default to zero */ - if (transaction_id != NULL) { - *transaction_id = 0; - } - if (!context || context->event_base == NULL || - callback == NULL || - context->resolution_type != GETDNS_CONTEXT_STUB) { + 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; } - - /* 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_general_ub(context->unbound_async, + context, + name, + request_type, + extensions, + userarg, + transaction_id, + callback); - return GETDNS_RETURN_GOOD; } /* getdns_general */ @@ -197,16 +249,13 @@ getdns_general( * getdns_address * */ -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_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) { int cleanup_extensions = 0; if (!extensions) { extensions = getdns_dict_create(); @@ -216,7 +265,7 @@ getdns_address( GETDNS_STR_EXTENSION_RETURN_BOTH_V4_AND_V6, GETDNS_EXTENSION_TRUE); - getdns_return_t result = + getdns_return_t result = getdns_general(context, name, GETDNS_RRTYPE_A, extensions, userarg, transaction_id, callback); @@ -224,6 +273,6 @@ getdns_address( getdns_dict_destroy(extensions); } return result; -} +} /* getdns_general.c */ diff --git a/src/general.h b/src/general.h new file mode 100644 index 00000000..ee3bd84b --- /dev/null +++ b/src/general.h @@ -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 + +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 diff --git a/src/getdns/getdns.h b/src/getdns/getdns.h index 046c9558..8965b4b8 100644 --- a/src/getdns/getdns.h +++ b/src/getdns/getdns.h @@ -14,10 +14,10 @@ * 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 @@ -34,7 +34,8 @@ #include #include #include -#include + +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. @@ -201,7 +202,7 @@ */ /** - * \defgroup respstatus Status Codes for Responses + * \defgroup respstatus Status Codes for Responses * @{ */ #define GETDNS_RESPSTATUS_GOOD 900 @@ -216,7 +217,7 @@ */ /** - * \defgroup extvals Values Associated With Extensions + * \defgroup extvals Values Associated With Extensions * @{ */ #define GETDNS_EXTENSION_TRUE 1 @@ -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" + + + + + + /** @} */ @@ -530,8 +564,8 @@ getdns_return_t getdns_dict_get_dict(struct getdns_dict *this_dict, char *name, */ getdns_return_t getdns_dict_get_list(struct getdns_dict *this_dict, char *name, struct getdns_list **answer); /** - * fetch the bindata associated with the specified name, the bindata should not be - * free()'d by the caller + * fetch the bindata associated with the specified name, the bindata should not be + * free()'d by the caller * @param this_dict dictionary from which to fetch the bindata * @param name a name/key value to look up in the dictionary * @param **answer a copy of the bindata will be stored at this address @@ -557,7 +591,7 @@ struct getdns_list * getdns_list_create(); /** * free memory allocated to the list (also frees all children of the list) * note that lists and bindata retrieved from the list via the getdns_list_get_* - * helper functions will be destroyed as well - if you fetched them previously + * helper functions will be destroyed as well - if you fetched them previously * you MUST copy those instances BEFORE you destroy the list else * unpleasant things will happen at run-time */ @@ -574,9 +608,9 @@ void getdns_list_destroy(struct getdns_list *list); getdns_return_t getdns_list_add_item(struct getdns_list *list, size_t *index); getdns_return_t getdns_list_set_dict(struct getdns_list *list, size_t index, struct getdns_dict *child_dict); /** - * assign the child_list to an item in a parent list, the parent list copies + * assign the child_list to an item in a parent list, the parent list copies * the child list and will free the copy when the list is destroyed - * @param list list containing the item to which child_list is to be assigned + * @param list list containing the item to which child_list is to be assigned * @param index index of the item within list to which child_list is to be assigned * @param *child_list list to assign to the item * @return GETDNS_RETURN_GOOD on success @@ -584,9 +618,9 @@ getdns_return_t getdns_list_set_dict(struct getdns_list *list, size_t index, str */ getdns_return_t getdns_list_set_list(struct getdns_list *list, size_t index, struct getdns_list *child_list); /** - * assign the child_bindata to an item in a parent list, the parent list copies + * assign the child_bindata to an item in a parent list, the parent list copies * the child data and will free the copy when the list is destroyed - * @param list list contiaining the item to which child_list is to be assigned + * @param list list contiaining the item to which child_list is to be assigned * @param index index of the item within list to which child_list is to be assigned * @param *child_bindata data to assign to the item * @return GETDNS_RETURN_GOOD on success @@ -611,7 +645,7 @@ struct getdns_dict *getdns_dict_create(); * for freeing storage allocated to returned value * NOTE: not thread safe - this needs to be fixed to be thread safe * @param srcdict the dictionary structure to copy - * @param dstdict pointer to the location to write pointer to new dictionary + * @param dstdict pointer to the location to write pointer to new dictionary * @return GETDNS_RETURN_GOOD on success */ getdns_return_t @@ -719,8 +753,8 @@ getdns_cancel_callback( /** * \defgroup syncfuns Synchronous API functions that do not use callbacks * These functions do not use callbacks, when the application calls one of these - * functions the library retrieves all of the data before returning. Return - * values are exactly the same as if you had used a callback with the + * functions the library retrieves all of the data before returning. Return + * values are exactly the same as if you had used a callback with the * asynchronous functions. * @{ */ diff --git a/src/list.c b/src/list.c index 41b04e76..9140ecd3 100644 --- a/src/list.c +++ b/src/list.c @@ -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; diff --git a/src/request-internal.c b/src/request-internal.c index 6299d534..604b6bac 100644 --- a/src/request-internal.c +++ b/src/request-internal.c @@ -30,6 +30,7 @@ #include "types-internal.h" #include "util-internal.h" +#include /* useful macros */ #define gd_malloc(sz) context->memory_allocator(sz) @@ -39,37 +40,34 @@ 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->result = NULL; + net_req->next = NULL; + net_req->request_type = request_type; - - /* 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_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, - const char* name, - uint16_t request_type, - struct getdns_dict *extensions, - getdns_transaction_t *transaction_id) { + struct ub_ctx* unbound, + const char* name, + uint16_t request_type, + 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; - - /* create the initial network request */ - net_req = network_req_new(context, name, request_type, - extensions); - if (!net_req) { + result->first_req = NULL; + result->trans_id = ldns_get_random(); + + 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 (!req) { dns_req_free(result); - result = NULL; - } - - result->trans_id = ldns_pkt_id(net_req->pkt); - if (transaction_id) { - *transaction_id = result->trans_id; + return NULL; + } + + result->current_req = req; + result->first_req = req; + + /* 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; } - result->current_req = net_req; - net_req->owner = result; - return result; } diff --git a/src/sync.c b/src/sync.c index 6f029286..fbdef668 100644 --- a/src/sync.c +++ b/src/sync.c @@ -1,7 +1,7 @@ /** * * /brief getdns core functions for synchronous use - * + * * Originally taken from the getdns API description pseudo implementation. * */ @@ -14,10 +14,10 @@ * 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 @@ -29,7 +29,10 @@ #include #include -#include "types-internal.h" +#include +#include +#include "context.h" +#include "general.h" /* stuff to make it compile pedantically */ #define UNUSED_PARAM(x) ((void)(x)) @@ -55,18 +58,17 @@ 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->name, - req_data->request_type, - req_data->extensions, - req_data, - NULL, - sync_callback_func); - - event_base_dispatch(event_base); + + 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, + req_data, + NULL, + sync_callback_func); + + 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 = { @@ -92,7 +94,7 @@ getdns_general_sync( GETDNS_RETURN_GOOD, response }; - + /* create the thread */ int ret = pthread_attr_init(&attr); if (ret != 0) { @@ -110,10 +112,7 @@ getdns_general_sync( if (ret != 0) { return GETDNS_RETURN_GENERIC_ERROR; } - - /* set the old event loop */ - getdns_extension_set_libevent_base(context, orig_base); - + return req_data.response_status; } @@ -134,7 +133,7 @@ getdns_address_sync( getdns_dict_set_int(extensions, GETDNS_STR_EXTENSION_RETURN_BOTH_V4_AND_V6, GETDNS_EXTENSION_TRUE); - + getdns_return_t result = getdns_general_sync(context, name, GETDNS_RRTYPE_A, extensions, response_length, response); @@ -164,7 +163,7 @@ getdns_service_sync( struct getdns_dict **response ) { - + return getdns_general_sync(context, name, GETDNS_RRTYPE_SRV, extensions, response_length, response); diff --git a/src/test/tests_stub_async.c b/src/test/tests_stub_async.c index 71728c23..8c6139ad 100644 --- a/src/test/tests_stub_async.c +++ b/src/test/tests_stub_async.c @@ -31,6 +31,7 @@ #include #include "testmessages.h" #include +#include /* 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); @@ -81,7 +78,7 @@ main() const char * this_name = "www.google.com"; char* this_userarg = "somestring"; // Could add things here to help identify this call getdns_transaction_t this_transaction_id = 0; - + /* Make the call */ getdns_return_t dns_request_return = getdns_address(this_context, this_name, NULL, this_userarg, &this_transaction_id, this_callbackfn); @@ -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 */ diff --git a/src/types-internal.h b/src/types-internal.h index 22eda146..15475a5d 100644 --- a/src/types-internal.h +++ b/src/types-internal.h @@ -36,76 +36,73 @@ /* 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; - - 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; +typedef enum network_req_state_enum { + NET_REQ_NOT_SENT, + NET_REQ_IN_FLIGHT, + NET_REQ_FINISHED, + NET_REQ_CANCELED +} network_req_state; -/* 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 */ - - int reissue_count; - int tx_count; /* the number of times that this packet has been sent */ - - /* not owned */ - getdns_nameserver *ns; /* the server which we sent to */ - - unsigned transmit_me :1; /* needs to be transmitted */ - - getdns_context_t context; - - struct getdns_dns_req *owner; - + /* the async_id from unbound */ + int unbound_id; + /* state var */ + network_req_state state; + /* owner request (contains name) */ + struct getdns_dns_req* owner; + + /* request type */ + uint16_t request_type; + + /* request class */ + uint16_t request_class; + + /* 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; /* utility methods */ @@ -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 diff --git a/src/util-internal.c b/src/util-internal.c index 746fa239..fe367c5f 100644 --- a/src/util-internal.c +++ b/src/util-internal.c @@ -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); } @@ -58,12 +62,12 @@ getdns_return_t dict_to_sockaddr(getdns_dict* ns, struct sockaddr_storage* outpu uint32_t port = 53; memset(output, 0, sizeof(struct sockaddr_storage)); output->ss_family = AF_UNSPEC; - + uint32_t prt = 0; if (getdns_dict_get_int(ns, GETDNS_STR_PORT, &prt) == GETDNS_RETURN_GOOD) { port = prt; } - + getdns_dict_util_get_string(ns, GETDNS_STR_ADDRESS_TYPE, &address_type); getdns_dict_get_bindata(ns, GETDNS_STR_ADDRESS_DATA, &address_data); if (!address_type || !address_data) { @@ -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": , "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": + "rdata_raw": + } + */ + 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": , + "type": 1, + "class": 1, + "ttl": 33000, + "rdata": + { + "ipv4_address": + "rdata_raw": + } + } + */ + 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": , "qtype": 1, "qclass": 1 }, + "answer": + [ + { + "name": , + "type": 1, + "class": 1, + "ttl": 33000, + "rdata": + { + "ipv4_address": + "rdata_raw": + } + } + ], + "authority": + [ + { + "name": , + "type": 1, + "class": 1, + "ttl": 600, + "rdata": + { + "ipv4_address": + "rdata_raw": + } + } + ] + "additional": [], + "canonical_name": , + "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; } diff --git a/src/util-internal.h b/src/util-internal.h index 5e144d56..22094572 100644 --- a/src/util-internal.h +++ b/src/util-internal.h @@ -28,24 +28,25 @@ * THE SOFTWARE. */ +/* #include "types-internal.h" +*/ + +#include +#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, diff --git a/unbound.2985.patch b/unbound.2985.patch new file mode 100644 index 00000000..ed3875c1 --- /dev/null +++ b/unbound.2985.patch @@ -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; ++}