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