diff --git a/src/Makefile.in b/src/Makefile.in index 5bc65d2e..b5abc5ce 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -27,11 +27,11 @@ LDFLAGS=-levent_core -lldns -lunbound 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 \ +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 $? diff --git a/src/context.c b/src/context.c index d80825dd..80e81130 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 @@ -33,6 +33,7 @@ #include #include "context.h" #include "util-internal.h" +#include "types-internal.h" #include #include #include @@ -122,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; @@ -134,7 +135,7 @@ static struct getdns_list* create_from_ldns_list(ldns_rdf** ldns_list, size_t co free(srch); } break; - + default: break; } @@ -162,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 * @@ -193,21 +214,24 @@ getdns_return_t getdns_context_create( 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; @@ -251,13 +275,14 @@ getdns_context_destroy( getdns_list_destroy(context->suffix); getdns_list_destroy(context->dnssec_trust_anchors); getdns_list_destroy(context->upstream_list); - + /* destroy the ub context */ ub_ctx_delete(context->unbound_async); ub_ctx_delete(context->unbound_sync); - + event_base_free(context->event_base_sync); - + + ldns_rbtree_free(context->outbound_requests); free(context); return; @@ -282,7 +307,7 @@ getdns_context_set_context_update_callback( */ 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_sync, opt, value); ub_ctx_set_option(ctx->unbound_async, opt, value); } @@ -303,7 +328,7 @@ static inline void clear_resolution_type_set_flag(getdns_context_t context, uint /* * getdns_context_set_context_update - * + * */ getdns_return_t getdns_context_set_context_update( @@ -336,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 */ @@ -363,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 */ @@ -425,7 +450,7 @@ getdns_context_set_limit_outstanding_queries( 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 */ @@ -460,7 +485,7 @@ getdns_context_set_follow_redirects( 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 */ @@ -491,7 +516,7 @@ getdns_context_set_dns_root_servers( /* validate and add ip str */ for (i = 0; i < count; ++i) { getdns_dict* dict = NULL; - getdns_list_get_dict(addresses, i, &dict); + getdns_list_get_dict(addresses, i, &dict); r = add_ip_str(dict); if (r != GETDNS_RETURN_GOOD) { break; @@ -561,7 +586,7 @@ getdns_context_set_suffix( clear_resolution_type_set_flag(context, GETDNS_CONTEXT_STUB); dispatch_updated(context, GETDNS_CONTEXT_CODE_SUFFIX); - + return GETDNS_RETURN_GOOD; } /* getdns_context_set_suffix */ @@ -586,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 */ @@ -603,7 +628,7 @@ getdns_context_set_dnssec_allowed_skew( 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 */ @@ -626,12 +651,12 @@ getdns_context_set_stub_resolution( getdns_list *copy = NULL; if (getdns_list_copy(upstream_list, ©) != GETDNS_RETURN_GOOD) { 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); + getdns_list_get_dict(upstream_list, i, &dict); r = add_ip_str(dict); if (r != GETDNS_RETURN_GOOD) { break; @@ -649,7 +674,7 @@ getdns_context_set_stub_resolution( 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 */ @@ -672,7 +697,7 @@ getdns_context_set_edns_maximum_udp_payload_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 */ @@ -689,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 */ @@ -706,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 */ @@ -728,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 */ @@ -791,8 +816,8 @@ getdns_extension_set_libevent_base( ub_ctx_set_event(context->unbound_async, this_event_base); context->async_set = 1; } else { - context->async_set = 0; ub_ctx_set_event(context->unbound_async, context->event_base_sync); + context->async_set = 0; } return GETDNS_RETURN_GOOD; } /* getdns_extension_set_libevent_base */ @@ -814,7 +839,7 @@ getdns_cancel_callback( static void ub_setup_stub(struct ub_ctx* ctx, getdns_list* upstreams, size_t count) { size_t i; - /* reset forwarding servers */ + /* reset forwarding servers */ ub_ctx_set_fwd(ctx, NULL); for (i = 0 ; i < count; ++i) { getdns_dict* dict = NULL; @@ -856,4 +881,36 @@ getdns_return_t getdns_context_prepare_for_resolution(getdns_context_t context) 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 32c9bff7..f9278896 100644 --- a/src/context.h +++ b/src/context.h @@ -34,6 +34,8 @@ 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); @@ -53,11 +55,11 @@ struct getdns_context_t { struct getdns_list *suffix; struct getdns_list *dnssec_trust_anchors; struct getdns_list *upstream_list; - + 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; @@ -65,18 +67,23 @@ struct getdns_context_t { /* Event loop for sync requests */ struct event_base* event_base_sync; - + /* 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 + + /* 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 **/ @@ -86,5 +93,10 @@ struct getdns_context_t { */ 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/general.c b/src/general.c index 5ef4a612..647def62 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 @@ -63,101 +63,143 @@ #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)) -typedef struct getdns_ub_req { - struct ub_ctx *unbound; - getdns_context_t context; - char* name; - void* userarg; - getdns_callback_t callback; - getdns_transaction_t transaction_id; -} getdns_ub_req; +/* 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); -static void getdns_ub_req_free(getdns_ub_req* req) { - free(req->name); - free(req); +/* 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); } -void ub_resolve_callback(void* arg, int err, ldns_buffer* result, int sec, char* bogus) { - getdns_ub_req* req = (getdns_ub_req*) arg; - ldns_pkt* pkt = NULL; - if (err) { - req->callback(req->context, - GETDNS_CALLBACK_ERROR, - NULL, - req->userarg, - req->transaction_id); - } else { - /* parse */ - ldns_status r = ldns_buffer2pkt_wire(&pkt, result); - if (r != LDNS_STATUS_OK) { - req->callback(req->context, - GETDNS_CALLBACK_ERROR, - NULL, - req->userarg, - req->transaction_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); + + cb(context, + GETDNS_CALLBACK_COMPLETE, + response, + user_arg, + trans_id); +} + +static int submit_network_request(getdns_network_req* netreq) { + getdns_dns_req *dns_req = netreq->owner; + netreq->state = NET_REQ_IN_FLIGHT; + return ub_resolve_event(dns_req->unbound, + dns_req->name, + netreq->request_type, + netreq->request_class, + netreq, + ub_resolve_callback, + &(netreq->unbound_id)); +} + +static void ub_resolve_callback(void* arg, int err, ldns_buffer* result, int sec, char* bogus) { + getdns_network_req* netreq = (getdns_network_req*) arg; + netreq->state = NET_REQ_FINISHED; + if (err) { + handle_network_request_error(netreq, err); } else { - getdns_dict* response = create_getdns_response(pkt); - ldns_pkt_free(pkt); - req->callback(req->context, - GETDNS_CALLBACK_COMPLETE, - response, - req->userarg, - req->transaction_id); + /* 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); + } + } } - } - /* cleanup */ - getdns_ub_req_free(req); } 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 -) { +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) { + getdns_return_t gr; int r; - int async_id = 0; + + gr = getdns_context_prepare_for_resolution(context); + if (gr != GETDNS_RETURN_GOOD) { + return GETDNS_RETURN_BAD_CONTEXT; + } /* request state */ - getdns_ub_req* req = (getdns_ub_req*) malloc(sizeof(getdns_ub_req)); - req->unbound = unbound; - req->context = context; - req->name = strdup(name); - req->userarg = userarg; - req->callback = callbackfn; + getdns_dns_req* req = dns_req_new(context, + unbound, + name, + request_type, + extensions); + if (!req) { + return GETDNS_RETURN_GENERIC_ERROR; + } - getdns_context_prepare_for_resolution(context); + req->user_pointer = userarg; + req->user_callback = callbackfn; - /* TODO: - setup root or stub + /* TODO: handle immediate callback - A + AAAA */ - r = ub_resolve_event(unbound, req->name, request_type, - LDNS_RR_CLASS_IN, req, ub_resolve_callback, - &async_id); - if (transaction_id) { - *transaction_id = async_id; + *transaction_id = req->trans_id; } - req->transaction_id = async_id; + getdns_context_track_outbound_request(req); + + /* issue the first network req */ + r = submit_network_request(req->first_req); if (r != 0) { - getdns_ub_req_free(req); - return GETDNS_RETURN_GENERIC_ERROR; + /* clean up the request */ + getdns_context_clear_outbound_request(req); + dns_req_free(req); + return GETDNS_RETURN_GENERIC_ERROR; } return GETDNS_RETURN_GOOD; } @@ -165,26 +207,23 @@ getdns_general_ub( /* * 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 -) -{ - + 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 + * or callback */ - return GETDNS_RETURN_BAD_CONTEXT; + return GETDNS_RETURN_BAD_CONTEXT; } - + return getdns_general_ub(context->unbound_async, context, name, @@ -201,16 +240,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(); @@ -220,7 +256,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); @@ -228,6 +264,6 @@ getdns_address( getdns_dict_destroy(extensions); } return result; -} +} /* getdns_general.c */ diff --git a/src/getdns/getdns.h b/src/getdns/getdns.h index deb86fa6..1ff2acb6 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 @@ -202,7 +202,7 @@ struct event_base; */ /** - * \defgroup respstatus Status Codes for Responses + * \defgroup respstatus Status Codes for Responses * @{ */ #define GETDNS_RESPSTATUS_GOOD 900 @@ -217,7 +217,7 @@ struct event_base; */ /** - * \defgroup extvals Values Associated With Extensions + * \defgroup extvals Values Associated With Extensions * @{ */ #define GETDNS_EXTENSION_TRUE 1 @@ -532,8 +532,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 @@ -559,7 +559,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 */ @@ -576,9 +576,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 @@ -586,9 +586,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 @@ -613,7 +613,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 @@ -721,8 +721,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/request-internal.c b/src/request-internal.c index 6299d534..8d0ed758 100644 --- a/src/request-internal.c +++ b/src/request-internal.c @@ -39,37 +39,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 +74,86 @@ 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->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/test/tests_stub_async.c b/src/test/tests_stub_async.c index b5f6933f..70bf8f4d 100644 --- a/src/test/tests_stub_async.c +++ b/src/test/tests_stub_async.c @@ -91,13 +91,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..94fd9108 100644 --- a/src/types-internal.h +++ b/src/types-internal.h @@ -36,76 +36,68 @@ /* 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 +} 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; + + /* 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 */ - + } getdns_dns_req; /* utility methods */ @@ -113,29 +105,23 @@ 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); - +/* cancel the request */ +getdns_return_t dns_req_cancel(getdns_dns_req* req); #endif diff --git a/src/util-internal.c b/src/util-internal.c index 05be6ef4..f198d817 100644 --- a/src/util-internal.c +++ b/src/util-internal.c @@ -29,6 +29,7 @@ */ #include "util-internal.h" +#include "types-internal.h" getdns_return_t getdns_dict_util_set_string(getdns_dict* dict, char* name, const char* value) { @@ -58,12 +59,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,28 +114,8 @@ 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; - } - 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; -} - -getdns_dict *create_getdns_response(ldns_pkt* pkt) { +getdns_dict *create_getdns_response(struct getdns_dns_req* completed_request) { + ldns_pkt* pkt = completed_request->first_req->result; char* data = ldns_pkt2str(pkt); getdns_dict* result = getdns_dict_create(); getdns_bindata bindata = { diff --git a/src/util-internal.h b/src/util-internal.h index b5e826bf..22094572 100644 --- a/src/util-internal.h +++ b/src/util-internal.h @@ -35,18 +35,13 @@ #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 */