Merge pull request #266 from wtoorop/features/limit_outstanding_queries

Features/limit outstanding queries
This commit is contained in:
wtoorop 2017-03-20 16:27:33 +01:00 committed by GitHub
commit b8c5f9131f
26 changed files with 2802 additions and 58 deletions

View File

@ -1267,6 +1267,26 @@ NULL_update_callback(
getdns_context *context, getdns_context_code_t code, void *userarg)
{ (void)context; (void)code; (void)userarg; }
static int
netreq_expiry_cmp(const void *id1, const void *id2)
{
getdns_network_req *req1 = (getdns_network_req *)id1;
getdns_network_req *req2 = (getdns_network_req *)id2;
return req1->owner->expires < req2->owner->expires ? -1 :
req1->owner->expires > req2->owner->expires ? 1 :
req1 < req2 ? -1 :
req1 > req2 ? 1 : 0;
}
void _getdns_check_expired_pending_netreqs(
getdns_context *context, uint64_t *now_ms);
static void _getdns_check_expired_pending_netreqs_cb(void *arg)
{
uint64_t now_ms = 0;
_getdns_check_expired_pending_netreqs((getdns_context *)arg, &now_ms);
}
/*
* getdns_context_create
*
@ -1330,6 +1350,15 @@ getdns_context_create_with_extended_memory_functions(
_getdns_rbtree_init(&result->outbound_requests, transaction_id_cmp);
_getdns_rbtree_init(&result->local_hosts, local_host_cmp);
_getdns_rbtree_init(&result->pending_netreqs, netreq_expiry_cmp);
result->first_pending_netreq = NULL;
result->netreqs_in_flight = 0;
result->pending_timeout_event.userarg = result;
result->pending_timeout_event.read_cb = NULL;
result->pending_timeout_event.write_cb = NULL;
result->pending_timeout_event.timeout_cb =
_getdns_check_expired_pending_netreqs_cb;
result->pending_timeout_event.ev = NULL;
result->server = NULL;

View File

@ -291,6 +291,14 @@ struct getdns_context {
*/
_getdns_rbtree_t outbound_requests;
/* network requests
*/
size_t netreqs_in_flight;
_getdns_rbtree_t pending_netreqs;
getdns_network_req *first_pending_netreq;
getdns_eventloop_event pending_timeout_event;
struct listen_set *server;
/* Event loop extension. */
@ -372,7 +380,6 @@ void _getdns_context_clear_outbound_request(getdns_dns_req *dnsreq);
*/
void _getdns_context_cancel_request(getdns_dns_req *dnsreq);
/* Calls user callback (with GETDNS_CALLBACK_TIMEOUT + response dict), then
* cancels and frees the getdns_dns_req with _getdns_context_cancel_request()
*/

View File

@ -3032,6 +3032,7 @@ static void check_chain_complete(chain_head *chain)
int r = GETDNS_RETURN_GOOD;
getdns_network_req **netreq_p, *netreq;
uint64_t now_ms = 0;
dnsreq->avoid_dnssec_roadblocks = 1;
@ -3039,9 +3040,9 @@ static void check_chain_complete(chain_head *chain)
; !r && (netreq = *netreq_p)
; netreq_p++) {
netreq->state = NET_REQ_NOT_SENT;
_getdns_netreq_change_state(netreq, NET_REQ_NOT_SENT);
netreq->owner = dnsreq;
r = _getdns_submit_netreq(netreq);
r = _getdns_submit_netreq(netreq, &now_ms);
}
return;
}

View File

