From fc2f091f0529ed884b59355debe25246dfc66d2e Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Tue, 1 Jul 2014 23:31:40 +0200 Subject: [PATCH] timed synchronous resolves Also returns an response dict with status GETDNS_RESPSTATUS_ALL_TIMEOUT on timeout --- src/Makefile.in | 2 +- src/dnssec.c | 24 ++++---- src/dnssec.h | 2 +- src/sync.c | 30 ++++++---- src/ub_timed_resolve.c | 125 +++++++++++++++++++++++++++++++++++++++++ src/ub_timed_resolve.h | 44 +++++++++++++++ src/util-internal.c | 40 +++++++++---- 7 files changed, 232 insertions(+), 35 deletions(-) create mode 100644 src/ub_timed_resolve.c create mode 100644 src/ub_timed_resolve.h diff --git a/src/Makefile.in b/src/Makefile.in index fd05c53c..ccd77397 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -77,7 +77,7 @@ 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 + getdns_error.lo rr-dict.lo dnssec.lo const-info.lo ub_timed_resolve.lo .SUFFIXES: .c .o .a .lo .h diff --git a/src/dnssec.c b/src/dnssec.c index 5006caf5..f92238fc 100644 --- a/src/dnssec.c +++ b/src/dnssec.c @@ -47,6 +47,7 @@ #include "types-internal.h" #include "dnssec.h" #include "rr-dict.h" +#include "ub_timed_resolve.h" void priv_getdns_call_user_callback(getdns_dns_req *, struct getdns_dict *); @@ -56,6 +57,7 @@ struct validation_chain { getdns_dns_req *dns_req; size_t lock; struct getdns_dict **sync_response; + uint64_t *timeout; }; struct chain_response { @@ -200,8 +202,8 @@ resolve(char* name, int rrtype, struct chain_response *response) if (response->chain->sync_response) { ub_res = NULL; - r = ub_resolve(response->chain->dns_req->context->unbound_ctx, - name, rrtype, LDNS_RR_CLASS_IN, &ub_res); + r = ub_timed_resolve(response->chain->dns_req->context->unbound_ctx, + name, rrtype, LDNS_RR_CLASS_IN, &ub_res, response->chain->timeout); ub_chain_response_callback(response, r, ub_res); return r; } else @@ -243,8 +245,8 @@ launch_chain_link_lookup(struct validation_chain *chain, char *name) chain->lock--; } -static struct validation_chain *create_chain( - getdns_dns_req *dns_req, struct getdns_dict **sync_response) +static struct validation_chain *create_chain(getdns_dns_req *dns_req, + struct getdns_dict **sync_response, uint64_t *timeout) { struct validation_chain *chain = GETDNS_MALLOC( dns_req->context->mf, struct validation_chain); @@ -261,6 +263,7 @@ static struct validation_chain *create_chain( chain->dns_req = dns_req; chain->lock = 0; chain->sync_response = sync_response; + chain->timeout = timeout; return chain; } @@ -284,11 +287,12 @@ static void destroy_chain(struct validation_chain *chain) /* Do some additional requests to fetch the complete validation chain */ static void -getdns_get_validation_chain( - getdns_dns_req *dns_req, struct getdns_dict **sync_response) +getdns_get_validation_chain(getdns_dns_req *dns_req, + struct getdns_dict **sync_response, uint64_t *timeout) { getdns_network_req *netreq = dns_req->first_req; - struct validation_chain *chain = create_chain(dns_req, sync_response); + struct validation_chain *chain = create_chain( + dns_req, sync_response, timeout); if (! chain) { if (sync_response) @@ -315,14 +319,14 @@ getdns_get_validation_chain( void priv_getdns_get_validation_chain(getdns_dns_req *dns_req) { - getdns_get_validation_chain(dns_req, NULL); + getdns_get_validation_chain(dns_req, NULL, NULL); } struct getdns_dict * -priv_getdns_get_validation_chain_sync(getdns_dns_req *dns_req) +priv_getdns_get_validation_chain_sync(getdns_dns_req *dns_req, uint64_t *timeout) { struct getdns_dict *sync_response = NULL; - getdns_get_validation_chain(dns_req, &sync_response); + getdns_get_validation_chain(dns_req, &sync_response, timeout); return sync_response; } diff --git a/src/dnssec.h b/src/dnssec.h index e71030ab..42fdc12b 100644 --- a/src/dnssec.h +++ b/src/dnssec.h @@ -45,7 +45,7 @@ void priv_getdns_get_validation_chain(getdns_dns_req *dns_req); struct getdns_dict * priv_getdns_get_validation_chain_sync( - getdns_dns_req *dns_req); + getdns_dns_req *dns_req, uint64_t *timeout); int priv_getdns_parse_ta_file(time_t *ta_mtime, ldns_rr_list *ta_rrs); diff --git a/src/sync.c b/src/sync.c index 10c0dcad..f5c49328 100644 --- a/src/sync.c +++ b/src/sync.c @@ -42,30 +42,35 @@ #include "types-internal.h" #include "util-internal.h" #include "dnssec.h" +#include "ub_timed_resolve.h" /* stuff to make it compile pedantically */ #define UNUSED_PARAM(x) ((void)(x)) #define RETURN_IF_NULL(ptr, code) if(ptr == NULL) return code; -getdns_return_t submit_request_sync(getdns_dns_req* req) { +static getdns_return_t submit_request_sync( + getdns_dns_req* req, uint64_t *timeout) +{ struct ub_result* ub_res = NULL; getdns_return_t gr = GETDNS_RETURN_GOOD; getdns_network_req *netreq = req->first_req; + while (netreq) { - int r = ub_resolve(req->context->unbound_ctx, + int r = ub_timed_resolve(req->context->unbound_ctx, req->name, netreq->request_type, netreq->request_class, - &ub_res); - if (r != 0) { - return GETDNS_RETURN_GENERIC_ERROR; - } + &ub_res, + timeout); gr = getdns_apply_network_result(netreq, ub_res); ub_resolve_free(ub_res); ub_res = NULL; - if (gr != GETDNS_RETURN_GOOD) { + + if (r != GETDNS_RETURN_GOOD) + return r; + else if (gr != GETDNS_RETURN_GOOD) return gr; - } + netreq = netreq->next; } return gr; @@ -80,6 +85,7 @@ getdns_general_sync(struct getdns_context *context, { getdns_dns_req *req; getdns_return_t response_status; + uint64_t timeout = context->timeout; RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER); RETURN_IF_NULL(response, GETDNS_RETURN_INVALID_PARAMETER); @@ -104,14 +110,16 @@ getdns_general_sync(struct getdns_context *context, if (!req) return GETDNS_RETURN_MEMORY_ERROR; - response_status = submit_request_sync(req); + response_status = submit_request_sync(req, &timeout); if (response_status == GETDNS_RETURN_GOOD) { if (is_extension_set(req->extensions, "dnssec_return_validation_chain")) - *response = priv_getdns_get_validation_chain_sync(req); + *response = priv_getdns_get_validation_chain_sync(req, &timeout); else *response = create_getdns_response(req); - } + + } else if (response_status == GETDNS_RESPSTATUS_ALL_TIMEOUT) + *response = create_getdns_response(req); dns_req_free(req); return response_status; diff --git a/src/ub_timed_resolve.c b/src/ub_timed_resolve.c new file mode 100644 index 00000000..c6e97876 --- /dev/null +++ b/src/ub_timed_resolve.c @@ -0,0 +1,125 @@ +/** + * + * /brief A timed synchronous unbound resolve function + * + */ +/* + * Copyright (c) 2014, 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 +#include +#include +#include "ub_timed_resolve.h" + +static struct ub_result error_result; + +static void cb_timed_resolve(void *my_arg, int err, struct ub_result *result) +{ + struct ub_result **to_return = (struct ub_result **)my_arg; + *to_return = err ? &error_result : result; +} + +int ub_timed_resolve(struct ub_ctx* ctx, char* name, + int rrtype, int rrclass, struct ub_result** result, uint64_t *timeout) +{ + fd_set rfds; + struct timeval tv, now, prev; + int r; + int ubfd; + int async_id; + uint64_t elapsed; + + assert(ctx != NULL); + assert(name != NULL); + assert(result != NULL); + assert(timeout != NULL); + + *result = NULL; + if (ub_resolve_async(ctx, name, rrtype, rrclass, + result, cb_timed_resolve, &async_id)) + return GETDNS_RETURN_GENERIC_ERROR; + + if (*result == &error_result) { + *result = NULL; + return GETDNS_RETURN_GENERIC_ERROR; + + } else if (*result) + return GETDNS_RETURN_GOOD; /* result came from cache */ + + ubfd = ub_fd(ctx); + + FD_ZERO(&rfds); + FD_SET(ubfd, &rfds); + + if (gettimeofday(&now, NULL) < 0) { + ub_cancel(ctx, async_id); + return GETDNS_RETURN_GENERIC_ERROR; + } + for (;;) { + /* timeout is in miliseconds, so map to seconds and microseconds */ + tv.tv_sec = *timeout / 1000; + tv.tv_usec = (*timeout % 1000) * 1000; + + r = select(ubfd + 1, &rfds, NULL, NULL, &tv); + if (r <= 0) + ub_cancel(ctx, async_id); + if (r < 0) + return GETDNS_RETURN_GENERIC_ERROR; + else if (r == 0) + return GETDNS_RESPSTATUS_ALL_TIMEOUT; + + prev = now; + if (gettimeofday(&now, NULL) < 0) { + ub_cancel(ctx, async_id); + return GETDNS_RETURN_GENERIC_ERROR; + } + elapsed = now.tv_sec * 1000 + now.tv_usec / 1000; + elapsed -= prev.tv_sec * 1000 + prev.tv_usec / 1000; + if (elapsed > *timeout) { + *timeout = 0; + ub_cancel(ctx, async_id); + return GETDNS_RESPSTATUS_ALL_TIMEOUT; + } + *timeout -= elapsed; + + /* We have readiness */ + if (! ub_poll(ctx)) + continue; + if (ub_process(ctx)) { + ub_cancel(ctx, async_id); + return GETDNS_RETURN_GENERIC_ERROR; + } + if (*result == &error_result) { + *result = NULL; + return GETDNS_RETURN_GENERIC_ERROR; + + } else if (*result) + return GETDNS_RETURN_GOOD; /* result came from cache */ + } +} + +/* ub_timed_resolve.c */ diff --git a/src/ub_timed_resolve.h b/src/ub_timed_resolve.h new file mode 100644 index 00000000..02767374 --- /dev/null +++ b/src/ub_timed_resolve.h @@ -0,0 +1,44 @@ +/** + * + * /brief A timed synchronous unbound resolve function + * + */ +/* + * Copyright (c) 2014, 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 UB_TIMED_RESOLVE_H_ +#define UB_TIMED_RESOLVE_H_ + +#include "getdns/getdns.h" +#include + +int ub_timed_resolve(struct ub_ctx *ctx, char *name, + int rrtype, int rrclass, struct ub_result **result, uint64_t *timeout); + +#endif + +/* ub_timed_resolve.h */ diff --git a/src/util-internal.c b/src/util-internal.c index 1d92bc70..e7e3ef83 100644 --- a/src/util-internal.c +++ b/src/util-internal.c @@ -556,9 +556,10 @@ create_getdns_response(struct getdns_dns_req * completed_request) completed_request->extensions, "dnssec_return_status") || completed_request->return_dnssec_status == GETDNS_EXTENSION_TRUE; - if (completed_request->first_req->request_class == GETDNS_RRTYPE_A || + if (completed_request->first_req && + (completed_request->first_req->request_class == GETDNS_RRTYPE_A || completed_request->first_req->request_class == - GETDNS_RRTYPE_AAAA) { + GETDNS_RRTYPE_AAAA)) { just_addrs = getdns_list_create_with_context( completed_request->context); } @@ -581,6 +582,9 @@ create_getdns_response(struct getdns_dns_req * completed_request) ; netreq && r == GETDNS_RETURN_GOOD ; netreq = netreq->next ) { + if (! netreq->result) + continue; + nreplies++; if (netreq->secure) nsecure++; @@ -771,16 +775,28 @@ validate_extensions(struct getdns_dict * extensions) getdns_return_t getdns_apply_network_result(getdns_network_req* netreq, struct ub_result* ub_res) { - if (ub_res->answer_packet == NULL) { - /* Likely to be because libunbound refused the request - * so ub_res->answer_packet=NULL, ub_res->answer_len=0 - * So we need to create an answer packet. - */ - netreq->result = ldns_pkt_query_new( - ldns_rdf_new_frm_str(LDNS_RDF_TYPE_DNAME, netreq->owner->name), - netreq->request_type, - netreq->request_class,LDNS_QR|LDNS_RD|LDNS_RA); - ldns_pkt_set_rcode(netreq->result, ub_res->rcode); + + if (ub_res == NULL) { /* Timeout */ + netreq->result = NULL; + return GETDNS_RETURN_GOOD; + + } else if (ub_res->answer_packet == NULL) { + if (ub_res->rcode == GETDNS_RCODE_SERVFAIL) { + /* Likely to be caused by timeout from a synchronous + * lookup. Don't forge a packet. + */ + netreq->result = NULL; + } else { + /* Likely to be because libunbound refused the request + * so ub_res->answer_packet=NULL, ub_res->answer_len=0 + * So we need to create an answer packet. + */ + netreq->result = ldns_pkt_query_new( + ldns_rdf_new_frm_str(LDNS_RDF_TYPE_DNAME, netreq->owner->name), + netreq->request_type, + netreq->request_class,LDNS_QR|LDNS_RD|LDNS_RA); + ldns_pkt_set_rcode(netreq->result, ub_res->rcode); + } } else { ldns_status r = ldns_wire2pkt(&(netreq->result), ub_res->answer_packet, ub_res->answer_len);