From 1f203485e20045b8c99488194e740c1d19f4fe50 Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Wed, 8 Oct 2014 15:42:33 +0200 Subject: [PATCH] eventloop separate from context & libmini_event --- src/Makefile.in | 13 +- src/context.c | 495 +++++++++++++--------------------- src/context.h | 32 +-- src/extension/libmini_event.c | 196 ++++++++++++++ src/extension/libmini_event.h | 65 +++++ src/general.c | 6 +- src/getdns/getdns_extra.h | 122 +++++---- src/request-internal.c | 2 +- src/types-internal.h | 4 +- 9 files changed, 537 insertions(+), 398 deletions(-) create mode 100755 src/extension/libmini_event.c create mode 100755 src/extension/libmini_event.h diff --git a/src/Makefile.in b/src/Makefile.in index 574a08bf..3682a470 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -78,7 +78,8 @@ EXTENSION_LIBUV_LDFLAGS=@EXTENSION_LIBUV_LDFLAGS@ GETDNS_OBJ=sync.lo context.lo list.lo dict.lo convert.lo general.lo \ hostname.lo service.lo request-internal.lo util-internal.lo \ getdns_error.lo rr-dict.lo dnssec.lo const-info.lo \ - ub_timed_resolve.lo stub.lo + ub_timed_resolve.lo stub.lo \ + libmini_event.lo GLDNS_OBJ=keyraw.lo gbuffer.lo wire2str.lo parse.lo parseutil.lo rrdef.lo \ str2wire.lo @@ -97,7 +98,7 @@ UTIL_OBJ=mini_event.lo rbtree.lo .c.lo: $(LIBTOOL) --quiet --tag=CC --mode=compile $(CC) $(CFLAGS) -c $< -o $@ -$(GLDNS_OBJ) $(COMPAT_OBJ) $(UTIL_OBJ): +$(GLDNS_OBJ) $(COMPAT_OBJ) $(UTIL_OBJ) libmini_event.lo: @: $(LIBTOOL) --quiet --tag=CC --mode=compile $(CC) $(CFLAGS) -c $< -o $@ @@ -193,10 +194,11 @@ configure.status: configure depend: (cd $(srcdir) ; awk 'BEGIN{P=1}{if(P)print}/^# Dependencies/{P=0}' Makefile.in > Makefile.in.new ) - (cd $(srcdir) ; gcc -MM -I. gldns/*.c compat/*.c util/*.c | \ + (cd $(srcdir) ; gcc -MM -I. gldns/*.c compat/*.c util/*.c extension/libmini_event.c| \ sed -e 's?gldns/?$$(srcdir)/gldns/?g' \ -e 's?compat/?$$(srcdir)/compat/?g' \ -e 's?util/?$$(srcdir)/util/?g' \ + -e 's?extension/?$$(srcdir)/extension/?g' \ -e 's!\(.*\)\.o[ :]*!\1.lo \1.o: !g' >> Makefile.in.new ) (cd $(srcdir) ; diff Makefile.in.new Makefile.in && rm Makefile.in.new \ || mv Makefile.in.new Makefile.in ) @@ -204,7 +206,7 @@ depend: .PHONY: clean test example FORCE: -# Dependencies for gldns and compatibility functions +# Dependencies for gldns, utils, the mini_event extension and compat functions gbuffer.lo gbuffer.o: $(srcdir)/gldns/gbuffer.c config.h $(srcdir)/gldns/gbuffer.h keyraw.lo keyraw.o: $(srcdir)/gldns/keyraw.c config.h $(srcdir)/gldns/keyraw.h $(srcdir)/gldns/rrdef.h parse.lo parse.o: $(srcdir)/gldns/parse.c config.h $(srcdir)/gldns/parse.h $(srcdir)/gldns/parseutil.h \ @@ -221,3 +223,6 @@ mini_event.lo mini_event.o: $(srcdir)/util/mini_event.c config.h $(srcdir)/util/ $(srcdir)/util/fptr_wlist.h rbtree.lo rbtree.o: $(srcdir)/util/rbtree.c config.h $(srcdir)/util/log.h $(srcdir)/util/fptr_wlist.h \ $(srcdir)/util/rbtree.h +libmini_event.lo libmini_event.o: $(srcdir)/extension/libmini_event.c config.h \ + $(srcdir)/extension/libmini_event.h types-internal.h getdns/getdns.h \ + getdns/getdns_extra.h getdns/getdns.h $(srcdir)/util/mini_event.h $(srcdir)/util/rbtree.h diff --git a/src/context.c b/src/context.c index 1498b742..cd239415 100755 --- a/src/context.c +++ b/src/context.c @@ -517,129 +517,6 @@ create_ldns_rbtree(getdns_context * context, return result; } -/****************************************************************************** - ****************************************************************************** - ***** - ***** Start of mini_event extension - ***** TODO: Move to own source file - *****/ - -/** call timeouts handlers, and return how long to wait for next one or -1 */ -void getdns_handle_timeouts(struct getdns_event_base* base, - struct timeval* now, struct timeval* wait); -/** call select and callbacks for that */ -int getdns_handle_select(struct getdns_event_base* base, struct timeval* wait); - -int -getdns_mini_event_settime(getdns_mini_event_extension *e) -{ - if (gettimeofday(e->base->time_tv, NULL) < 0) - return -1; - *e->base->time_secs = (time_t)e->base->time_tv->tv_sec; - return 0; -} - -static void -getdns_mini_event_timeout_cb(int fd, short bits, void *arg) -{ - getdns_timeout_data_t* timeout_data = (getdns_timeout_data_t*)arg; - timeout_data->callback(timeout_data->userarg); -} - -static getdns_return_t -getdns_mini_event_schedule_timeout(getdns_context *context, void *ext, - uint64_t timeout, getdns_timeout_data_t *timeout_data) -{ - getdns_mini_event_extension *e = (getdns_mini_event_extension *)ext; - struct timeval tv; - struct getdns_event *ev; - - tv.tv_sec = timeout / 1000; - tv.tv_usec = (timeout % 1000) * 1000; - - ev = GETDNS_MALLOC(context->mf, struct getdns_event); - timeout_data->extension_timer = ev; - getdns_event_set(ev, -1, EV_TIMEOUT, getdns_mini_event_timeout_cb, - timeout_data); - - (void) getdns_mini_event_settime(e); - (void) getdns_event_base_set(e->base, ev); - (void) getdns_event_add(ev, &tv); - return GETDNS_RETURN_GOOD; -} - -static getdns_return_t -getdns_mini_event_clear_timeout(getdns_context *context, void *ext, void *timer) -{ - /* getdns_mini_event_extension *e = (getdns_mini_event_extension *)ext; - */ - struct getdns_event *ev = (struct getdns_event *)timer; - - (void) getdns_event_del(ev); - GETDNS_FREE(context->mf, ev); - return GETDNS_RETURN_GOOD; -} - -static getdns_return_t -getdns_mini_event_request_count_changed(getdns_context *context, - uint32_t request_count, void *ext) -{ - getdns_mini_event_extension *e = (getdns_mini_event_extension *)ext; - - if (request_count == 0) - (void) getdns_event_del(&e->ub_event); - return GETDNS_RETURN_GOOD; -} - -static void -getdns_mini_event_cb(int fd, short bits, void *arg) -{ - getdns_context *context = (getdns_context *)arg; - - if (getdns_context_process_async(context)) - return; - - getdns_mini_event_request_count_changed(context, - getdns_context_get_num_pending_requests(context, NULL), - context->extension_data); -} - -static getdns_return_t -getdns_mini_event_cleanup(getdns_context *context, void *ext); -getdns_return_t -getdns_mini_event_extension_init(getdns_mini_event_extension *e) -{ - e->base = getdns_event_init(&e->time_secs, &e->time_tv); - if (!e->base) - return GETDNS_RETURN_MEMORY_ERROR; - e->ub_event.ev_fd = -1; - e->ext.cleanup_data = getdns_mini_event_cleanup; - e->ext.schedule_timeout = getdns_mini_event_schedule_timeout; - e->ext.clear_timeout = getdns_mini_event_clear_timeout; - e->ext.request_count_changed = getdns_mini_event_request_count_changed; - return GETDNS_RETURN_GOOD; -} - -static getdns_return_t -getdns_mini_event_cleanup(getdns_context *context, void *ext) -{ - getdns_mini_event_extension *e = (getdns_mini_event_extension *)ext; - - if (e->ub_event.ev_fd != -1) { - getdns_event_del(&e->ub_event); - e->ub_event.ev_fd = -1; - } - /* TODO: Cleanup all synchronous events? Maybe not... */ - getdns_event_base_free(e->base); - return GETDNS_RETURN_GOOD; -} - -/***** - ***** End of mini_event extension - ***** - ***************************************************************************** - *****************************************************************************/ - /* * getdns_context_create * @@ -655,99 +532,95 @@ getdns_context_create_with_extended_memory_functions( void (*free)(void *userarg, void *) ) { - struct getdns_context *result = NULL; - mf_union mf; + getdns_return_t r; + struct getdns_context *result = NULL; + mf_union mf; - if (!context || !malloc || !realloc || !free) - return GETDNS_RETURN_INVALID_PARAMETER; + if (!context || !malloc || !realloc || !free) + return GETDNS_RETURN_INVALID_PARAMETER; - /** default init **/ - mf.ext.malloc = malloc; - result = userarg == MF_PLAIN - ? (*mf.pln.malloc)( sizeof(struct getdns_context)) - : (*mf.ext.malloc)(userarg, sizeof(struct getdns_context)); - if (!result) { - return GETDNS_RETURN_GENERIC_ERROR; - } - result->processing = 0; - result->destroying = 0; - result->my_mf.mf_arg = userarg; - result->my_mf.mf.ext.malloc = malloc; - result->my_mf.mf.ext.realloc = realloc; - result->my_mf.mf.ext.free = free; + /** default init **/ + mf.ext.malloc = malloc; + result = userarg == MF_PLAIN + ? (*mf.pln.malloc)( sizeof(struct getdns_context)) + : (*mf.ext.malloc)(userarg, sizeof(struct getdns_context)); - result->update_callback = NULL; + if (!result) + return GETDNS_RETURN_MEMORY_ERROR; - result->mf.mf_arg = userarg; - result->mf.mf.ext.malloc = malloc; - result->mf.mf.ext.realloc = realloc; - result->mf.mf.ext.free = free; + result->processing = 0; + result->destroying = 0; + result->my_mf.mf_arg = userarg; + result->my_mf.mf.ext.malloc = malloc; + result->my_mf.mf.ext.realloc = realloc; + result->my_mf.mf.ext.free = free; - result->resolution_type_set = 0; + result->update_callback = NULL; - result->outbound_requests = create_ldns_rbtree(result, transaction_id_cmp); - result->local_hosts = create_ldns_rbtree(result, local_host_cmp); + result->mf.mf_arg = userarg; + result->mf.mf.ext.malloc = malloc; + result->mf.mf.ext.realloc = realloc; + result->mf.mf.ext.free = free; + result->resolution_type_set = 0; - result->resolution_type = GETDNS_RESOLUTION_RECURSING; - if(create_default_namespaces(result) != GETDNS_RETURN_GOOD) { - getdns_context_destroy(result); - return GETDNS_RETURN_GENERIC_ERROR; - } + result->outbound_requests = create_ldns_rbtree(result, transaction_id_cmp); + result->local_hosts = create_ldns_rbtree(result, local_host_cmp); - result->timeout = 5000; - result->follow_redirects = GETDNS_REDIRECTS_FOLLOW; - result->dns_root_servers = create_default_root_servers(); - result->append_name = GETDNS_APPEND_NAME_ALWAYS; - result->suffix = NULL; + if (!result->outbound_requests || !result->local_hosts) { + r = GETDNS_RETURN_MEMORY_ERROR; + goto error; + } - result->dnssec_trust_anchors = NULL; + result->resolution_type = GETDNS_RESOLUTION_RECURSING; + if ((r = create_default_namespaces(result))) + goto error; - result->edns_extended_rcode = 0; - result->edns_version = 0; - result->edns_do_bit = 1; + result->timeout = 5000; + result->follow_redirects = GETDNS_REDIRECTS_FOLLOW; + result->dns_root_servers = create_default_root_servers(); + result->append_name = GETDNS_APPEND_NAME_ALWAYS; + result->suffix = NULL; - result->extension = &result->mini_event_extension.ext; - result->extension_data = (void *)&result->mini_event_extension; - if (getdns_mini_event_extension_init(&result->mini_event_extension)) - return GETDNS_RETURN_GENERIC_ERROR; + result->dnssec_trust_anchors = NULL; + + result->edns_extended_rcode = 0; + result->edns_version = 0; + result->edns_do_bit = 1; + + result->extension = &result->mini_event.loop; + if ((r = getdns_mini_event_init(result, &result->mini_event))) + goto error; result->fchg_resolvconf = NULL; - result->fchg_hosts = NULL; - if (set_from_os) { - if (GETDNS_RETURN_GOOD != set_os_defaults(result)) { - getdns_context_destroy(result); - return GETDNS_RETURN_GENERIC_ERROR; - } - } - result->dnssec_allowed_skew = 0; - result->edns_maximum_udp_payload_size = 512; - result->dns_transport = GETDNS_TRANSPORT_UDP_FIRST_AND_FALL_BACK_TO_TCP; - result->limit_outstanding_queries = 0; - result->has_ta = priv_getdns_parse_ta_file(NULL, NULL); - result->return_dnssec_status = GETDNS_EXTENSION_FALSE; - if (!result->outbound_requests || - !result->local_hosts) { - getdns_context_destroy(result); - return GETDNS_RETURN_MEMORY_ERROR; - } - /* unbound context is initialized here */ - result->unbound_ctx = NULL; - if (GETDNS_RETURN_GOOD != rebuild_ub_ctx(result)) { - getdns_context_destroy(result); - return GETDNS_RETURN_GENERIC_ERROR; - } - /* ldns context is initialised to NULL here and rebuilt later if needed */ - result->ldns_res = NULL; + result->fchg_hosts = NULL; - if(create_local_hosts(result) != GETDNS_RETURN_GOOD) { - getdns_context_destroy(result); - return GETDNS_RETURN_GENERIC_ERROR; - } + if (set_from_os && (r = set_os_defaults(result))) + goto error; - *context = result; + result->dnssec_allowed_skew = 0; + result->edns_maximum_udp_payload_size = 512; + result->dns_transport = GETDNS_TRANSPORT_UDP_FIRST_AND_FALL_BACK_TO_TCP; + result->limit_outstanding_queries = 0; + result->has_ta = priv_getdns_parse_ta_file(NULL, NULL); + result->return_dnssec_status = GETDNS_EXTENSION_FALSE; - return GETDNS_RETURN_GOOD; + /* unbound context is initialized here */ + result->unbound_ctx = NULL; + if ((r = rebuild_ub_ctx(result))) + goto error; + + /* ldns context is initialised to NULL here and rebuilt later if needed */ + result->ldns_res = NULL; + + if ((r = create_local_hosts(result))) + goto error; + + *context = result; + return GETDNS_RETURN_GOOD; +error: + getdns_context_destroy(result); + return r; } /* getdns_context_create_with_extended_memory_functions */ /* @@ -805,7 +678,7 @@ getdns_context_destroy(struct getdns_context *context) } context->destroying = 1; cancel_outstanding_requests(context, 1); - getdns_extension_detach_eventloop(context); + getdns_context_detach_eventloop(context); if (context->namespaces) GETDNS_FREE(context->my_mf, context->namespaces); @@ -878,6 +751,33 @@ set_ub_number_opt(struct getdns_context *ctx, char *opt, uint16_t value) set_ub_string_opt(ctx, opt, buffer); } +static void +getdns_context_request_count_changed(getdns_context *context, uint32_t prev_rc) +{ + if ((!prev_rc && !context->outbound_requests->count) || + ( prev_rc && context->outbound_requests->count)) + return; + + if (context->outbound_requests->count) + context->extension->functions->schedule_read( + context->extension, ub_fd(context->unbound_ctx), + TIMEOUT_FOREVER, &context->ub_event); + else + context->extension->functions->clear_read( + context->extension, &context->ub_event); +} + +static void +getdns_context_ub_read_cb(void *userarg) +{ + getdns_context *context = (getdns_context *)userarg; + int32_t prev_rc = context->outbound_requests->count; + + if (getdns_context_process_async(context)) return; + (void) getdns_context_get_num_pending_requests(context, NULL); + getdns_context_request_count_changed(context, prev_rc); +} + static getdns_return_t rebuild_ub_ctx(struct getdns_context* context) { if (context->unbound_ctx != NULL) { @@ -903,14 +803,11 @@ rebuild_ub_ctx(struct getdns_context* context) { (void) ub_ctx_add_ta_file( context->unbound_ctx, TRUST_ANCHOR_FILE); } - if (context->extension == (void *)&context->mini_event_extension.ext) { - getdns_mini_event_extension *e =&context->mini_event_extension; - getdns_event_set(&e->ub_event, getdns_context_fd(context), - EV_READ, getdns_mini_event_cb, context); - (void) getdns_event_base_set(e->base, &e->ub_event); - (void) getdns_event_add(&e->ub_event, NULL); - } + context->ub_event.userarg = context; + context->ub_event.read_cb = getdns_context_ub_read_cb; + context->ub_event.timeout_cb = NULL; + return GETDNS_RETURN_GOOD; } @@ -1597,25 +1494,6 @@ getdns_context_cancel_request(struct getdns_context *context, return GETDNS_RETURN_GOOD; } -/* - * getdns_cancel_callback - * - */ -getdns_return_t -getdns_cancel_callback(struct getdns_context *context, - getdns_transaction_t transaction_id) -{ - RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER); - context->processing = 1; - getdns_return_t r = getdns_context_cancel_request(context, transaction_id, 1); - if (context->extension) { - context->extension->request_count_changed(context, - context->outbound_requests->count, context->extension_data); - } - context->processing = 0; - return r; -} /* getdns_cancel_callback */ - static getdns_return_t ub_setup_stub(struct ub_ctx *ctx, getdns_upstreams *upstreams) { @@ -1815,47 +1693,54 @@ getdns_context_prepare_for_resolution(struct getdns_context *context, getdns_return_t getdns_context_track_outbound_request(getdns_dns_req * req) { - if (!req) { - return GETDNS_RETURN_GENERIC_ERROR; - } - struct getdns_context *context = req->context; - ldns_rbnode_t *node = GETDNS_MALLOC(context->my_mf, 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 */ - GETDNS_FREE(context->my_mf, node); - return GETDNS_RETURN_GENERIC_ERROR; - } - if (context->extension) { - context->extension->request_count_changed(context, - context->outbound_requests->count, context->extension_data); - } - return GETDNS_RETURN_GOOD; + uint32_t prev_rc; + ldns_rbnode_t *node; + + if (!req) + return GETDNS_RETURN_INVALID_PARAMETER; + + prev_rc = req->context->outbound_requests->count; + + if (!(node = GETDNS_MALLOC(req->context->my_mf, ldns_rbnode_t))) + return GETDNS_RETURN_MEMORY_ERROR; + + node->key = &(req->trans_id); + node->data = req; + if (! ldns_rbtree_insert(req->context->outbound_requests, node)) { + GETDNS_FREE(req->context->my_mf, node); + return GETDNS_RETURN_GENERIC_ERROR; + } + getdns_context_request_count_changed(req->context, prev_rc); + return GETDNS_RETURN_GOOD; } getdns_return_t getdns_context_clear_outbound_request(getdns_dns_req * req) { - if (!req) { - return GETDNS_RETURN_GENERIC_ERROR; - } - struct getdns_context *context = req->context; - ldns_rbnode_t *node = ldns_rbtree_delete(context->outbound_requests, - &(req->trans_id)); - if (node) { - GETDNS_FREE(context->my_mf, node); - } - return GETDNS_RETURN_GOOD; + uint32_t prev_rc; + ldns_rbnode_t *node; + + if (!req) + return GETDNS_RETURN_INVALID_PARAMETER; + + prev_rc = req->context->outbound_requests->count; + + node = ldns_rbtree_delete( + req->context->outbound_requests, &req->trans_id); + if (!node) + return GETDNS_RETURN_GENERIC_ERROR; + + GETDNS_FREE(req->context->my_mf, node); + getdns_context_request_count_changed(req->context, prev_rc); + return GETDNS_RETURN_GOOD; } getdns_return_t getdns_context_request_timed_out(struct getdns_dns_req *req) { + /* Don't use req after callback */ getdns_context* context = req->context; + uint32_t prev_rc = context->outbound_requests->count; getdns_transaction_t trans_id = req->trans_id; getdns_callback_t cb = req->user_callback; void *user_arg = req->user_pointer; @@ -1866,10 +1751,7 @@ getdns_context_request_timed_out(struct getdns_dns_req context->processing = 1; cb(context, GETDNS_CALLBACK_TIMEOUT, response, user_arg, trans_id); context->processing = 0; - if (context->extension) { - context->extension->request_count_changed(context, - context->outbound_requests->count, context->extension_data); - } + getdns_context_request_count_changed(context, prev_rc); return GETDNS_RETURN_GOOD; } @@ -1927,17 +1809,13 @@ uint32_t getdns_context_get_num_pending_requests(struct getdns_context* context, struct timeval* next_timeout) { - static struct timeval dummy = { 0, 0 }; RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER); if (context->outbound_requests->count && - context->extension == (void *)&context->mini_event_extension.ext && - getdns_mini_event_settime(&context->mini_event_extension) == 0) + context->extension == &context->mini_event.loop) - getdns_handle_timeouts( - context->mini_event_extension.base, - context->mini_event_extension.base->time_tv, - next_timeout ? next_timeout : &dummy); + getdns_mini_event_handle_timeouts( + &context->mini_event, next_timeout); return context->outbound_requests->count; } @@ -1945,7 +1823,6 @@ getdns_context_get_num_pending_requests(struct getdns_context* context, /* process async reqs */ getdns_return_t getdns_context_process_async(struct getdns_context* context) { - struct timeval immediately = { 0, 0 }; RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER); context->processing = 1; @@ -1954,9 +1831,8 @@ getdns_return_t getdns_context_process_async(struct getdns_context* context) context->processing = 0; return GETDNS_RETURN_GENERIC_ERROR; } - if (context->extension == (void *)&context->mini_event_extension.ext - && getdns_handle_select(context->mini_event_extension.base, - &immediately)) { + if (context->extension == &context->mini_event.loop + && getdns_mini_event_handle_select(&context->mini_event, NULL)) { context->processing = 0; return GETDNS_RETURN_GENERIC_ERROR; @@ -1994,7 +1870,7 @@ cancel_outstanding_requests(struct getdns_context* context, int fire_callback) { } getdns_return_t -getdns_extension_detach_eventloop(struct getdns_context* context) +getdns_context_detach_eventloop(struct getdns_context* context) { getdns_return_t r = GETDNS_RETURN_GOOD; @@ -2010,73 +1886,64 @@ getdns_extension_detach_eventloop(struct getdns_context* context) context->processing = 1; /* cancel all outstanding requests */ cancel_outstanding_requests(context, 1); - r = context->extension->cleanup_data(context, - context->extension_data); + r = context->extension->functions->cleanup(context->extension); if (r == GETDNS_RETURN_GOOD) { - context->extension = &context->mini_event_extension.ext; - context->extension_data =(void*)&context->mini_event_extension; - r = getdns_mini_event_extension_init( - &context->mini_event_extension); + context->extension = &context->mini_event.loop; + r = getdns_mini_event_init(context, &context->mini_event); } context->processing = 0; return r; } getdns_return_t -getdns_extension_set_eventloop(struct getdns_context* context, - getdns_eventloop_extension* extension, void* extension_data) +getdns_context_set_eventloop(struct getdns_context* context, getdns_eventloop* loop) { - RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER); - RETURN_IF_NULL(extension, GETDNS_RETURN_INVALID_PARAMETER); - getdns_return_t r = getdns_extension_detach_eventloop(context); - if (r != GETDNS_RETURN_GOOD) { - return r; - } - context->extension = extension; - context->extension_data = extension_data; - return GETDNS_RETURN_GOOD; + RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER); + RETURN_IF_NULL(loop , GETDNS_RETURN_INVALID_PARAMETER); + + getdns_return_t r = getdns_context_detach_eventloop(context); + if (r != GETDNS_RETURN_GOOD) + return r; + + context->extension = loop; + return GETDNS_RETURN_GOOD; } getdns_return_t -getdns_context_schedule_timeout(getdns_context* context, uint64_t timeout, - getdns_timeout_callback callback, void* userarg, - getdns_timeout_data_t *timeout_data) +getdns_context_schedule_timeout(getdns_context *context, uint64_t timeout, + getdns_eventloop_callback callback, void *userarg, + getdns_eventloop_event *el_ev) { - RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER); + RETURN_IF_NULL(context , GETDNS_RETURN_INVALID_PARAMETER); RETURN_IF_NULL(callback, GETDNS_RETURN_INVALID_PARAMETER); - RETURN_IF_NULL(timeout_data, GETDNS_RETURN_INVALID_PARAMETER); + RETURN_IF_NULL(el_ev , GETDNS_RETURN_INVALID_PARAMETER); - /* Initialize timeout_data struct */ - timeout_data->context = context; - timeout_data->callback = callback; - timeout_data->userarg = userarg; - timeout_data->extension_timer = NULL; + /* Initialize eev_data struct */ + el_ev->userarg = userarg; + el_ev->read_cb = NULL; + el_ev->timeout_cb = callback; + el_ev->ev = NULL; - return context->extension->schedule_timeout(context, - context->extension_data, timeout, timeout_data); + return context->extension->functions->schedule_timeout( + context->extension, timeout, el_ev); } getdns_return_t getdns_context_clear_timeout(getdns_context* context, - getdns_timeout_data_t *timeout_data) + getdns_eventloop_event *el_ev) { RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER); - RETURN_IF_NULL(timeout_data, GETDNS_RETURN_INVALID_PARAMETER); - RETURN_IF_NULL(timeout_data->context, GETDNS_RETURN_GOOD); + RETURN_IF_NULL(el_ev, GETDNS_RETURN_INVALID_PARAMETER); + RETURN_IF_NULL(el_ev->timeout_cb, GETDNS_RETURN_GOOD); - context->extension->clear_timeout(context, - context->extension_data, timeout_data->extension_timer); - timeout_data->context = NULL; - + if (el_ev->timeout_cb) { + context->extension->functions->clear_timeout( + context->extension, el_ev); + el_ev->timeout_cb = NULL; + } return GETDNS_RETURN_GOOD; } -void* -getdns_context_get_extension_data(struct getdns_context* context) { - RETURN_IF_NULL(context, NULL); - return context->extension_data; -} - static inline getdns_return_t priv_dict_set_list_if_not_null(getdns_dict* dict, const char* name, getdns_list* list) { diff --git a/src/context.h b/src/context.h index 627b4354..6eb2ca1d 100755 --- a/src/context.h +++ b/src/context.h @@ -41,7 +41,7 @@ #include "getdns/getdns_extra.h" #include "config.h" #include "types-internal.h" -#include "util/mini_event.h" +#include "extension/libmini_event.h" struct getdns_dns_req; struct ldns_rbtree_t; @@ -82,14 +82,6 @@ typedef struct getdns_upstreams { struct getdns_upstream upstreams[]; } getdns_upstreams; -typedef struct getdns_mini_event_extention { - getdns_eventloop_extension ext; - time_t time_secs; - struct timeval time_tv; - struct getdns_event_base *base; - struct getdns_event ub_event; -} getdns_mini_event_extension; - struct getdns_context { /* Context values */ getdns_resolution_t resolution_type; @@ -137,20 +129,12 @@ struct getdns_context { */ struct ldns_rbtree_t *outbound_requests; - /* - * Event loop extension functions - * These structs are static and should never be freed - * since they are just a collection of function pointers - */ - getdns_eventloop_extension* extension; - /* - * Extension data that will be freed by the functions - * in the extension struct - */ - void* extension_data; + /* Event loop extension. */ + getdns_eventloop *extension; + getdns_eventloop_event ub_event; /* The default extension */ - getdns_mini_event_extension mini_event_extension; + getdns_mini_event mini_event; /* * state data used to detect changes to the system config files @@ -197,11 +181,11 @@ void getdns_bindata_destroy( /* timeout scheduling */ getdns_return_t getdns_context_schedule_timeout(getdns_context* context, - uint64_t timeout, getdns_timeout_callback callback, void* userarg, - getdns_timeout_data_t *init_to_track); + uint64_t timeout, getdns_eventloop_callback callback, void* userarg, + getdns_eventloop_event *init_to_track); getdns_return_t getdns_context_clear_timeout(getdns_context* context, - getdns_timeout_data_t *timeout_data); + getdns_eventloop_event *timeout_data); /* perform name resolution in /etc/hosts */ getdns_return_t getdns_context_local_namespace_resolve(getdns_dns_req* req, diff --git a/src/extension/libmini_event.c b/src/extension/libmini_event.c new file mode 100755 index 00000000..e82d9107 --- /dev/null +++ b/src/extension/libmini_event.c @@ -0,0 +1,196 @@ +/** + * + * \file libmini_event.c + * @brief Build in default eventloop extension that uses select. + * + */ + +/* + * Copyright (c) 2013, NLnet Labs, Verisign, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the names of the copyright holders nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Verisign, Inc. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "extension/libmini_event.h" +#include "util/mini_event.h" +#include "context.h" + +void getdns_handle_timeouts(struct getdns_event_base* base, + struct timeval* now, struct timeval* wait); +int getdns_handle_select(struct getdns_event_base* base, struct timeval* wait); + +static getdns_return_t getdns_mini_event_cleanup(getdns_eventloop *loop); +static getdns_return_t getdns_mini_event_schedule_read(getdns_eventloop *loop, + int fd, uint64_t timeout, getdns_eventloop_event *ev); +static getdns_return_t getdns_mini_event_schedule_timeout + (getdns_eventloop *loop, uint64_t timeout, getdns_eventloop_event *ev); +static getdns_return_t getdns_mini_event_clear_event + (getdns_eventloop *loop, getdns_eventloop_event *ev); + +static getdns_eventloop_functions getdns_mini_event_functions = { + getdns_mini_event_cleanup, + getdns_mini_event_schedule_read, + getdns_mini_event_clear_event, + getdns_mini_event_schedule_timeout, + getdns_mini_event_clear_event, +}; + +getdns_return_t +getdns_mini_event_init(getdns_context *context, getdns_mini_event *ext) +{ + if (!context) return GETDNS_RETURN_BAD_CONTEXT; + if (!ext) return GETDNS_RETURN_INVALID_PARAMETER; + + ext->loop.functions = &getdns_mini_event_functions; + ext->base = getdns_event_init(&ext->time_secs, &ext->time_tv); + if (!ext->base) + return GETDNS_RETURN_MEMORY_ERROR; + + ext->mf = context->mf; + return GETDNS_RETURN_GOOD; +} + +getdns_return_t +getdns_mini_event_create(getdns_context *context, getdns_mini_event **ext) +{ + if (!context) return GETDNS_RETURN_BAD_CONTEXT; + if (!ext) return GETDNS_RETURN_INVALID_PARAMETER; + + *ext = GETDNS_MALLOC(context->mf, getdns_mini_event); + return getdns_mini_event_init(context, *ext); +} + +void +getdns_mini_event_destroy(getdns_mini_event *ext) +{ + if (ext) { + ext->loop.functions->cleanup(&ext->loop); + GETDNS_FREE(ext->mf, ext); + } +} + +static getdns_return_t +getdns_mini_event_cleanup(getdns_eventloop *loop) +{ + getdns_mini_event *ext = (getdns_mini_event *)loop; + + getdns_event_base_free(ext->base); + return GETDNS_RETURN_GOOD; +} + +static int +getdns_mini_event_settime(getdns_mini_event *ext) +{ + if (gettimeofday(&ext->time_tv, NULL) < 0) + return -1; + ext->time_secs = (time_t)ext->time_tv.tv_sec; + return 0; +} + +/** Call timeouts handlers, and return how long to wait for next one or -1 */ +void +getdns_mini_event_handle_timeouts(getdns_mini_event *ext, struct timeval *wait) +{ + struct timeval dispose = { 0, 0 }; + + if (getdns_mini_event_settime(ext) == 0) + getdns_handle_timeouts( + ext->base, &ext->time_tv, wait ? wait : &dispose); +} + +/** Call select and callbacks for that */ +getdns_return_t +getdns_mini_event_handle_select(getdns_mini_event *ext, struct timeval* wait) +{ + static struct timeval immediately = { 0, 0 }; + + return getdns_handle_select(ext->base, wait ? wait : &immediately) + ? GETDNS_RETURN_GENERIC_ERROR : GETDNS_RETURN_GOOD; +} + +static void +getdns_mini_event_callback(int fd, short bits, void *arg) +{ + getdns_eventloop_event *el_ev = (getdns_eventloop_event *)arg; + if (bits & EV_READ) { + assert(el_ev->read_cb); + el_ev->read_cb(el_ev->userarg); + } else if (bits & EV_TIMEOUT) { + assert(el_ev->timeout_cb); + el_ev->timeout_cb(el_ev->userarg); + } else + assert(ASSERT_UNREACHABLE); +} + +static getdns_return_t +getdns_mini_event_schedule_read(getdns_eventloop *loop, + int fd, uint64_t timeout, getdns_eventloop_event *el_ev) +{ + getdns_mini_event *ext = (getdns_mini_event *)loop; + struct getdns_event *my_ev; + struct timeval tv = { timeout / 1000, (timeout % 1000) * 1000 }; + short bits = + ((fd >= 0 && el_ev->read_cb ? EV_READ : 0) | + (timeout!=TIMEOUT_FOREVER && el_ev->timeout_cb ? EV_TIMEOUT : 0)); + + if (!bits) + return GETDNS_RETURN_GOOD; /* Nothing to schedule */ + + my_ev = GETDNS_MALLOC(ext->mf, struct getdns_event); + el_ev->ev = my_ev; + getdns_event_set(my_ev, fd, bits, getdns_mini_event_callback, el_ev); + + if (getdns_mini_event_settime(ext)) + return GETDNS_RETURN_GENERIC_ERROR; + (void) getdns_event_base_set(ext->base, my_ev); + if (getdns_event_add(my_ev, &tv)) + return GETDNS_RETURN_GENERIC_ERROR; + + return GETDNS_RETURN_GOOD; +} + +static getdns_return_t +getdns_mini_event_schedule_timeout(getdns_eventloop *loop, + uint64_t timeout, getdns_eventloop_event *el_ev) +{ + return getdns_mini_event_schedule_read(loop, -1, timeout, el_ev); +} + +static getdns_return_t +getdns_mini_event_clear_event(getdns_eventloop *loop, + getdns_eventloop_event *el_ev) +{ + getdns_mini_event *ext = (getdns_mini_event *)loop; + + assert(el_ev->ev); + + if (getdns_event_del(el_ev->ev) != 0) + return GETDNS_RETURN_GENERIC_ERROR; + GETDNS_FREE(ext->mf, el_ev->ev); + el_ev->ev = NULL; + return GETDNS_RETURN_GOOD; +} + +/* libmini_event.c */ diff --git a/src/extension/libmini_event.h b/src/extension/libmini_event.h new file mode 100755 index 00000000..48580d28 --- /dev/null +++ b/src/extension/libmini_event.h @@ -0,0 +1,65 @@ +/** + * + * \file libmini_event.h + * @brief Build in default eventloop extension that uses select. + * + */ +/* + * Copyright (c) 2013, NLnet Labs, Verisign, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the names of the copyright holders nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Verisign, Inc. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _GETDNS_LIBMINI_EVENT_H_ +#define _GETDNS_LIBMINI_EVENT_H_ + +#include "util/mini_event.h" +#include "types-internal.h" + +typedef struct getdns_mini_event { + getdns_eventloop loop; + time_t time_secs; + struct timeval time_tv; + struct getdns_event_base *base; + struct mem_funcs mf; +} getdns_mini_event; + +getdns_return_t +getdns_mini_event_init(getdns_context *context, getdns_mini_event *mini_event); + +getdns_return_t +getdns_mini_event_create(getdns_context *ctxt, getdns_mini_event **mini_event); + +void +getdns_mini_event_destroy(getdns_mini_event *mini_event); + +/** Call timeouts handlers, and return how long to wait for next one or -1 */ +void +getdns_mini_event_handle_timeouts(getdns_mini_event *ext, struct timeval *wait); + +/** Call select and callbacks for that */ +getdns_return_t +getdns_mini_event_handle_select(getdns_mini_event *ext, struct timeval* wait); + +#endif /* _GETDNS_LIBMINI_EVENT_H_ */ diff --git a/src/general.c b/src/general.c index 8869221e..69669856 100644 --- a/src/general.c +++ b/src/general.c @@ -50,7 +50,7 @@ /* declarations */ static void ub_resolve_callback(void* mydata, int err, struct ub_result* result); -static getdns_return_t ub_resolve_timeout(void *arg); +static void ub_resolve_timeout(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); @@ -64,11 +64,11 @@ typedef struct netreq_cb_data } netreq_cb_data; /* cancel, cleanup and send timeout to callback */ -static getdns_return_t +static void ub_resolve_timeout(void *arg) { getdns_dns_req *dns_req = (getdns_dns_req *) arg; - return getdns_context_request_timed_out(dns_req); + (void) getdns_context_request_timed_out(dns_req); } void priv_getdns_call_user_callback(getdns_dns_req *dns_req, diff --git a/src/getdns/getdns_extra.h b/src/getdns/getdns_extra.h index 9c5d0c8a..34387174 100644 --- a/src/getdns/getdns_extra.h +++ b/src/getdns/getdns_extra.h @@ -39,85 +39,105 @@ extern "C" { value is either GETDNS_EXTENSION_TRUE or GETDNS_EXTENSION_FALSE returns GETDNS_RETURN_GOOD on success or GETDNS_RETURN_INVALID_PARAMETER if context or value is invalid */ -getdns_return_t getdns_context_set_return_dnssec_status(getdns_context* context, int enabled); +getdns_return_t getdns_context_set_return_dnssec_status( + getdns_context *context, int enabled); /* dict util */ /* set a string as bindata */ -getdns_return_t getdns_dict_util_set_string(struct getdns_dict * dict, char *name, - const char *value); +getdns_return_t getdns_dict_util_set_string(struct getdns_dict * dict, + char *name, const char *value); /* get a string from a dict. the result must be freed if valid */ -getdns_return_t getdns_dict_util_get_string(struct getdns_dict * dict, char *name, - char **result); - -/* Async support */ -uint32_t getdns_context_get_num_pending_requests(getdns_context* context, struct timeval* next_timeout); +getdns_return_t getdns_dict_util_get_string(struct getdns_dict * dict, + char *name, char **result); /* get the fd */ int getdns_context_fd(getdns_context* context); +/* tells underlying unbound to use background threads or fork */ +getdns_return_t getdns_context_set_use_threads(getdns_context* context, + int use_threads); + +/* Async support */ +uint32_t getdns_context_get_num_pending_requests(getdns_context* context, + struct timeval* next_timeout); + /* process async reqs */ getdns_return_t getdns_context_process_async(getdns_context* context); -/* tells underlying unbound to use background threads or fork */ -getdns_return_t getdns_context_set_use_threads(getdns_context* context, int use_threads); +/***************** functions for eventloop extensions ******************/ -/* extensions */ -typedef getdns_return_t (*getdns_timeout_callback) (void* userarg); +typedef void (*getdns_eventloop_callback)(void *userarg); -/* context timeout data */ -typedef struct getdns_timeout_data { - /* the timeout callback to fire */ - getdns_timeout_callback callback; - /* timeout callback user arg */ - void* userarg; - /* pointer to the underlying extension pointer that the extension - will create and free */ - void* extension_timer; - /* context */ - struct getdns_context* context; -} getdns_timeout_data_t; +/* context extension event data */ +typedef struct getdns_eventloop_event { + void *userarg; + getdns_eventloop_callback read_cb; + getdns_eventloop_callback timeout_cb; -/* call the extension when the data needs to be cleaned up */ -typedef getdns_return_t (*getdns_eventloop_cleanup_t)(struct getdns_context* context, void* eventloop_data); + /* Pointer to the underlying event + * that the eventloop extension will create and free. + */ + void *ev; +} getdns_eventloop_event; -/* call the extension to schedule a timer. Any timer data that needs to be tracked should be - stored in eventloop_timer */ -typedef getdns_return_t (*getdns_eventloop_schedule_timeout_t)(struct getdns_context* context, - void* eventloop_data, uint64_t timeout, - getdns_timeout_data_t* timeout_data); +typedef struct getdns_eventloop_functions getdns_eventloop_functions; +typedef struct getdns_eventloop { + getdns_eventloop_functions *functions; +} getdns_eventloop; -/* call the extension to free a timer. The timer passed in is the same as that returned in - the schedule timeout */ -typedef getdns_return_t (*getdns_eventloop_clear_timeout_t)(struct getdns_context* context, - void* eventloop_data, void* eventloop_timer); +/* Call the extension to clean up data allocated on initialization. */ +typedef getdns_return_t (*getdns_eventloop_cleanup)(getdns_eventloop *loop); -/* call the extension to tell it that the number of outbound requests changed. This is called - when an async request is submitted or canceled by the user */ -typedef getdns_return_t (*getdns_eventloop_request_count_changed_t)(struct getdns_context* context, - uint32_t request_count, void* eventloop_data); +/* Call the extension to schedule an event that will trigger when + * file descriptor fd will become readble. + * + * The getdns_eventloop_event must be provided by the caller with the callbacks + * and userarg therein already supplied (by the caller). This function must set + * the ev pointer (in the getdns_eventloop_event) to refer to the underlying + * (extension) event. + */ +typedef getdns_return_t (*getdns_eventloop_schedule_read)(getdns_eventloop *loop, + int fd, uint64_t timeout, getdns_eventloop_event *ev); -typedef struct getdns_eventloop_extension { - getdns_eventloop_cleanup_t cleanup_data; - getdns_eventloop_schedule_timeout_t schedule_timeout; - getdns_eventloop_clear_timeout_t clear_timeout; - getdns_eventloop_request_count_changed_t request_count_changed; -} getdns_eventloop_extension; +/* Call the extension to free a read event. */ +typedef getdns_return_t (*getdns_eventloop_clear_read) + (getdns_eventloop *loop, getdns_eventloop_event *ev); + +/* Call the extension to schedule a timer. + * + * The getdns_eventloop_event must be provided by the caller with the timeout + * callback and userarg therein already supplied (by the caller). + * This function must set the ev pointer (in the getdns_eventloop_event) + * to refer to the underlying (extension) event. + */ +typedef getdns_return_t (*getdns_eventloop_schedule_timeout) + (getdns_eventloop *loop, uint64_t timeout, getdns_eventloop_event *ev); + +/* Call the extension to free a timer. */ +typedef getdns_return_t (*getdns_eventloop_clear_timeout) + (getdns_eventloop *loop, getdns_eventloop_event *ev); + +struct getdns_eventloop_functions { + getdns_eventloop_cleanup cleanup; + getdns_eventloop_schedule_read schedule_read; + getdns_eventloop_clear_read clear_read; + getdns_eventloop_schedule_timeout schedule_timeout; + getdns_eventloop_clear_timeout clear_timeout; +}; /* set an event loop extension on the context */ getdns_return_t -getdns_extension_set_eventloop(struct getdns_context* context, - getdns_eventloop_extension* extension, void* extension_data); - -void* -getdns_context_get_extension_data(struct getdns_context* context); +getdns_context_set_eventloop(getdns_context* context, + getdns_eventloop *eventloop); /* detach the eventloop from the context */ getdns_return_t -getdns_extension_detach_eventloop(struct getdns_context* context); +getdns_context_detach_eventloop(getdns_context *context); #ifdef __cplusplus } #endif #endif + diff --git a/src/request-internal.c b/src/request-internal.c index 03ce8680..0b2156b5 100644 --- a/src/request-internal.c +++ b/src/request-internal.c @@ -130,7 +130,7 @@ dns_req_new(struct getdns_context *context, /* will be set by caller */ result->user_pointer = NULL; result->user_callback = NULL; - memset(&result->timeout, 0, sizeof(getdns_timeout_data_t)); + memset(&result->timeout, 0, sizeof(result->timeout)); /* check the specify_class extension */ (void) getdns_dict_get_int(extensions, "specify_class", &klass); diff --git a/src/types-internal.h b/src/types-internal.h index 497a488b..a7d17565 100644 --- a/src/types-internal.h +++ b/src/types-internal.h @@ -93,6 +93,8 @@ struct getdns_context; #define GETDNS_STR_KEY_NSCOUNT "nscount" #define GETDNS_STR_KEY_ARCOUNT "arcount" +#define TIMEOUT_FOREVER ((int64_t)-1) +#define ASSERT_UNREACHABLE 0 /** @} */ @@ -198,7 +200,7 @@ typedef struct getdns_dns_req /* the transaction id */ getdns_transaction_t trans_id; - getdns_timeout_data_t timeout; + getdns_eventloop_event timeout; /* dnssec status */ int return_dnssec_status;