@ -90,13 +90,23 @@ void
_getdns_check_dns_req_complete(getdns_dns_req *dns_req)
{
getdns_network_req **netreq_p, *netreq;
int results_found = 0, r;
int results_found = 0, timed_out = 1, r;
uint64_t now_ms = 0;
for (netreq_p = dns_req->netreqs; (netreq = *netreq_p); netreq_p++)
if (!_getdns_netreq_finished(netreq))
return;
else if (netreq->response_len > 0)
results_found = 1;
else {
if (netreq->state != NET_REQ_TIMED_OUT)
timed_out = 0;
if (netreq->response_len > 0)
results_found = 1;
}
if (timed_out) {
_getdns_context_request_timed_out(dns_req);
return;
}
/* Do we have to check more suffixes on nxdomain/nodata?
*/
@ -126,10 +136,10 @@ _getdns_check_dns_req_complete(getdns_dns_req *dns_req)
; (netreq = *netreq_p)
; netreq_p++ ) {
_getdns_netreq_reinit(netreq);
if ((r = _getdns_submit_netreq(netreq))) {
if ((r = _getdns_submit_netreq(netreq, &now_ms))) {
if (r == DNS_REQ_FINISHED)
return;
netreq->state = NET_REQ_FINISHED;
_getdns_netreq_change_state(netreq, NET_REQ_ERRORED);
}
}
_getdns_check_dns_req_complete(dns_req);
@ -164,10 +174,10 @@ _getdns_check_dns_req_complete(getdns_dns_req *dns_req)
; (netreq = *netreq_p)
; netreq_p++ ) {
_getdns_netreq_reinit(netreq);
if ((r = _getdns_submit_netreq(netreq))) {
if ((r = _getdns_submit_netreq(netreq, &now_ms))) {
if (r == DNS_REQ_FINISHED)
return;
netreq->state = NET_REQ_FINISHED;
_getdns_netreq_change_state(netreq, NET_REQ_ERRORED);
}
}
_getdns_check_dns_req_complete(dns_req);
@ -208,7 +218,7 @@ ub_resolve_event_callback(void* arg, int rcode, void *pkt, int pkt_len,
getdns_network_req *netreq = (getdns_network_req *) arg;
getdns_dns_req *dns_req = netreq->owner;
netreq->state = NET_REQ_FINISHED;
_getdns_netreq_change_state(netreq, NET_REQ_FINISHED);
/* parse */
if (getdns_apply_network_result(
netreq, rcode, pkt, pkt_len, sec, why_bogus)) {
@ -226,7 +236,7 @@ ub_resolve_callback(void* arg, int err, struct ub_result* ub_res)
getdns_network_req *netreq = (getdns_network_req *) arg;
getdns_dns_req *dns_req = netreq->owner;
netreq->state = NET_REQ_FINISHED;
_getdns_netreq_change_state(netreq, NET_REQ_FINISHED);
if (err != 0) {
_getdns_call_user_callback(dns_req, NULL);
return;
@ -247,28 +257,124 @@ ub_resolve_callback(void* arg, int err, struct ub_result* ub_res)
#endif
void _getdns_check_expired_pending_netreqs(
getdns_context *context, uint64_t *now_ms)
{
getdns_network_req *first;
assert(context);
while (context->pending_netreqs.count) {
first = (getdns_network_req *)
_getdns_rbtree_first(&context->pending_netreqs);
if (_getdns_ms_until_expiry2(first->owner->expires, now_ms) > 0)
break;
(void) _getdns_rbtree_delete(&context->pending_netreqs, first);
_getdns_netreq_change_state(first, NET_REQ_TIMED_OUT);
_getdns_check_dns_req_complete(first->owner);
}
first = context->pending_netreqs.count ? (getdns_network_req *)
_getdns_rbtree_first(&context->pending_netreqs) : NULL;
if (first == context->first_pending_netreq ||
(first && context->first_pending_netreq &&
first->owner->expires == context->first_pending_netreq->owner->expires))
return; /* Nothing changed */
if (context->first_pending_netreq)
GETDNS_CLEAR_EVENT( context->extension
, &context->pending_timeout_event);
if ((context->first_pending_netreq = first))
GETDNS_SCHEDULE_EVENT( context->extension, -1,
_getdns_ms_until_expiry2(first->owner->expires, now_ms),
&context->pending_timeout_event);
}
void
_getdns_netreq_change_state(
getdns_network_req *netreq, network_req_state new_state)
{
getdns_context *context;
uint64_t now_ms;
getdns_network_req *prev;
if (!netreq)
return;
context = netreq->owner->context;
if (netreq->state != NET_REQ_IN_FLIGHT) {
if (new_state == NET_REQ_IN_FLIGHT)
context->netreqs_in_flight += 1;
netreq->state = new_state;
return;
}
if (new_state == NET_REQ_IN_FLIGHT) /* No change */
return;
netreq->state = new_state;
context->netreqs_in_flight -= 1;
now_ms = 0;
prev = NULL;
while (context->pending_netreqs.count > 0 &&
( context->limit_outstanding_queries > context->netreqs_in_flight
|| context->limit_outstanding_queries == 0 )) {
getdns_network_req *first = (getdns_network_req *)
_getdns_rbtree_first(&context->pending_netreqs);
/* To prevent loops due to _getdns_submit_netreq re-inserting
* because of errno == EMFILE
*/
if (first == prev)
break;
else
prev = first;
(void) _getdns_rbtree_delete(&context->pending_netreqs, first);
(void) _getdns_submit_netreq(first, &now_ms);
}
}
int
_getdns_submit_netreq(getdns_network_req *netreq)
_getdns_submit_netreq(getdns_network_req *netreq, uint64_t *now_ms)
{
getdns_return_t r;
getdns_dns_req *dns_req = netreq->owner;
getdns_context *context = dns_req->context;
char name[1024];
int dnsreq_freed = 0;
#ifdef HAVE_LIBUNBOUND
int ub_resolve_r;
#endif
if (context->limit_outstanding_queries > 0 &&
context->netreqs_in_flight >= context->limit_outstanding_queries) {
netreq->node.key = netreq;
if (_getdns_rbtree_insert(
&context->pending_netreqs, &netreq->node)) {
_getdns_check_expired_pending_netreqs(context, now_ms);
return GETDNS_RETURN_GOOD;
}
}
_getdns_netreq_change_state(netreq, NET_REQ_IN_FLIGHT);
#ifdef STUB_NATIVE_DNSSEC
# ifdef DNSSEC_ROADBLOCK_AVOIDANCE
if ((dns_req->context->resolution_type == GETDNS_RESOLUTION_RECURSING
if ((context->resolution_type == GETDNS_RESOLUTION_RECURSING
&& !dns_req->dnssec_roadblock_avoidance)
|| dns_req->avoid_dnssec_roadblocks) {
# else
if ( dns_req->context->resolution_type == GETDNS_RESOLUTION_RECURSING) {
if ( context->resolution_type == GETDNS_RESOLUTION_RECURSING) {
# endif
#else
if ( dns_req->context->resolution_type == GETDNS_RESOLUTION_RECURSING
if ( context->resolution_type == GETDNS_RESOLUTION_RECURSING
|| dns_req->dnssec_return_status
|| dns_req->dnssec_return_only_secure
|| dns_req->dnssec_return_all_statuses
@ -284,7 +390,8 @@ _getdns_submit_netreq(getdns_network_req *netreq)
_getdns_context_request_timed_out;
dns_req->timeout.ev = NULL;
if ((r = dns_req->loop->vmt->schedule(dns_req->loop, -1,
dns_req->context->timeout, &dns_req->timeout)))
_getdns_ms_until_expiry2(dns_req->expires, now_ms),
&dns_req->timeout)))
return r;
}
(void) gldns_wire2str_dname_buf(dns_req->name,
@ -293,15 +400,15 @@ _getdns_submit_netreq(getdns_network_req *netreq)
#ifdef HAVE_LIBUNBOUND
dns_req->freed = &dnsreq_freed;
#ifdef HAVE_UNBOUND_EVENT_API
if (_getdns_ub_loop_enabled(&dns_req->context->ub_loop))
ub_resolve_r = ub_resolve_event(dns_req->context->unbound_ctx,
name, netreq->request_type, netreq->owner->request_class,
if (_getdns_ub_loop_enabled(&context->ub_loop))
ub_resolve_r = ub_resolve_event(context->unbound_ctx,
name, netreq->request_type, dns_req->request_class,
netreq, ub_resolve_event_callback, &(netreq->unbound_id)) ?
GETDNS_RETURN_GENERIC_ERROR : GETDNS_RETURN_GOOD;
else
#endif
ub_resolve_r = ub_resolve_async(dns_req->context->unbound_ctx,
name, netreq->request_type, netreq->owner->request_class,
ub_resolve_r = ub_resolve_async(context->unbound_ctx,
name, netreq->request_type, dns_req->request_class,
netreq, ub_resolve_callback, &(netreq->unbound_id)) ?
GETDNS_RETURN_GENERIC_ERROR : GETDNS_RETURN_GOOD;
if (dnsreq_freed)
@ -314,7 +421,7 @@ _getdns_submit_netreq(getdns_network_req *netreq)
}
/* Submit with stub resolver */
dns_req->freed = &dnsreq_freed;
r = _getdns_submit_stub_request(netreq);
r = _getdns_submit_stub_request(netreq, now_ms);
if (dnsreq_freed)
return DNS_REQ_FINISHED;
dns_req->freed = NULL;
@ -413,6 +520,7 @@ getdns_general_ns(getdns_context *context, getdns_eventloop *loop,
getdns_dns_req *req;
getdns_dict *localnames_response;
size_t i;
uint64_t now_ms = 0;
if (!context || !name || (!callbackfn && !internal_cb))
return GETDNS_RETURN_INVALID_PARAMETER;
@ -430,7 +538,7 @@ getdns_general_ns(getdns_context *context, getdns_eventloop *loop,
/* create the request */
if (!(req = _getdns_dns_req_new(
context, loop, name, request_type, extensions)))
context, loop, name, request_type, extensions, &now_ms)))
return GETDNS_RETURN_MEMORY_ERROR;
req->user_pointer = userarg;
@ -448,13 +556,13 @@ getdns_general_ns(getdns_context *context, getdns_eventloop *loop,
for ( netreq_p = req->netreqs
; !r && (netreq = *netreq_p)
; netreq_p++) {
if ((r = _getdns_submit_netreq(netreq))) {
if ((r = _getdns_submit_netreq(netreq, &now_ms))) {
if (r == DNS_REQ_FINISHED) {
if (return_netreq_p)
*return_netreq_p = NULL;
return GETDNS_RETURN_GOOD;
}
netreq->state = NET_REQ_FINISHED;
_getdns_netreq_change_state(netreq, NET_REQ_ERRORED);
}
}
@ -483,7 +591,7 @@ getdns_general_ns(getdns_context *context, getdns_eventloop *loop,
*return_netreq_p = NULL;
return GETDNS_RETURN_GOOD;
}
netreq->state = NET_REQ_FINISHED;
_getdns_netreq_change_state(netreq, NET_REQ_ERRORED);
}
}
/* Stop processing more namespaces, since there was a match */
@ -500,13 +608,13 @@ getdns_general_ns(getdns_context *context, getdns_eventloop *loop,
for ( netreq_p = req->netreqs
; !r && (netreq = *netreq_p)
; netreq_p++) {
if ((r = _getdns_submit_netreq(netreq))) {
if ((r = _getdns_submit_netreq(netreq, &now_ms))) {
if (r == DNS_REQ_FINISHED) {
if (return_netreq_p)
*return_netreq_p = NULL;
return GETDNS_RETURN_GOOD;
}
netreq->state = NET_REQ_FINISHED;
_getdns_netreq_change_state(netreq, NET_REQ_ERRORED);
}
}
break;

View File

@ -45,8 +45,20 @@
#define DNS_REQ_FINISHED -1
void _getdns_call_user_callback(getdns_dns_req *, getdns_dict *);
/* Change state of the netreq req.
* - Increments context->netreqs_in_flight
* when state changes from NOT_SENT to IN_FLIGHT
* - Decrements context->netreqs_in_flight
* when state changes from IN_FLIGHT to FINISHED, TIMED_OUT or ERRORED
* - Resubmits NOT_SENT netreqs from context->pending_netreqs,
* when # pending_netreqs < limit_outstanding_queries
*/
void _getdns_netreq_change_state(
getdns_network_req *netreq, network_req_state new_state);
void _getdns_check_dns_req_complete(getdns_dns_req *dns_req);
int _getdns_submit_netreq(getdns_network_req *netreq);
int _getdns_submit_netreq(getdns_network_req *netreq, uint64_t *now_ms);
getdns_return_t

View File

@ -121,7 +121,7 @@ mdns_timeout_cb(void *userarg)
#else
close(netreq->fd);
#endif
netreq->state = NET_REQ_TIMED_OUT;
_getdns_netreq_change_state(netreq, NET_REQ_TIMED_OUT);
if (netreq->owner->user_callback) {
netreq->debug_end_time = _getdns_get_time_as_uintt64();
(void)_getdns_context_request_timed_out(netreq->owner);
@ -182,7 +182,7 @@ mdns_udp_read_cb(void *userarg)
netreq->response_len = read;
netreq->debug_end_time = _getdns_get_time_as_uintt64();
netreq->state = NET_REQ_FINISHED;
_getdns_netreq_change_state(netreq, NET_REQ_FINISHED);
_getdns_check_dns_req_complete(dnsreq);
}

View File

@ -43,6 +43,7 @@
#include "dict.h"
#include "debug.h"
#include "convert.h"
#include "general.h"
/* MAXIMUM_TSIG_SPACE = TSIG name (dname) : 256
* TSIG type (uint16_t) : 2
@ -118,7 +119,7 @@ netreq_reset(getdns_network_req *net_req)
/* variables that need to be reset on reinit
*/
net_req->unbound_id = -1;
net_req->state = NET_REQ_NOT_SENT;
_getdns_netreq_change_state(net_req, NET_REQ_NOT_SENT);
net_req->dnssec_status = GETDNS_DNSSEC_INDETERMINATE;
net_req->tsig_status = GETDNS_DNSSEC_INDETERMINATE;
net_req->query_id = 0;
@ -183,6 +184,10 @@ network_req_init(getdns_network_req *net_req, getdns_dns_req *owner,
net_req->debug_tls_auth_status = GETDNS_AUTH_NONE;
net_req->debug_udp = 0;
/* Scheduling, touch only via _getdns_netreq_change_state!
*/
net_req->state = NET_REQ_NOT_SENT;
if (max_query_sz == 0) {
net_req->query = NULL;
net_req->opt = NULL;
@ -658,7 +663,8 @@ static const uint8_t no_suffixes[] = { 1, 0 };
/* create a new dns req to be submitted */
getdns_dns_req *
_getdns_dns_req_new(getdns_context *context, getdns_eventloop *loop,
const char *name, uint16_t request_type, getdns_dict *extensions)
const char *name, uint16_t request_type, getdns_dict *extensions,
uint64_t *now_ms)
{
int dnssec_return_status = is_extension_set(
extensions, "dnssec_return_status",
@ -953,5 +959,10 @@ _getdns_dns_req_new(getdns_context *context, getdns_eventloop *loop,
netreq_sz - sizeof(getdns_network_req), max_query_sz,
extensions);
if (*now_ms == 0 && (*now_ms = _getdns_get_now_ms()) == 0)
result->expires = 0;
else
result->expires = *now_ms + context->timeout;
return result;
}

View File

@ -54,15 +54,18 @@ typedef u_short sa_family_t;
#define _getdns_EWOULDBLOCK (WSAGetLastError() == WSATRY_AGAIN ||\
WSAGetLastError() == WSAEWOULDBLOCK)
#define _getdns_EINPROGRESS (WSAGetLastError() == WSAEINPROGRESS)
#define _getdns_EMFILE (WSAGetLastError() == WSAEMFILE)
#else
#define _getdns_EWOULDBLOCK (errno == EAGAIN || errno == EWOULDBLOCK)
#define _getdns_EINPROGRESS (errno == EINPROGRESS)
#define _getdns_EMFILE (errno == EMFILE)
#endif
/* WSA TODO:
* STUB_TCP_WOULDBLOCK added to deal with edge triggered event loops (versus
* level triggered). See also lines containing WSA TODO below...
*/
#define STUB_TRY_AGAIN_LATER -24 /* EMFILE, i.e. Out of OS resources */
#define STUB_NO_AUTH -8 /* Existing TLS connection is not authenticated */
#define STUB_CONN_GONE -7 /* Connection has failed, clear queue*/
#define STUB_TCP_WOULDBLOCK -6
@ -550,7 +553,7 @@ upstream_failed(getdns_upstream *upstream, int during_setup)
netreq = (getdns_network_req *)
_getdns_rbtree_first(&upstream->netreq_by_query_id);
stub_cleanup(netreq);
netreq->state = NET_REQ_FINISHED;
_getdns_netreq_change_state(netreq, NET_REQ_FINISHED);
_getdns_check_dns_req_complete(netreq->owner);
}
}
@ -580,7 +583,7 @@ stub_timeout_cb(void *userarg)
DEBUG_STUB("%s %-35s: MSG: %p\n",
STUB_DEBUG_CLEANUP, __FUNC__, (void*)netreq);
stub_cleanup(netreq);
netreq->state = NET_REQ_TIMED_OUT;
_getdns_netreq_change_state(netreq, NET_REQ_TIMED_OUT);
/* Handle upstream*/
if (netreq->fd >= 0) {
#ifdef USE_WINSOCK
@ -1358,8 +1361,8 @@ stub_udp_read_cb(void *userarg)
dnsreq)) == -1)
break;
upstream_schedule_netreq(netreq->upstream, netreq);
GETDNS_SCHEDULE_EVENT(
dnsreq->loop, -1, dnsreq->context->timeout,
GETDNS_SCHEDULE_EVENT(dnsreq->loop, -1,
_getdns_ms_until_expiry(dnsreq->expires),
getdns_eventloop_event_init(&netreq->event,
netreq, NULL, NULL, stub_timeout_cb));
@ -1368,7 +1371,7 @@ stub_udp_read_cb(void *userarg)
netreq->response_len = read;
dnsreq->upstreams->current_udp = 0;
netreq->debug_end_time = _getdns_get_time_as_uintt64();
netreq->state = NET_REQ_FINISHED;
_getdns_netreq_change_state(netreq, NET_REQ_FINISHED);
upstream->udp_responses++;
#if defined(DAEMON_DEBUG) && DAEMON_DEBUG
if (upstream->udp_responses == 1 ||
@ -1421,8 +1424,8 @@ stub_udp_write_cb(void *userarg)
#endif
return;
}
GETDNS_SCHEDULE_EVENT(
dnsreq->loop, netreq->fd, dnsreq->context->timeout,
GETDNS_SCHEDULE_EVENT(dnsreq->loop, netreq->fd,
_getdns_ms_until_expiry(dnsreq->expires),
getdns_eventloop_event_init(&netreq->event, netreq,
stub_udp_read_cb, NULL, stub_timeout_cb));
}
@ -1495,7 +1498,7 @@ upstream_read_cb(void *userarg)
DEBUG_STUB("%s %-35s: MSG: %p (read)\n",
STUB_DEBUG_READ, __FUNC__, (void*)netreq);
netreq->state = NET_REQ_FINISHED;
_getdns_netreq_change_state(netreq, NET_REQ_FINISHED);
netreq->response = upstream->tcp.read_buf;
netreq->response_len =
upstream->tcp.read_pos - upstream->tcp.read_buf;
@ -1614,7 +1617,7 @@ upstream_write_cb(void *userarg)
#endif
if (fallback_on_write(netreq) == STUB_TCP_ERROR) {
/* TODO: Need new state to report transport unavailable*/
netreq->state = NET_REQ_FINISHED;
_getdns_netreq_change_state(netreq, NET_REQ_FINISHED);
_getdns_check_dns_req_complete(netreq->owner);
}
return;
@ -1905,8 +1908,14 @@ upstream_find_for_netreq(getdns_network_req *netreq)
upstream = upstream_find_for_transport(netreq,
netreq->transports[i],
&fd);
if (fd == -1 || !upstream)
if (!upstream)
continue;
if (fd == -1) {
if (_getdns_EMFILE)
return STUB_TRY_AGAIN_LATER;
return -1;
}
netreq->transport_current = i;
netreq->upstream = upstream;
netreq->keepalive_sent = 0;
@ -1928,12 +1937,13 @@ upstream_find_for_netreq(getdns_network_req *netreq)
static int
fallback_on_write(getdns_network_req *netreq)
{
uint64_t now_ms = 0;
/* Deal with UDP one day*/
DEBUG_STUB("%s %-35s: MSG: %p FALLING BACK \n", STUB_DEBUG_SCHEDULE, __FUNC__, (void*)netreq);
/* Try to find a fallback transport*/
getdns_return_t result = _getdns_submit_stub_request(netreq);
getdns_return_t result = _getdns_submit_stub_request(netreq, &now_ms);
if (result != GETDNS_RETURN_GOOD)
return STUB_TCP_ERROR;
@ -1997,8 +2007,8 @@ upstream_schedule_netreq(getdns_upstream *upstream, getdns_network_req *netreq)
if (upstream->queries_sent == 0) {
/* Set a timeout on the upstream so we can catch failed setup*/
upstream->event.timeout_cb = upstream_setup_timeout_cb;
GETDNS_SCHEDULE_EVENT(upstream->loop,
upstream->fd, netreq->owner->context->timeout / 2,
GETDNS_SCHEDULE_EVENT(upstream->loop, upstream->fd,
_getdns_ms_until_expiry(netreq->owner->expires)/2,
&upstream->event);
} else {
GETDNS_SCHEDULE_EVENT(upstream->loop,
@ -2027,12 +2037,17 @@ upstream_schedule_netreq(getdns_upstream *upstream, getdns_network_req *netreq)
}
getdns_return_t
_getdns_submit_stub_request(getdns_network_req *netreq)
_getdns_submit_stub_request(getdns_network_req *netreq, uint64_t *now_ms)
{
int fd = -1;
getdns_dns_req *dnsreq;
getdns_context *context;
DEBUG_STUB("%s %-35s: MSG: %p TYPE: %d\n", STUB_DEBUG_ENTRY, __FUNC__,
(void*)netreq, netreq->request_type);
int fd = -1;
getdns_dns_req *dnsreq = netreq->owner;
dnsreq = netreq->owner;
context = dnsreq->context;
/* This does a best effort to get a initial fd.
* All other set up is done async*/
@ -2040,14 +2055,20 @@ _getdns_submit_stub_request(getdns_network_req *netreq)
if (fd == -1)
return GETDNS_RETURN_NO_UPSTREAM_AVAILABLE;
getdns_transport_list_t transport =
netreq->transports[netreq->transport_current];
switch(transport) {
else if (fd == STUB_TRY_AGAIN_LATER) {
_getdns_netreq_change_state(netreq, NET_REQ_NOT_SENT);
netreq->node.key = netreq;
if (_getdns_rbtree_insert(
&context->pending_netreqs, &netreq->node))
return GETDNS_RETURN_GOOD;
return GETDNS_RETURN_NO_UPSTREAM_AVAILABLE;
}
switch(netreq->transports[netreq->transport_current]) {
case GETDNS_TRANSPORT_UDP:
netreq->fd = fd;
GETDNS_CLEAR_EVENT(dnsreq->loop, &netreq->event);
GETDNS_SCHEDULE_EVENT(
dnsreq->loop, netreq->fd, dnsreq->context->timeout,
GETDNS_SCHEDULE_EVENT(dnsreq->loop, netreq->fd,
_getdns_ms_until_expiry2(dnsreq->expires, now_ms),
getdns_eventloop_event_init(&netreq->event, netreq,
NULL, stub_udp_write_cb, stub_timeout_cb));
return GETDNS_RETURN_GOOD;
@ -2121,7 +2142,7 @@ _getdns_submit_stub_request(getdns_network_req *netreq)
*/
GETDNS_SCHEDULE_EVENT(
dnsreq->loop, -1,
dnsreq->context->timeout,
_getdns_ms_until_expiry2(dnsreq->expires, now_ms),
getdns_eventloop_event_init(
&netreq->event, netreq, NULL, NULL,
stub_timeout_cb));

View File

@ -37,7 +37,8 @@
#include "getdns/getdns.h"
#include "types-internal.h"
getdns_return_t _getdns_submit_stub_request(getdns_network_req *netreq);
getdns_return_t _getdns_submit_stub_request(
getdns_network_req *netreq, uint64_t *now_ms);
void _getdns_cancel_stub_request(getdns_network_req *netreq);

View File

@ -0,0 +1,16 @@
BaseName: 070-coding-practice
Version: 1.0
Description: Check for non-recommended coding practices
CreationDate: ma 20 mrt 2017 15:55:19 CET
Maintainer: Willem Toorop
Category:
Component:
CmdDepends:
Depends:
Help:
Pre: 070-coding-practice.pre
Post:
Test: 070-coding-practice.test
AuxFiles:
Passed:
Failure:

View File

@ -0,0 +1,14 @@
# #-- 070-coding-practice.pre--#
# source the master var file when it's there
if [ -f ../.tpkg.var.master ]
then
source ../.tpkg.var.master
else
(
cd ..
[ -f "${TPKG_SRCDIR}/setup-env.sh" ] \
&& sh "${TPKG_SRCDIR}/setup-env.sh"
) && source ../.tpkg.var.master
fi
# use .tpkg.var.test for in test variable passing
[ -f .tpkg.var.test ] && source .tpkg.var.test

View File

@ -0,0 +1,40 @@
# #-- 070-coding-practice.test --#
# source the master var file when it's there
[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master
# use .tpkg.var.test for in test variable passing
[ -f .tpkg.var.test ] && source .tpkg.var.test
rm -f report.txt
(
cd ${SRCROOT}/src
if [ `grep '[^!=]=[ ][ ]*NET_REQ_' *.[ch] */*.[ch] | wc -l` -gt 1 ]
then
echo "*** "
echo "*** Setting getdns_network_req->state should be done via"
echo "*** _getdns_netreq_change_state() only, for anticipating"
echo "*** running out of filedescriptors (sockets) and for the"
echo "*** limit_outstanding_queries feature."
echo "*** "
grep '[^!=]=[ ][ ]*NET_REQ_' *.[ch] */*.[ch]
echo ""
fi
) >> report.txt
(
cd ${SRCROOT}/src
if [ `grep '__FUNCTION__' *.[ch] */*.[ch] | wc -l` -gt 0 ]
then
echo "*** "
echo "*** Use __FUNC__ instead of __FUNCTION__ for portability"
echo "*** __FUNC__ is aliases in config.h to name to be used"
echo "*** for the system with a #define"
echo "*** "
grep '__FUNCION__' *.[ch] */*.[ch]
echo ""
fi
) >> report.txt
if [ -s report.txt ]
then
cat report.txt
false
fi

View File

@ -0,0 +1,15 @@
builddir = @BUILDDIR@
testname = @TPKG_NAME@
LIBTOOL = $(builddir)/libtool
CFLAGS=-Wall -Wextra -I$(builddir)/src
LDLIBS=$(builddir)/src/libgetdns.la
.SUFFIXES: .c .o .a .lo .h
.c.lo:
$(LIBTOOL) --quiet --tag=CC --mode=compile $(CC) $(CFLAGS) -c $< -o $@
$(testname): $(testname).lo
$(LIBTOOL) --tag=CC --mode=link $(CC) $(LDLIBS) $(LDFLAGS) -o $(testname) $(testname).lo

View File

@ -0,0 +1,138 @@
/*
* delaydns.c - A DNS proxy that adds delay to replies
*
* Copyright (c) 2016, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* 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 name of the NLNET LABS 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 THE COPYRIGHT
* HOLDER OR CONTRIBUTORS 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 <getdns/getdns_extra.h>
#include <stdio.h>
#include <string.h>
static int n_requests = 0;
typedef struct transaction_t {
getdns_transaction_t request_id;
getdns_dict *request;
getdns_context *context;
getdns_eventloop *loop;
getdns_eventloop_event ev;
} transaction_t;
void delay_cb(void *userarg)
{
transaction_t *trans = userarg;
trans->loop->vmt->clear(trans->loop, &trans->ev);
(void) getdns_reply(trans->context, trans->request, trans->request_id);
getdns_dict_destroy(trans->request);
free(trans);
n_requests -= 1;
}
void handler(getdns_context *context, getdns_callback_type_t callback_type,
getdns_dict *request, void *userarg, getdns_transaction_t request_id)
{
transaction_t *trans = NULL;
getdns_bindata *qname;
char nreq_str[255];
getdns_bindata nreq_bd = { 0, (void *)nreq_str };
(void) userarg; (void)callback_type;
nreq_bd.size = snprintf(nreq_str, sizeof(nreq_str), "n_requests: %d", ++n_requests);
if (getdns_dict_get_bindata(request, "/question/qname", &qname) ||
getdns_dict_set_bindata(request, "/answer/0/name", qname) ||
getdns_dict_set_int(request, "/answer/0/type", GETDNS_RRTYPE_TXT) ||
getdns_dict_set_bindata(request, "/answer/0/rdata/txt_strings/-", &nreq_bd))
fprintf(stderr, "Request init error\n");
else if (qname->size >= 6 && qname->data[0] == 4 &&
qname->data[1] == 'q' && qname->data[2] == 'u' &&
qname->data[3] == 'i' && qname->data[4] == 't') {
(void) getdns_reply(context, request, request_id);
(void) getdns_context_set_listen_addresses(context, NULL, NULL, NULL);
getdns_dict_destroy(request);
return;
} else if (!(trans = malloc(sizeof(transaction_t))))
perror("memerror");
else {
(void) memset(trans, 0, sizeof(transaction_t));
trans->request_id = request_id;
trans->request = request;
trans->context = context;
trans->ev.userarg = trans;
trans->ev.timeout_cb = delay_cb;
if (getdns_context_get_eventloop(context, &trans->loop)
|| trans->loop->vmt->schedule(trans->loop, -1, 300, &trans->ev))
fprintf(stderr, "Could not schedule delay\n");
else return;
}
getdns_dict_destroy(trans->request);
if (trans) free(trans);
exit(EXIT_FAILURE);
}
int main()
{
getdns_context *context = NULL;
getdns_list *listeners = NULL;
getdns_dict *address = NULL;
uint32_t port = 18000;
getdns_return_t r;
if ((r = getdns_str2list("[ 127.0.0.1:18000 ]", &listeners)) ||
(r = getdns_list_get_dict(listeners, 0, &address)) ||
(r = getdns_context_create(&context, 0)))
fprintf(stderr, "Error initializing: ");
else while (++port < 18200 &&
!(r = getdns_dict_set_int(address, "port", port)) &&
(r = getdns_context_set_listen_addresses(
context, listeners, NULL, handler)))
; /* pass */
if (r) fprintf(stderr, "%s\n", getdns_get_errorstr_by_id(r));
else {
fprintf(stdout, "%d\n", (int)port);
fflush(stdout);
getdns_context_run(context);
}
getdns_list_destroy(listeners);
getdns_context_destroy(context);
return r;
}

View File

@ -0,0 +1,16 @@
BaseName: 280-limit_outstanding_queries
Version: 1.0
Description: Test if outstanding queries setting is obeyed
CreationDate: Tue Mar 14 10:43:45 CET 2017
Maintainer: Willem Toorop
Category:
Component:
CmdDepends:
Depends: 210-stub-only-link.tpkg
Help:
Pre: 280-limit_outstanding_queries.pre
Post:
Test: 280-limit_outstanding_queries.test
AuxFiles:
Passed:
Failure:

View File

@ -0,0 +1,14 @@
# #-- 280-limit_outstanding_queries.test --#
# source the master var file when it's there
[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master
# use .tpkg.var.test for in test variable passing
[ -f .tpkg.var.test ] && source .tpkg.var.test
(
grep '^CC=' "${BUILDDIR}/build-stub-only/src/Makefile"
grep '^LDFLAGS=' "${BUILDDIR}/build-stub-only/src/Makefile"
BUILDDIR4SED=`echo "${BUILDDIR}/build-stub-only" | sed 's/\//\\\\\//g'`
sed -e "s/@BUILDDIR@/${BUILDDIR4SED}/g" \
-e "s/@TPKG_NAME@/${TPKG_NAME}/g" "${TPKG_NAME}.Makefile"
) > Makefile

View File

@ -0,0 +1,40 @@
# #-- 280-limit_outstanding_queries.test --#
# source the master var file when it's there
[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master
# use .tpkg.var.test for in test variable passing
[ -f .tpkg.var.test ] && source .tpkg.var.test
QLIMIT=64
NQUERIES=`wc "./${TPKG_NAME}.queries"|sed 's/ .*$//g'`
# Test will take NQUERIES / QLIMIT * answer delay
# For current parameters this is 1000 / 64 * 0.3 = 4.6875
# which is smaller than 5 seconds default query timeout value,
# so the test should succeed.
make && "./${TPKG_NAME}" | (
read PORT
${GETDNS_STUB_QUERY} @127.0.0.1:$PORT TXT \
-a -F "./${TPKG_NAME}.queries" \
"{limit_outstanding_queries:$QLIMIT}" 2>&1 > out
${GETDNS_STUB_QUERY} -q @127.0.0.1:$PORT TXT quit.
) && grep '"n_requests: [0-9][0-9]*"' out | sed -e 's/^.*n_requests: //g' -e 's/".*$//g' \
| awk -vQLIMIT=$QLIMIT -vNQUERIES=$NQUERIES '
BEGIN{
max_outstanding = 0;
}
{
if ($1 > max_outstanding)
max_outstanding = $1;
}
END{
printf("%d of %d queries answered (%.1f%%)\n", NR, NQUERIES, (NR / NQUERIES * 100));
if (max_outstanding > QLIMIT) {
print "ERROR: More than "QLIMIT" outstanding queries: "max_outstanding;
exit(-1);
} else
print "SUCCESS: No more than "QLIMIT" outstanding queries: "max_outstanding;
}'

View File

@ -0,0 +1,15 @@
builddir = @BUILDDIR@
testname = @TPKG_NAME@
LIBTOOL = $(builddir)/libtool
CFLAGS=-Wall -Wextra -I$(builddir)/src
LDLIBS=$(builddir)/src/libgetdns.la
.SUFFIXES: .c .o .a .lo .h
.c.lo:
$(LIBTOOL) --quiet --tag=CC --mode=compile $(CC) $(CFLAGS) -c $< -o $@
$(testname): $(testname).lo
$(LIBTOOL) --tag=CC --mode=link $(CC) $(LDLIBS) $(LDFLAGS) -o $(testname) $(testname).lo

View File

@ -0,0 +1,143 @@
/*
* delaydns.c - A DNS proxy that adds delay to replies
*
* Copyright (c) 2016, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* 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 name of the NLNET LABS 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 THE COPYRIGHT
* HOLDER OR CONTRIBUTORS 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 <getdns/getdns_extra.h>
#include <stdio.h>
#include <string.h>
static int n_requests = 0;
typedef struct transaction_t {
getdns_transaction_t request_id;
getdns_dict *request;
getdns_context *context;
getdns_eventloop *loop;
getdns_eventloop_event ev;
} transaction_t;
void delay_cb(void *userarg)
{
transaction_t *trans = userarg;
trans->loop->vmt->clear(trans->loop, &trans->ev);
(void) getdns_reply(trans->context, trans->request, trans->request_id);
getdns_dict_destroy(trans->request);
free(trans);
n_requests -= 1;
}
void handler(getdns_context *context, getdns_callback_type_t callback_type,
getdns_dict *request, void *userarg, getdns_transaction_t request_id)
{
transaction_t *trans = NULL;
getdns_bindata *qname;
char nreq_str[255];
getdns_bindata nreq_bd = { 0, (void *)nreq_str };
(void) userarg; (void)callback_type;
nreq_bd.size = snprintf(nreq_str, sizeof(nreq_str), "n_requests: %d", ++n_requests);
if (getdns_dict_get_bindata(request, "/question/qname", &qname) ||
getdns_dict_set_bindata(request, "/answer/0/name", qname) ||
getdns_dict_set_int(request, "/answer/0/type", GETDNS_RRTYPE_TXT) ||
getdns_dict_set_bindata(request, "/answer/0/rdata/txt_strings/-", &nreq_bd))
fprintf(stderr, "Request init error\n");
else if (qname->size >= 6 && qname->data[0] == 4 &&
qname->data[1] == 'q' && qname->data[2] == 'u' &&
qname->data[3] == 'i' && qname->data[4] == 't') {
(void) getdns_reply(context, request, request_id);
(void) getdns_context_set_listen_addresses(context, NULL, NULL, NULL);
getdns_dict_destroy(request);
return;
} else if (!(trans = malloc(sizeof(transaction_t))))
perror("memerror");
else {
char *fqdn;
getdns_convert_dns_name_to_fqdn(qname, &fqdn);
(void) memset(trans, 0, sizeof(transaction_t));
trans->request_id = request_id;
trans->request = request;
trans->context = context;
trans->ev.userarg = trans;
trans->ev.timeout_cb = delay_cb;
fprintf(stderr, "sched delay for query %s, n_request %d\n", fqdn, (int)n_requests);
free(fqdn);
if (getdns_context_get_eventloop(context, &trans->loop)
|| trans->loop->vmt->schedule(trans->loop, -1, 300, &trans->ev))
fprintf(stderr, "Could not schedule delay\n");
else return;
}
getdns_dict_destroy(trans->request);
if (trans) free(trans);
exit(EXIT_FAILURE);
}
int main()
{
getdns_context *context = NULL;
getdns_list *listeners = NULL;
getdns_dict *address = NULL;
uint32_t port = 18000;
getdns_return_t r;
if ((r = getdns_str2list("[ 127.0.0.1:18000 ]", &listeners)) ||
(r = getdns_list_get_dict(listeners, 0, &address)) ||
(r = getdns_context_create(&context, 0)))
fprintf(stderr, "Error initializing: ");
else while (++port < 18200 &&
!(r = getdns_dict_set_int(address, "port", port)) &&
(r = getdns_context_set_listen_addresses(
context, listeners, NULL, handler)))
; /* pass */
if (r) fprintf(stderr, "%s\n", getdns_get_errorstr_by_id(r));
else {
fprintf(stdout, "%d\n", (int)port);
fflush(stdout);
getdns_context_run(context);
}
getdns_list_destroy(listeners);
getdns_context_destroy(context);
return r;
}

View File

@ -0,0 +1,16 @@
BaseName: 285-out_of_filedescriptors
Version: 1.0
Description: Test if outstanding queries setting is obeyed
CreationDate: ma 20 mrt 2017 15:17:45 CET
Maintainer: Willem Toorop
Category:
Component:
CmdDepends:
Depends: 210-stub-only-link.tpkg
Help:
Pre: 285-out_of_filedescriptors.pre
Post:
Test: 285-out_of_filedescriptors.test
AuxFiles:
Passed:
Failure:

View File

@ -0,0 +1,14 @@
# #-- 285-out_of_filedescriptors.test --#
# source the master var file when it's there
[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master
# use .tpkg.var.test for in test variable passing
[ -f .tpkg.var.test ] && source .tpkg.var.test
(
grep '^CC=' "${BUILDDIR}/build-stub-only/src/Makefile"
grep '^LDFLAGS=' "${BUILDDIR}/build-stub-only/src/Makefile"
BUILDDIR4SED=`echo "${BUILDDIR}/build-stub-only" | sed 's/\//\\\\\//g'`
sed -e "s/@BUILDDIR@/${BUILDDIR4SED}/g" \
-e "s/@TPKG_NAME@/${TPKG_NAME}/g" "${TPKG_NAME}.Makefile"
) > Makefile

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,48 @@
# #-- 285-out_of_filedescriptors.test --#
# source the master var file when it's there
[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master
# use .tpkg.var.test for in test variable passing
[ -f .tpkg.var.test ] && source .tpkg.var.test
QLIMIT=79
NQUERIES=`wc "./${TPKG_NAME}.queries"|sed 's/ .*$//g'`
# This time the query limit is set by setting the maximum open
# filedescriptors. We seem to be needing a higher QLIMIT, than
# with limit_outstanding_queries unit test.
#
# 4 filedescriptors are already needed for overhead (logging etc),
# but experiments showed that to prevent timeouts, we should
# have a higher value than 72 at least.
#
# Test will take NQUERIES / QLIMIT * answer delay
# For current parameters this is 1000 / 75 * 0.3 = 4.0
# which is smaller than 5 seconds default query timeout value,
# so the test should succeed.
make && "./${TPKG_NAME}" | (
read PORT
ulimit -n $QLIMIT
${GETDNS_STUB_QUERY} @127.0.0.1:$PORT TXT \
-a -F "./${TPKG_NAME}.queries" 2>&1 > out
${GETDNS_STUB_QUERY} -q @127.0.0.1:$PORT TXT quit.
) && grep '"n_requests: [0-9][0-9]*"' out | sed -e 's/^.*n_requests: //g' -e 's/".*$//g' \
| awk -vQLIMIT=$QLIMIT -vNQUERIES=$NQUERIES '
BEGIN{
max_outstanding = 0;
}
{
if ($1 > max_outstanding)
max_outstanding = $1;
}
END{
printf("%d of %d queries answered (%.1f%%)\n", NR, NQUERIES, (NR / NQUERIES * 100));
if (max_outstanding > QLIMIT) {
print "ERROR: More than "QLIMIT" outstanding queries: "max_outstanding;
exit(-1);
} else
print "SUCCESS: No more than "QLIMIT" outstanding queries: "max_outstanding;
}'

View File

@ -338,6 +338,11 @@ typedef struct getdns_dns_req {
/* the transaction id */
getdns_transaction_t trans_id;
/* Absolute time (in miliseconds since epoch),
* after which this dns request is expired; i.e. timed out
*/
uint64_t expires;
/* for scheduling timeouts when using libunbound */
getdns_eventloop_event timeout;
@ -408,7 +413,7 @@ extern getdns_dict *dnssec_ok_checking_disabled_avoid_roadblocks;
/* dns request utils */
getdns_dns_req *_getdns_dns_req_new(getdns_context *context, getdns_eventloop *loop,
const char *name, uint16_t request_type, getdns_dict *extensions);
const char *name, uint16_t request_type, getdns_dict *extensions, uint64_t *now_ms);
void _getdns_dns_req_free(getdns_dns_req * req);

View File

@ -198,5 +198,25 @@ INLINE void _dname_canonicalize2(uint8_t *dname)
_dname_canonicalize(dname, dname);
}
INLINE uint64_t _getdns_get_now_ms()
{
struct timeval tv;
(void) gettimeofday(&tv, NULL);
return tv.tv_sec * 1000 + tv.tv_usec / 1000;
}
INLINE uint64_t _getdns_ms_until_expiry(uint64_t expires)
{
uint64_t now_ms = _getdns_get_now_ms();
return now_ms >= expires ? 0 : expires - now_ms;
}
INLINE uint64_t _getdns_ms_until_expiry2(uint64_t expires, uint64_t *now_ms)
{
if (*now_ms == 0) *now_ms = _getdns_get_now_ms();
return *now_ms >= expires ? 0 : expires - *now_ms;
}
#endif
/* util-internal.h */