Handle immediate callback.

This commit is contained in:
Neel Goyal 2013-11-05 13:31:29 -05:00
parent 266cdb0063
commit 4fb66d8ea7
5 changed files with 103 additions and 36 deletions

View File

@ -919,6 +919,9 @@ getdns_return_t getdns_context_prepare_for_resolution(getdns_context_t 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);
/* use /etc/hosts */
ub_ctx_hosts(context->unbound_sync, NULL);
ub_ctx_hosts(context->unbound_async, NULL);
} else if (context->resolution_type == GETDNS_CONTEXT_RECURSING) {
/* set recursive */

View File

@ -53,19 +53,26 @@ void this_callbackfn(struct getdns_context_t *this_context,
this_ret = getdns_dict_get_int(this_response, "status", &this_error); // Ignore any error
if (this_error != GETDNS_RESPSTATUS_GOOD) // If the search didn't return "good"
{
fprintf(stderr, "The search had no results, and a return value of %d. Exiting.", this_error);
fprintf(stderr, "The search had no results, and a return value of %d. Exiting.\n", this_error);
getdns_dict_destroy(this_response);
return;
}
struct getdns_list * just_the_addresses_ptr;
this_ret = getdns_dict_get_list(this_response, "just_address_answers", &just_the_addresses_ptr);
if (this_ret != GETDNS_RETURN_GOOD) // This check is really not needed, but prevents a compiler error under "pedantic"
{
fprintf(stderr, "Trying to get the answers failed: %d", this_ret);
fprintf(stderr, "Trying to get the answers failed: %d\n", this_ret);
getdns_dict_destroy(this_response);
return;
}
size_t num_addresses = 0;
this_ret = getdns_list_get_length(just_the_addresses_ptr, &num_addresses); // Ignore any error
/* Go through each record */
if (num_addresses == 0) {
fprintf(stderr, "There are no addresses.\n");
getdns_dict_destroy(this_response);
return;
}
for ( size_t rec_count = 0; rec_count < num_addresses; ++rec_count )
{
struct getdns_bindata * this_address_data;
@ -77,9 +84,9 @@ void this_callbackfn(struct getdns_context_t *this_context,
}
}
else if (this_callback_type == GETDNS_CALLBACK_CANCEL)
fprintf(stderr, "The callback with ID %"PRIu64" was cancelled. Exiting.", this_transaction_id);
fprintf(stderr, "The callback with ID %"PRIu64" was cancelled. Exiting.\n", this_transaction_id);
else
fprintf(stderr, "The callback got a callback_type of %d. Exiting.", this_callback_type);
fprintf(stderr, "The callback got a callback_type of %d. Exiting.\n", this_callback_type);
/* clean up */
getdns_dict_destroy(this_response);
@ -101,7 +108,7 @@ main()
this_event_base = event_base_new();
if (this_event_base == NULL)
{
fprintf(stderr, "Trying to create the event base failed.");
fprintf(stderr, "Trying to create the event base failed.\n");
return(GETDNS_RETURN_GENERIC_ERROR);
}
(void)getdns_extension_set_libevent_base(this_context, this_event_base);
@ -110,12 +117,17 @@ main()
char* this_userarg = "somestring"; // Could add things here to help identify this call
getdns_transaction_t this_transaction_id = 0;
// getdns_context_set_resolution_type(this_context, GETDNS_CONTEXT_STUB);
/* Make the call */
getdns_return_t dns_request_return = getdns_address(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);
fprintf(stderr, "A bad domain name was used: %s. Exiting.\n", this_name);
return(GETDNS_RETURN_GENERIC_ERROR);
} else if (dns_request_return != GETDNS_RETURN_GOOD) {
fprintf(stderr, "The context is not setup properly.\n");
return(GETDNS_RETURN_GENERIC_ERROR);
}
else

View File

@ -28,36 +28,6 @@
* THE SOFTWARE.
*/
/**
* Much of this is based on / duplicated code from libevent evdns. Credits to
* Nick Mathewson and Niels Provos
*
* https://github.com/libevent/libevent/
*
* libevent dns is based on software by Adam Langly. Adam's original message:
*
* Async DNS Library
* Adam Langley <agl@imperialviolet.org>
* http://www.imperialviolet.org/eventdns.html
* Public Domain code
*
* This software is Public Domain. To view a copy of the public domain dedication,
* visit http://creativecommons.org/licenses/publicdomain/ or send a letter to
* Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA.
*
* I ask and expect, but do not require, that all derivative works contain an
* attribution similar to:
* Parts developed by Adam Langley <agl@imperialviolet.org>
*
* You may wish to replace the word "Parts" with something else depending on
* the amount of original code.
*
* (Derivative works does not include programs which link against, run or include
* the source verbatim in their source distributions)
*
* Version: 0.1b
*/
#include <string.h>
#include <unbound.h>
#include <unbound-event.h>
@ -66,16 +36,28 @@
#include "context.h"
#include "types-internal.h"
#include "util-internal.h"
#include <stdio.h>
/* stuff to make it compile pedantically */
#define UNUSED_PARAM(x) ((void)(x))
/* declarations */
static void ub_resolve_callback(void* arg, int err, ldns_buffer* result, int sec, char* bogus);
static void ub_resolve_timeout(evutil_socket_t fd, short what, void *arg);
static void ub_local_resolve_timeout(evutil_socket_t fd, short what, void *arg);
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);
typedef struct netreq_cb_data {
getdns_network_req *netreq;
int err;
ldns_buffer* result;
int sec;
char* bogus;
} netreq_cb_data;
/* cancel, cleanup and send timeout to callback */
static void ub_resolve_timeout(evutil_socket_t fd, short what, void *arg) {
getdns_dns_req *dns_req = (getdns_dns_req*) arg;
@ -97,6 +79,27 @@ static void ub_resolve_timeout(evutil_socket_t fd, short what, void *arg) {
trans_id);
}
static void ub_local_resolve_timeout(evutil_socket_t fd, short what, void *arg) {
netreq_cb_data* cb_data = (netreq_cb_data*) arg;
/* cleanup the local timer here since the memory may be
* invalid after calling ub_resolve_callback
*/
getdns_dns_req* dnsreq = cb_data->netreq->owner;
event_free(dnsreq->local_cb_timer);
dnsreq->local_cb_timer = NULL;
/* just call ub_resolve_callback */
ub_resolve_callback(cb_data->netreq, cb_data->err, cb_data->result, cb_data->sec, cb_data->bogus);
/* cleanup the state */
ldns_buffer_free(cb_data->result);
if (cb_data->bogus) {
free(cb_data->bogus);
}
free(cb_data);
}
/* 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;
@ -157,12 +160,45 @@ static int submit_network_request(getdns_network_req* netreq) {
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
*/
if (netreq->state == NET_REQ_NOT_SENT) {
/* just do a very short timer since this was called immediately.
* we can make this less hacky, but it gets interesting when multiple
* netreqs need to be issued and some resolve immediately vs. not.
*/
struct timeval tv;
getdns_dns_req* dnsreq = netreq->owner;
netreq_cb_data* cb_data = (netreq_cb_data*) malloc(sizeof(netreq_cb_data));
cb_data->netreq = netreq;
cb_data->err = err;
cb_data->sec = sec;
cb_data->result = NULL;
cb_data->bogus = NULL; /* unused but here in case we need it */
if (result) {
cb_data->result = ldns_buffer_new(ldns_buffer_limit(result));
if (!cb_data->result) {
cb_data->err = GETDNS_RETURN_GENERIC_ERROR;
} else {
/* copy */
ldns_buffer_copy(cb_data->result, result);
}
}
/* schedule the timeout */
dnsreq->local_cb_timer = evtimer_new(dnsreq->ev_base, ub_local_resolve_timeout, cb_data);
tv.tv_sec = 0;
/* half ms */
tv.tv_usec = 500;
evtimer_add(dnsreq->local_cb_timer, &tv);
return;
}
netreq->state = NET_REQ_FINISHED;
if (err) {
handle_network_request_error(netreq, err);
@ -230,12 +266,14 @@ getdns_general_ub(struct ub_ctx* unbound,
getdns_context_track_outbound_request(req);
/* assign a timeout */
req->ev_base = ev_base;
req->timeout = evtimer_new(ev_base, ub_resolve_timeout, req);
tv.tv_sec = context->timeout / 1000;
tv.tv_usec = (context->timeout % 1000) * 1000;
evtimer_add(req->timeout, &tv);
/* issue the first network req */
r = submit_network_request(req->first_req);
if (r != 0) {

View File

@ -96,6 +96,11 @@ void dns_req_free(getdns_dns_req* req) {
event_free(req->timeout);
}
if (req->local_cb_timer) {
event_del(req->local_cb_timer);
event_free(req->local_cb_timer);
}
/* free strduped name */
free(req->name);
@ -127,6 +132,8 @@ getdns_dns_req* dns_req_new(getdns_context_t context,
result->first_req = NULL;
result->trans_id = ldns_get_random();
result->timeout = NULL;
result->local_cb_timer = NULL;
result->ev_base = NULL;
getdns_dict_copy(extensions, &result->extensions);

View File

@ -39,6 +39,7 @@ struct getdns_dns_req;
struct getdns_network_req;
struct ub_ctx;
struct event;
struct event_base;
typedef enum network_req_state_enum {
NET_REQ_NOT_SENT,
@ -91,6 +92,12 @@ typedef struct getdns_dns_req {
/* request timeout event */
struct event* timeout;
/* local callback timer */
struct event* local_cb_timer;
/* event base this req is scheduled on */
struct event_base* ev_base;
/* context that owns the request */
getdns_context_t context;