summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Stenberg <daniel@haxx.se>2008-05-13 20:48:48 +0000
committerDaniel Stenberg <daniel@haxx.se>2008-05-13 20:48:48 +0000
commit46dbd9461af66b9aa11a7f9e776979df6be028ac (patch)
tree3bfc49e25d58aec0342727b02ba904c7ea6c7e0e
parent76d91c21ad6248518e37eb6dd6b52fb7ab8a7fbd (diff)
downloadc-ares-46dbd9461af66b9aa11a7f9e776979df6be028ac.tar.gz
c-ares-46dbd9461af66b9aa11a7f9e776979df6be028ac.tar.bz2
c-ares-46dbd9461af66b9aa11a7f9e776979df6be028ac.zip
- Introducing millisecond resolution support for the timeout option. See
ares_init_options()'s ARES_OPT_TIMEOUTMS.
-rw-r--r--CHANGES5
-rw-r--r--ares.h4
-rw-r--r--ares_init.323
-rw-r--r--ares_init.c12
-rw-r--r--ares_private.h22
-rw-r--r--ares_process.c132
-rw-r--r--ares_send.c14
-rw-r--r--ares_timeout.c27
8 files changed, 167 insertions, 72 deletions
diff --git a/CHANGES b/CHANGES
index 3d44b19..db3d035 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,5 +1,10 @@
Changelog for the c-ares project
+* May 13 2008 (Daniel Stenberg)
+
+- Introducing millisecond resolution support for the timeout option. See
+ ares_init_options()'s ARES_OPT_TIMEOUTMS.
+
* May 9 2008 (Yang Tse)
- Use monotonic time source if available, for private function ares__tvnow()
diff --git a/ares.h b/ares.h
index 6401019..3609f86 100644
--- a/ares.h
+++ b/ares.h
@@ -1,6 +1,7 @@
/* $Id$ */
/* Copyright 1998 by the Massachusetts Institute of Technology.
+ * Copyright (C) 2007 by Daniel Stenberg
*
* Permission to use, copy, modify, and distribute this
* software and its documentation for any purpose and without
@@ -112,6 +113,7 @@ extern "C" {
#define ARES_OPT_SORTLIST (1 << 10)
#define ARES_OPT_SOCK_SNDBUF (1 << 11)
#define ARES_OPT_SOCK_RCVBUF (1 << 12)
+#define ARES_OPT_TIMEOUTMS (1 << 13)
/* Nameinfo flag values */
#define ARES_NI_NOFQDN (1 << 0)
@@ -179,7 +181,7 @@ struct apattern;
struct ares_options {
int flags;
- int timeout;
+ int timeout; /* in seconds or milliseconds, depending on options */
int tries;
int ndots;
unsigned short udp_port;
diff --git a/ares_init.3 b/ares_init.3
index 026d8da..428cb03 100644
--- a/ares_init.3
+++ b/ares_init.3
@@ -1,6 +1,7 @@
.\" $Id$
.\"
.\" Copyright 1998 by the Massachusetts Institute of Technology.
+.\" Copyright (C) 2007-2008 by Daniel Stenberg
.\"
.\" Permission to use, copy, modify, and distribute this
.\" software and its documentation for any purpose and without
@@ -14,7 +15,7 @@
.\" this software for any purpose. It is provided "as is"
.\" without express or implied warranty.
.\"
-.TH ARES_INIT 3 "7 December 2004"
+.TH ARES_INIT 3 "13 May 2008"
.SH NAME
ares_init, ares_init_options \- Initialize a resolver channel
.SH SYNOPSIS
@@ -49,10 +50,22 @@ description of possible flag values.
.B ARES_OPT_TIMEOUT
.B int \fItimeout\fP;
.br
-The number of seconds each name server is given to respond to a query
-on the first try. (After the first try, the timeout algorithm becomes
-more complicated, but scales linearly with the value of
-\fItimeout\fP.) The default is five seconds.
+The number of seconds each name server is given to respond to a query on the
+first try. (After the first try, the timeout algorithm becomes more
+complicated, but scales linearly with the value of \fItimeout\fP.) The
+default is five seconds. This option is being deprecated by
+\fIARES_OPT_TIMEOUTMS\fP starting in c-ares 1.5.2.
+.TP 18
+.B ARES_OPT_TIMEOUTMS
+.B int \fItimeout\fP;
+.br
+The number of milliseconds each name server is given to respond to a query on
+the first try. (After the first try, the timeout algorithm becomes more
+complicated, but scales linearly with the value of \fItimeout\fP.) The
+default is five seconds. Note that this option is specified with the same
+struct field as the former \fIARES_OPT_TIMEOUT\fP, it is but the option bits
+that tell c-ares how to interpret the number. This option was added in c-ares
+1.5.2.
.TP 18
.B ARES_OPT_TRIES
.B int \fItries\fP;
diff --git a/ares_init.c b/ares_init.c
index 9b655a8..2781813 100644
--- a/ares_init.c
+++ b/ares_init.c
@@ -1,6 +1,7 @@
/* $Id$ */
/* Copyright 1998 by the Massachusetts Institute of Technology.
+ * Copyright (C) 2007-2008 by Daniel Stenberg
*
* Permission to use, copy, modify, and distribute this
* software and its documentation for any purpose and without
@@ -254,13 +255,16 @@ int ares_save_options(ares_channel channel, struct ares_options *options,
if (!ARES_CONFIG_CHECK(channel))
return ARES_ENODATA;
- (*optmask) = (ARES_OPT_FLAGS|ARES_OPT_TIMEOUT|ARES_OPT_TRIES|ARES_OPT_NDOTS|
+ (*optmask) = (ARES_OPT_FLAGS|ARES_OPT_TRIES|ARES_OPT_NDOTS|
ARES_OPT_UDP_PORT|ARES_OPT_TCP_PORT|ARES_OPT_SOCK_STATE_CB|
ARES_OPT_SERVERS|ARES_OPT_DOMAINS|ARES_OPT_LOOKUPS|
- ARES_OPT_SORTLIST);
+ ARES_OPT_SORTLIST|ARES_OPT_TIMEOUTMS);
/* Copy easy stuff */
options->flags = channel->flags;
+
+ /* We return full millisecond resolution but that's only because we don't
+ set the ARES_OPT_TIMEOUT anymore, only the new ARES_OPT_TIMEOUTMS */
options->timeout = channel->timeout;
options->tries = channel->tries;
options->ndots = channel->ndots;
@@ -328,8 +332,10 @@ static int init_by_options(ares_channel channel,
/* Easy stuff. */
if ((optmask & ARES_OPT_FLAGS) && channel->flags == -1)
channel->flags = options->flags;
- if ((optmask & ARES_OPT_TIMEOUT) && channel->timeout == -1)
+ if ((optmask & ARES_OPT_TIMEOUTMS) && channel->timeout == -1)
channel->timeout = options->timeout;
+ else if ((optmask & ARES_OPT_TIMEOUT) && channel->timeout == -1)
+ channel->timeout = options->timeout * 1000;
if ((optmask & ARES_OPT_TRIES) && channel->tries == -1)
channel->tries = options->tries;
if ((optmask & ARES_OPT_NDOTS) && channel->ndots == -1)
diff --git a/ares_private.h b/ares_private.h
index 9354e63..6ec4dc4 100644
--- a/ares_private.h
+++ b/ares_private.h
@@ -4,6 +4,7 @@
/* $Id$ */
/* Copyright 1998 by the Massachusetts Institute of Technology.
+ * Copyright (C) 2004-2008 by Daniel Stenberg
*
* Permission to use, copy, modify, and distribute this
* software and its documentation for any purpose and without
@@ -48,7 +49,7 @@
#include <time.h>
#endif
-#define DEFAULT_TIMEOUT 5
+#define DEFAULT_TIMEOUT 5000 /* milliseconds */
#define DEFAULT_TRIES 4
#ifndef INADDR_NONE
#define INADDR_NONE 0xffffffff
@@ -149,7 +150,7 @@ struct server_state {
struct query {
/* Query ID from qbuf, for faster lookup, and current timeout */
unsigned short qid;
- time_t timeout;
+ struct timeval timeout;
/*
* Links for the doubly-linked lists in which we insert a query.
@@ -217,7 +218,7 @@ typedef struct rc4_key
struct ares_channeldata {
/* Configuration data */
int flags;
- int timeout;
+ int timeout; /* in milliseconds */
int tries;
int ndots;
int udp_port;
@@ -242,7 +243,8 @@ struct ares_channeldata {
/* Generation number to use for the next TCP socket open/close */
int tcp_connection_generation;
- /* The time at which we last called process_timeouts() */
+ /* The time at which we last called process_timeouts(). Uses integer seconds
+ just to draw the line somewhere. */
time_t last_timeout_processed;
/* Circular, doubly-linked list of queries, bucketed various ways.... */
@@ -259,8 +261,18 @@ struct ares_channeldata {
void *sock_state_cb_data;
};
+/* return true if now is exactly check time or later */
+int ares__timedout(struct timeval *now,
+ struct timeval *check);
+/* add the specific number of milliseconds to the time in the first argument */
+int ares__timeadd(struct timeval *now,
+ int millisecs);
+/* return time offset between now and (future) check, in milliseconds */
+int ares__timeoffset(struct timeval *now,
+ struct timeval *check);
void ares__rc4(rc4_key* key,unsigned char *buffer_ptr, int buffer_len);
-void ares__send_query(ares_channel channel, struct query *query, time_t now);
+void ares__send_query(ares_channel channel, struct query *query,
+ struct timeval *now);
void ares__close_sockets(ares_channel channel, struct server_state *server);
int ares__get_hostent(FILE *fp, int family, struct hostent **host);
int ares__read_line(FILE *fp, char **buf, int *bufsize);
diff --git a/ares_process.c b/ares_process.c
index 442e328..eca363a 100644
--- a/ares_process.c
+++ b/ares_process.c
@@ -1,6 +1,7 @@
/* $Id$ */
/* Copyright 1998 by the Massachusetts Institute of Technology.
+ * Copyright (C) 2004-2008 by Daniel Stenberg
*
* Permission to use, copy, modify, and distribute this
* software and its documentation for any purpose and without
@@ -42,6 +43,9 @@
#ifdef HAVE_ARPA_NAMESER_COMPAT_H
#include <arpa/nameser_compat.h>
#endif
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
#endif /* WIN32 && !WATT32 */
#ifdef HAVE_STRINGS_H
@@ -71,21 +75,25 @@
static int try_again(int errnum);
static void write_tcp_data(ares_channel channel, fd_set *write_fds,
- ares_socket_t write_fd, time_t now);
+ ares_socket_t write_fd, struct timeval *now);
static void read_tcp_data(ares_channel channel, fd_set *read_fds,
- ares_socket_t read_fd, time_t now);
+ ares_socket_t read_fd, struct timeval *now);
static void read_udp_packets(ares_channel channel, fd_set *read_fds,
- ares_socket_t read_fd, time_t now);
+ ares_socket_t read_fd, struct timeval *now);
static void advance_tcp_send_queue(ares_channel channel, int whichserver,
ssize_t num_bytes);
-static void process_timeouts(ares_channel channel, time_t now);
-static void process_broken_connections(ares_channel channel, time_t now);
+static void process_timeouts(ares_channel channel, struct timeval *now);
+static void process_broken_connections(ares_channel channel,
+ struct timeval *now);
static void process_answer(ares_channel channel, unsigned char *abuf,
- int alen, int whichserver, int tcp, time_t now);
-static void handle_error(ares_channel channel, int whichserver, time_t now);
+ int alen, int whichserver, int tcp,
+ struct timeval *now);
+static void handle_error(ares_channel channel, int whichserver,
+ struct timeval *now);
static void skip_server(ares_channel channel, struct query *query,
int whichserver);
-static void next_server(ares_channel channel, struct query *query, time_t now);
+static void next_server(ares_channel channel, struct query *query,
+ struct timeval *now);
static int configure_socket(int s, ares_channel channel);
static int open_tcp_socket(ares_channel channel, struct server_state *server);
static int open_udp_socket(ares_channel channel, struct server_state *server);
@@ -94,19 +102,59 @@ static int same_questions(const unsigned char *qbuf, int qlen,
static void end_query(ares_channel channel, struct query *query, int status,
unsigned char *abuf, int alen);
+/* return true if now is exactly check time or later */
+int ares__timedout(struct timeval *now,
+ struct timeval *check)
+{
+ int secs = (now->tv_sec - check->tv_sec);
+
+ if(secs > 0)
+ return 1; /* yes, timed out */
+ if(secs < -1)
+ return 0; /* nope, not timed out */
+
+ /* if the full seconds were identical, check the sub second parts */
+ return (now->tv_usec - check->tv_usec >= 0);
+}
+
+/* add the specific number of milliseconds to the time in the first argument */
+int ares__timeadd(struct timeval *now,
+ int millisecs)
+{
+ now->tv_sec += millisecs/1000;
+ now->tv_usec += (millisecs%1000)*1000;
+
+ if(now->tv_usec >= 1000000) {
+ ++(now->tv_sec);
+ now->tv_usec -= 1000000;
+ }
+
+ return 0;
+}
+
+/* return time offset between now and (future) check, in milliseconds */
+int ares__timeoffset(struct timeval *now,
+ struct timeval *check)
+{
+ int secs = (check->tv_sec - now->tv_sec); /* this many seconds */
+ int us = (check->tv_usec - now->tv_usec); /* this many microseconds */
+
+ return secs*1000 + us/1000; /* return them combined as milliseconds */
+}
+
+
/* Something interesting happened on the wire, or there was a timeout.
* See what's up and respond accordingly.
*/
void ares_process(ares_channel channel, fd_set *read_fds, fd_set *write_fds)
{
- time_t now;
-
- time(&now);
- write_tcp_data(channel, write_fds, ARES_SOCKET_BAD, now);
- read_tcp_data(channel, read_fds, ARES_SOCKET_BAD, now);
- read_udp_packets(channel, read_fds, ARES_SOCKET_BAD, now);
- process_timeouts(channel, now);
- process_broken_connections(channel, now);
+ struct timeval now = ares__tvnow();
+
+ write_tcp_data(channel, write_fds, ARES_SOCKET_BAD, &now);
+ read_tcp_data(channel, read_fds, ARES_SOCKET_BAD, &now);
+ read_udp_packets(channel, read_fds, ARES_SOCKET_BAD, &now);
+ process_timeouts(channel, &now);
+ process_broken_connections(channel, &now);
}
/* Something interesting happened on the wire, or there was a timeout.
@@ -117,13 +165,12 @@ void ares_process_fd(ares_channel channel,
file descriptors */
ares_socket_t write_fd)
{
- time_t now;
+ struct timeval now = ares__tvnow();
- time(&now);
- write_tcp_data(channel, NULL, write_fd, now);
- read_tcp_data(channel, NULL, read_fd, now);
- read_udp_packets(channel, NULL, read_fd, now);
- process_timeouts(channel, now);
+ write_tcp_data(channel, NULL, write_fd, &now);
+ read_tcp_data(channel, NULL, read_fd, &now);
+ read_udp_packets(channel, NULL, read_fd, &now);
+ process_timeouts(channel, &now);
}
@@ -158,7 +205,7 @@ static int try_again(int errnum)
static void write_tcp_data(ares_channel channel,
fd_set *write_fds,
ares_socket_t write_fd,
- time_t now)
+ struct timeval *now)
{
struct server_state *server;
struct send_request *sendreq;
@@ -177,7 +224,8 @@ static void write_tcp_data(ares_channel channel,
/* Make sure server has data to send and is selected in write_fds or
write_fd. */
server = &channel->servers[i];
- if (!server->qhead || server->tcp_socket == ARES_SOCKET_BAD || server->is_broken)
+ if (!server->qhead || server->tcp_socket == ARES_SOCKET_BAD ||
+ server->is_broken)
continue;
if(write_fds) {
@@ -281,7 +329,7 @@ static void advance_tcp_send_queue(ares_channel channel, int whichserver,
* a packet if we finish reading one.
*/
static void read_tcp_data(ares_channel channel, fd_set *read_fds,
- ares_socket_t read_fd, time_t now)
+ ares_socket_t read_fd, struct timeval *now)
{
struct server_state *server;
int i;
@@ -377,7 +425,7 @@ static void read_tcp_data(ares_channel channel, fd_set *read_fds,
/* If any UDP sockets select true for reading, process them. */
static void read_udp_packets(ares_channel channel, fd_set *read_fds,
- ares_socket_t read_fd, time_t now)
+ ares_socket_t read_fd, struct timeval *now)
{
struct server_state *server;
int i;
@@ -428,7 +476,7 @@ static void read_udp_packets(ares_channel channel, fd_set *read_fds,
}
/* If any queries have timed out, note the timeout and move them on. */
-static void process_timeouts(ares_channel channel, time_t now)
+static void process_timeouts(ares_channel channel, struct timeval *now)
{
time_t t; /* the time of the timeouts we're processing */
struct query *query;
@@ -441,14 +489,14 @@ static void process_timeouts(ares_channel channel, time_t now)
* only a handful of requests that fall into the "now" bucket, so
* this should be quite quick.
*/
- for (t = channel->last_timeout_processed; t <= now; t++)
+ for (t = channel->last_timeout_processed; t <= now->tv_sec; t++)
{
list_head = &(channel->queries_by_timeout[t % ARES_TIMEOUT_TABLE_SIZE]);
for (list_node = list_head->next; list_node != list_head; )
{
query = list_node->data;
list_node = list_node->next; /* in case the query gets deleted */
- if (query->timeout != 0 && now >= query->timeout)
+ if (query->timeout.tv_sec && ares__timedout(now, &query->timeout))
{
query->error_status = ARES_ETIMEOUT;
++query->timeouts;
@@ -456,12 +504,13 @@ static void process_timeouts(ares_channel channel, time_t now)
}
}
}
- channel->last_timeout_processed = now;
+ channel->last_timeout_processed = now->tv_sec;
}
/* Handle an answer from a server. */
static void process_answer(ares_channel channel, unsigned char *abuf,
- int alen, int whichserver, int tcp, time_t now)
+ int alen, int whichserver, int tcp,
+ struct timeval *now)
{
int tc, rcode;
unsigned short id;
@@ -539,7 +588,8 @@ static void process_answer(ares_channel channel, unsigned char *abuf,
}
/* Close all the connections that are no longer usable. */
-static void process_broken_connections(ares_channel channel, time_t now)
+static void process_broken_connections(ares_channel channel,
+ struct timeval *now)
{
int i;
for (i = 0; i < channel->nservers; i++)
@@ -552,7 +602,8 @@ static void process_broken_connections(ares_channel channel, time_t now)
}
}
-static void handle_error(ares_channel channel, int whichserver, time_t now)
+static void handle_error(ares_channel channel, int whichserver,
+ struct timeval *now)
{
struct server_state *server;
struct query *query;
@@ -603,7 +654,8 @@ static void skip_server(ares_channel channel, struct query *query,
}
}
-static void next_server(ares_channel channel, struct query *query, time_t now)
+static void next_server(ares_channel channel, struct query *query,
+ struct timeval *now)
{
/* Advance to the next server or try. */
query->server++;
@@ -640,7 +692,8 @@ static void next_server(ares_channel channel, struct query *query, time_t now)
end_query(channel, query, query->error_status, NULL, 0);
}
-void ares__send_query(ares_channel channel, struct query *query, time_t now)
+void ares__send_query(ares_channel channel, struct query *query,
+ struct timeval *now)
{
struct send_request *sendreq;
struct server_state *server;
@@ -707,16 +760,17 @@ void ares__send_query(ares_channel channel, struct query *query, time_t now)
return;
}
}
- query->timeout = now
- + ((query->try == 0) ? channel->timeout
- : channel->timeout << query->try / channel->nservers);
+ query->timeout = *now;
+ ares__timeadd(&query->timeout,
+ (query->try == 0) ? channel->timeout
+ : channel->timeout << query->try / channel->nservers);
/* Keep track of queries bucketed by timeout, so we can process
* timeout events quickly.
*/
ares__remove_from_list(&(query->queries_by_timeout));
ares__insert_in_list(
&(query->queries_by_timeout),
- &(channel->queries_by_timeout[query->timeout %
+ &(channel->queries_by_timeout[query->timeout.tv_sec %
ARES_TIMEOUT_TABLE_SIZE]));
/* Keep track of queries bucketed by server, so we can process server
diff --git a/ares_send.c b/ares_send.c
index 2007321..fe921b6 100644
--- a/ares_send.c
+++ b/ares_send.c
@@ -39,7 +39,7 @@ void ares_send(ares_channel channel, const unsigned char *qbuf, int qlen,
{
struct query *query;
int i;
- time_t now;
+ struct timeval now;
/* Verify that the query is at least long enough to hold the header. */
if (qlen < HFIXEDSZ || qlen >= (1 << 16))
@@ -74,7 +74,7 @@ void ares_send(ares_channel channel, const unsigned char *qbuf, int qlen,
/* Compute the query ID. Start with no timeout. */
query->qid = (unsigned short)DNS_HEADER_QID(qbuf);
- query->timeout = 0;
+ query->timeout.tv_sec = query->timeout.tv_usec = 0;
/* Form the TCP query buffer by prepending qlen (as two
* network-order bytes) to qbuf.
@@ -107,17 +107,17 @@ void ares_send(ares_channel channel, const unsigned char *qbuf, int qlen,
ares__init_list_node(&(query->queries_by_timeout), query);
ares__init_list_node(&(query->queries_to_server), query);
ares__init_list_node(&(query->all_queries), query);
-
+
/* Chain the query into the list of all queries. */
ares__insert_in_list(&(query->all_queries), &(channel->all_queries));
/* Keep track of queries bucketed by qid, so we can process DNS
* responses quickly.
*/
ares__insert_in_list(
- &(query->queries_by_qid),
- &(channel->queries_by_qid[query->qid % ARES_QID_TABLE_SIZE]));
+ &(query->queries_by_qid),
+ &(channel->queries_by_qid[query->qid % ARES_QID_TABLE_SIZE]));
/* Perform the first query action. */
- time(&now);
- ares__send_query(channel, query, now);
+ now = ares__tvnow();
+ ares__send_query(channel, query, &now);
}
diff --git a/ares_timeout.c b/ares_timeout.c
index 12b93b0..ce98491 100644
--- a/ares_timeout.c
+++ b/ares_timeout.c
@@ -37,16 +37,16 @@ struct timeval *ares_timeout(ares_channel channel, struct timeval *maxtv,
struct query *query;
struct list_node* list_head;
struct list_node* list_node;
- time_t now;
- time_t offset, min_offset; /* these use time_t since some 32 bit systems
- still use 64 bit time_t! (like VS2005) */
+ struct timeval now;
+ struct timeval nextstop;
+ long offset, min_offset;
/* No queries, no timeout (and no fetch of the current time). */
if (ares__is_list_empty(&(channel->all_queries)))
return maxtv;
/* Find the minimum timeout for the current set of queries. */
- time(&now);
+ now = ares__tvnow();
min_offset = -1;
list_head = &(channel->all_queries);
@@ -54,23 +54,26 @@ struct timeval *ares_timeout(ares_channel channel, struct timeval *maxtv,
list_node = list_node->next)
{
query = list_node->data;
- if (query->timeout == 0)
+ if (query->timeout.tv_sec == 0)
continue;
- offset = query->timeout - now;
+ offset = ares__timeoffset(&now, &query->timeout);
if (offset < 0)
offset = 0;
if (min_offset == -1 || offset < min_offset)
min_offset = offset;
}
- /* If we found a minimum timeout and it's sooner than the one
- * specified in maxtv (if any), return it. Otherwise go with
- * maxtv.
+ if(min_offset != -1) {
+ nextstop = now;
+ ares__timeadd(&now, min_offset);
+ }
+
+ /* If we found a minimum timeout and it's sooner than the one specified in
+ * maxtv (if any), return it. Otherwise go with maxtv.
*/
- if (min_offset != -1 && (!maxtv || min_offset <= maxtv->tv_sec))
+ if (min_offset != -1 && (!maxtv || ares__timedout(maxtv, &nextstop)))
{
- tvbuf->tv_sec = (long)min_offset;
- tvbuf->tv_usec = 0;
+ *tvbuf = nextstop;
return tvbuf;
}
else