timed synchronous resolves

Also returns an response dict with status GETDNS_RESPSTATUS_ALL_TIMEOUT on timeout
This commit is contained in:
Willem Toorop 2014-07-01 23:31:40 +02:00
parent a0c2c05811
commit fc2f091f05
7 changed files with 232 additions and 35 deletions

View File

@ -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

View File

@ -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;
}

View File

@ -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);

View File

@ -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;

125
src/ub_timed_resolve.c Normal file
View File

@ -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 <sys/select.h>
#include <sys/time.h>
#include <assert.h>
#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 */

44
src/ub_timed_resolve.h Normal file
View File

@ -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 <unbound.h>
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 */

View File

@ -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);