diff --git a/ares/CHANGES b/ares/CHANGES index b4580c773..3f607b880 100644 --- a/ares/CHANGES +++ b/ares/CHANGES @@ -1,6 +1,16 @@ Changelog for the c-ares project +* May 30 2007 + +- Daniel S added ares_process_fd() to allow applications to ask for processing + on specific sockets and thus avoiding select() and associated + functions/macros. This function will be used by upcoming libcurl releases + for this very reason. It also made me export the ares_socket_t type in the + public ares.h header file, since ares_process_fd() uses that type for two of + the arguments. + * May 25 2007 + - Ravi Pratap fixed a flaw in the init_by_resolv_conf() function for windows that could cause it to return a bad return code. diff --git a/ares/ares.h b/ares/ares.h index 83a1c8e46..73fc3e6c3 100644 --- a/ares/ares.h +++ b/ares/ares.h @@ -136,6 +136,22 @@ extern "C" { #define ARES_GETSOCK_WRITABLE(bits,num) (bits & (1 << ((num) + \ ARES_GETSOCK_MAXNUM))) + +/* + * Typedef our socket type + */ + +#ifndef ares_socket_typedef +#ifdef WIN32 +typedef SOCKET ares_socket_t; +#define ARES_SOCKET_BAD INVALID_SOCKET +#else +typedef int ares_socket_t; +#define ARES_SOCKET_BAD -1 +#endif +#define ares_socket_typedef +#endif /* ares_socket_typedef */ + #ifdef WIN32 typedef void (*ares_sock_state_cb)(void *data, SOCKET socket, @@ -200,6 +216,8 @@ int ares_getsock(ares_channel channel, int *socks, int numsocks); struct timeval *ares_timeout(ares_channel channel, struct timeval *maxtv, struct timeval *tv); void ares_process(ares_channel channel, fd_set *read_fds, fd_set *write_fds); +void ares_process_fd(ares_channel channel, ares_socket_t read_fd, + ares_socket_t write_fd); int ares_mkquery(const char *name, int dnsclass, int type, unsigned short id, int rd, unsigned char **buf, int *buflen); diff --git a/ares/ares_process.3 b/ares/ares_process.3 index 50216e144..c9cd5477f 100644 --- a/ares/ares_process.3 +++ b/ares/ares_process.3 @@ -24,16 +24,16 @@ ares_process \- Process events for name resolution .B void ares_process(ares_channel \fIchannel\fP, fd_set *\fIread_fds\fP, .B fd_set *\fIwrite_fds\fP) .fi +.PP +.B void ares_process_fd(ares_channel \fIchannel\fP, +.B ares_socket_t \fIread_fd\fP, +.B ares_socket_t \fIwrite_fd\fP) +.fi .SH DESCRIPTION -The -.B ares_process -function handles input/output events and timeouts associated with -queries pending on the name service channel identified by +The \fBares_process(3)\fP function handles input/output events and timeouts +associated with queries pending on the name service channel identified by .IR channel . -The file descriptor sets pointed to by -.I read_fds -and -.I write_fds +The file descriptor sets pointed to by \fIread_fds\fP and \fIwrite_fds\fP should have file descriptors set in them according to whether the file descriptors specified by \fIares_fds(3)\fP are ready for reading and writing. (The easiest way to determine this information is to invoke @@ -44,6 +44,11 @@ The .B ares_process function will invoke callbacks for pending queries if they complete successfully or fail. + +\fBares_process_fd(3)\fP works the same way but acts and operates only on the +specific file descriptors (sockets) you pass in to the function. Use +ARES_SOCKET_BAD for "no action". This function is of course provided to allow +users of c-ares to void select() in their applications and within c-ares. .SS EXAMPLE The following code fragment waits for all pending queries on a channel to complete: diff --git a/ares/ares_process.c b/ares/ares_process.c index 8cb51c3b2..efc462a40 100644 --- a/ares/ares_process.c +++ b/ares/ares_process.c @@ -56,10 +56,11 @@ static int try_again(int errnum); static void write_tcp_data(ares_channel channel, fd_set *write_fds, - time_t now); -static void read_tcp_data(ares_channel channel, fd_set *read_fds, time_t now); + ares_socket_t write_fd, time_t now); +static void read_tcp_data(ares_channel channel, fd_set *read_fds, + ares_socket_t read_fd, time_t now); static void read_udp_packets(ares_channel channel, fd_set *read_fds, - time_t now); + ares_socket_t read_fd, time_t now); static void process_timeouts(ares_channel channel, time_t now); static void process_answer(ares_channel channel, unsigned char *abuf, int alen, int whichserver, int tcp, int now); @@ -80,12 +81,30 @@ 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, now); - read_tcp_data(channel, read_fds, now); - read_udp_packets(channel, read_fds, 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); } +/* Something interesting happened on the wire, or there was a timeout. + * See what's up and respond accordingly. + */ +void ares_process_fd(ares_channel channel, + ares_socket_t read_fd, /* use ARES_SOCKET_BAD or valid + file descriptors */ + ares_socket_t write_fd) +{ + time_t now; + + 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); +} + + /* Return 1 if the specified error number describes a readiness error, or 0 * otherwise. This is mostly for HP-UX, which could return EAGAIN or * EWOULDBLOCK. See this man page @@ -114,7 +133,10 @@ static int try_again(int errnum) /* If any TCP sockets select true for writing, write out queued data * we have for them. */ -static void write_tcp_data(ares_channel channel, fd_set *write_fds, time_t now) +static void write_tcp_data(ares_channel channel, + fd_set *write_fds, + ares_socket_t write_fd, + time_t now) { struct server_state *server; struct send_request *sendreq; @@ -124,14 +146,27 @@ static void write_tcp_data(ares_channel channel, fd_set *write_fds, time_t now) ssize_t wcount; size_t n; + if(!write_fds && (write_fd == ARES_SOCKET_BAD)) + /* no possible action */ + return; + for (i = 0; i < channel->nservers; i++) { - /* Make sure server has data to send and is selected in write_fds. */ + /* 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 - || !FD_ISSET(server->tcp_socket, write_fds)) + if (!server->qhead || server->tcp_socket == ARES_SOCKET_BAD) continue; + if(write_fds) { + if(!FD_ISSET(server->tcp_socket, write_fds)) + continue; + } + else { + if(server->tcp_socket != write_fd) + continue; + } + /* Count the number of send queue items. */ n = 0; for (sendreq = server->qhead; sendreq; sendreq = sendreq->next) @@ -218,20 +253,33 @@ static void write_tcp_data(ares_channel channel, fd_set *write_fds, time_t now) * allocate a buffer if we finish reading the length word, and process * a packet if we finish reading one. */ -static void read_tcp_data(ares_channel channel, fd_set *read_fds, time_t now) +static void read_tcp_data(ares_channel channel, fd_set *read_fds, + ares_socket_t read_fd, time_t now) { struct server_state *server; int i; ssize_t count; + if(!read_fds && (read_fd == ARES_SOCKET_BAD)) + /* no possible action */ + return; + for (i = 0; i < channel->nservers; i++) { /* Make sure the server has a socket and is selected in read_fds. */ server = &channel->servers[i]; - if (server->tcp_socket == ARES_SOCKET_BAD || - !FD_ISSET(server->tcp_socket, read_fds)) + if (server->tcp_socket == ARES_SOCKET_BAD) continue; + if(read_fds) { + if(!FD_ISSET(server->tcp_socket, read_fds)) + continue; + } + else { + if(server->tcp_socket != read_fd) + continue; + } + if (server->tcp_lenbuf_pos != 2) { /* We haven't yet read a length word, so read that (or @@ -294,22 +342,34 @@ static void read_tcp_data(ares_channel channel, fd_set *read_fds, time_t now) /* If any UDP sockets select true for reading, process them. */ static void read_udp_packets(ares_channel channel, fd_set *read_fds, - time_t now) + ares_socket_t read_fd, time_t now) { struct server_state *server; int i; ssize_t count; unsigned char buf[PACKETSZ + 1]; + if(!read_fds && (read_fd == ARES_SOCKET_BAD)) + /* no possible action */ + return; + for (i = 0; i < channel->nservers; i++) { /* Make sure the server has a socket and is selected in read_fds. */ server = &channel->servers[i]; - if (server->udp_socket == ARES_SOCKET_BAD || - !FD_ISSET(server->udp_socket, read_fds)) + if (server->udp_socket == ARES_SOCKET_BAD) continue; + if(read_fds) { + if(!FD_ISSET(server->udp_socket, read_fds)) + continue; + } + else { + if(server->udp_socket != read_fd) + continue; + } + count = sread(server->udp_socket, buf, sizeof(buf)); if (count == -1 && try_again(SOCKERRNO)) continue; diff --git a/ares/setup.h b/ares/setup.h index 465e012fe..2106df770 100644 --- a/ares/setup.h +++ b/ares/setup.h @@ -3,7 +3,7 @@ /* $Id$ */ -/* Copyright (C) 2004 - 2005 by Daniel Stenberg et al +/* Copyright (C) 2004 - 2007 by Daniel Stenberg et al * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, provided @@ -117,18 +117,6 @@ #undef VERSION #undef PACKAGE -/* - * Typedef our socket type - */ - -#ifdef USE_WINSOCK -typedef SOCKET ares_socket_t; -#define ARES_SOCKET_BAD INVALID_SOCKET -#else -typedef int ares_socket_t; -#define ARES_SOCKET_BAD -1 -#endif - /* * Assume a few thing unless they're set by configure */