From dd06c60ada039faafd937a136e3dc7ff9c41159a Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 3 May 2006 06:11:44 +0000 Subject: Nick Mathewson added the ARES_OPT_SOCK_STATE_CB option that when set makes c-ares call a callback on socket state changes. A better way than the ares_getsock() to get full control over the socket state. --- ares/CHANGES | 10 ++++++++-- ares/ares.h | 15 +++++++++++++++ ares/ares__close_sockets.c | 4 +++- ares/ares_cancel.c | 2 +- ares/ares_destroy.c | 2 +- ares/ares_init.3 | 18 ++++++++++++++++++ ares/ares_init.c | 6 ++++++ ares/ares_private.h | 11 ++++++++++- ares/ares_process.c | 22 +++++++++++++++++----- 9 files changed, 79 insertions(+), 11 deletions(-) diff --git a/ares/CHANGES b/ares/CHANGES index 11c2955c5..3c91e27d0 100644 --- a/ares/CHANGES +++ b/ares/CHANGES @@ -1,5 +1,11 @@ Changelog for the c-ares project +* May 3, 2006 + +- Nick Mathewson added the ARES_OPT_SOCK_STATE_CB option that when set makes + c-ares call a callback on socket state changes. A better way than the + ares_getsock() to get full control over the socket state. + * January 9, 2006 - Alexander Lazic improved the getservbyport_r() configure check. @@ -28,8 +34,8 @@ - Added constants that will be used by ares_getaddrinfo -- Made ares_getnameinfo use the reentrant getservbyport (getservbyport_r) if it is - available to ensure it works properly in a threaded environment. +- Made ares_getnameinfo use the reentrant getservbyport (getservbyport_r) if it + is available to ensure it works properly in a threaded environment. * September 10 diff --git a/ares/ares.h b/ares/ares.h index 8e8da2847..9414f9b78 100644 --- a/ares/ares.h +++ b/ares/ares.h @@ -93,6 +93,7 @@ extern "C" { #define ARES_OPT_SERVERS (1 << 6) #define ARES_OPT_DOMAINS (1 << 7) #define ARES_OPT_LOOKUPS (1 << 8) +#define ARES_OPT_SOCK_STATE_CB (1 << 9) /* Nameinfo flag values */ #define ARES_NI_NOFQDN (1 << 0) @@ -135,6 +136,18 @@ extern "C" { #define ARES_GETSOCK_WRITABLE(bits,num) (bits & (1 << ((num) + \ ARES_GETSOCK_MAXNUM))) +#ifdef WIN32 +typedef void (*ares_sock_state_cb)(void *data, + SOCKET socket, + int readable, + int writable); +#else +typedef void (*ares_sock_state_cb)(void *data, + int socket, + int readable, + int writable); +#endif + struct ares_options { int flags; int timeout; @@ -147,6 +160,8 @@ struct ares_options { char **domains; int ndomains; char *lookups; + ares_sock_state_cb sock_state_cb; + void *sock_state_cb_data; }; struct hostent; diff --git a/ares/ares__close_sockets.c b/ares/ares__close_sockets.c index fb883f9a9..7475a4b44 100644 --- a/ares/ares__close_sockets.c +++ b/ares/ares__close_sockets.c @@ -23,7 +23,7 @@ #include "ares.h" #include "ares_private.h" -void ares__close_sockets(struct server_state *server) +void ares__close_sockets(ares_channel channel, struct server_state *server) { struct send_request *sendreq; @@ -46,11 +46,13 @@ void ares__close_sockets(struct server_state *server) /* Close the TCP and UDP sockets. */ if (server->tcp_socket != ARES_SOCKET_BAD) { + SOCK_STATE_CALLBACK(channel, server->tcp_socket, 0, 0); closesocket(server->tcp_socket); server->tcp_socket = ARES_SOCKET_BAD; } if (server->udp_socket != ARES_SOCKET_BAD) { + SOCK_STATE_CALLBACK(channel, server->udp_socket, 0, 0); closesocket(server->udp_socket); server->udp_socket = ARES_SOCKET_BAD; } diff --git a/ares/ares_cancel.c b/ares/ares_cancel.c index 04642848d..4df1e9f5b 100644 --- a/ares/ares_cancel.c +++ b/ares/ares_cancel.c @@ -38,6 +38,6 @@ void ares_cancel(ares_channel channel) if (!(channel->flags & ARES_FLAG_STAYOPEN)) { for (i = 0; i < channel->nservers; i++) - ares__close_sockets(&channel->servers[i]); + ares__close_sockets(channel, &channel->servers[i]); } } diff --git a/ares/ares_destroy.c b/ares/ares_destroy.c index efe2b5383..d3860da13 100644 --- a/ares/ares_destroy.c +++ b/ares/ares_destroy.c @@ -24,7 +24,7 @@ void ares_destroy(ares_channel channel) struct query *query; for (i = 0; i < channel->nservers; i++) - ares__close_sockets(&channel->servers[i]); + ares__close_sockets(channel, &channel->servers[i]); free(channel->servers); for (i = 0; i < channel->ndomains; i++) free(channel->domains[i]); diff --git a/ares/ares_init.3 b/ares/ares_init.3 index 1f252f22b..026d8da0e 100644 --- a/ares/ares_init.3 +++ b/ares/ares_init.3 @@ -98,6 +98,24 @@ The lookups to perform for host queries. .I lookups should be set to a string of the characters "b" or "f", where "b" indicates a DNS lookup and "f" indicates a lookup in the hosts file. +.TP 18 +.B ARES_OPT_SOCK_STATE_CB +.B void (*\fIsock_state_cb\fP)(void *data, int s, int read, int write); +.br +.B void *\fIsock_state_cb_data\fP; +.br +A callback function to be invoked when a socket changes state. +.I s +will be passed the socket whose state has changed; +.I read +will be set to true if the socket should listen for read events, and +.I write +will be set to true if the socket should listen for write events. +The value of +.I sock_state_cb_data +will be passed as the +.I data +argument. .PP The .I flags diff --git a/ares/ares_init.c b/ares/ares_init.c index a6a672343..2f320a2fa 100644 --- a/ares/ares_init.c +++ b/ares/ares_init.c @@ -113,6 +113,7 @@ int ares_init_options(ares_channel *channelptr, struct ares_options *options, channel->queries = NULL; channel->domains = NULL; channel->sortlist = NULL; + channel->sock_state_cb = NULL; /* Initialize configuration by each of the four sources, from highest * precedence to lowest. @@ -192,6 +193,11 @@ static int init_by_options(ares_channel channel, struct ares_options *options, channel->udp_port = options->udp_port; if ((optmask & ARES_OPT_TCP_PORT) && channel->tcp_port == -1) channel->tcp_port = options->tcp_port; + if ((optmask & ARES_OPT_SOCK_STATE_CB) && channel->sock_state_cb == NULL) + { + channel->sock_state_cb = options->sock_state_cb; + channel->sock_state_cb_data = options->sock_state_cb_data; + } /* Copy the servers, if given. */ if ((optmask & ARES_OPT_SERVERS) && channel->nservers == -1) diff --git a/ares/ares_private.h b/ares/ares_private.h index df51266dc..8d5af0817 100644 --- a/ares/ares_private.h +++ b/ares/ares_private.h @@ -176,13 +176,22 @@ struct ares_channeldata { /* Active queries */ struct query *queries; + + ares_sock_state_cb sock_state_cb; + void *sock_state_cb_data; }; void ares__send_query(ares_channel channel, struct query *query, time_t now); -void ares__close_sockets(struct server_state *server); +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); +#define SOCK_STATE_CALLBACK(c, s, r, w) \ + do { \ + if ((c)->sock_state_cb) \ + (c)->sock_state_cb((c)->sock_state_cb_data, (s), (r), (w)); \ + } while (0) + #ifdef CURLDEBUG /* This is low-level hard-hacking memory leak tracking and similar. Using the libcurl lowlevel code from within library is ugly and only works when diff --git a/ares/ares_process.c b/ares/ares_process.c index 456d95218..f38e591f2 100644 --- a/ares/ares_process.c +++ b/ares/ares_process.c @@ -149,7 +149,10 @@ static void write_tcp_data(ares_channel channel, fd_set *write_fds, time_t now) wcount -= sendreq->len; server->qhead = sendreq->next; if (server->qhead == NULL) - server->qtail = NULL; + { + SOCK_STATE_CALLBACK(channel, server->tcp_socket, 1, 0); + server->qtail = NULL; + } free(sendreq); } else @@ -179,7 +182,10 @@ static void write_tcp_data(ares_channel channel, fd_set *write_fds, time_t now) { server->qhead = sendreq->next; if (server->qhead == NULL) - server->qtail = NULL; + { + SOCK_STATE_CALLBACK(channel, server->tcp_socket, 1, 0); + server->qtail = NULL; + } free(sendreq); } else @@ -380,7 +386,7 @@ static void handle_error(ares_channel channel, int whichserver, time_t now) struct query *query, *next; /* Reset communications with this server. */ - ares__close_sockets(&channel->servers[whichserver]); + ares__close_sockets(channel, &channel->servers[whichserver]); /* Tell all queries talking to this server to move on and not try * this server again. @@ -452,7 +458,10 @@ void ares__send_query(ares_channel channel, struct query *query, time_t now) if (server->qtail) server->qtail->next = sendreq; else - server->qhead = sendreq; + { + SOCK_STATE_CALLBACK(channel, server->tcp_socket, 1, 1); + server->qhead = sendreq; + } server->qtail = sendreq; query->timeout = 0; } @@ -575,6 +584,7 @@ static int open_tcp_socket(ares_channel channel, struct server_state *server) } } + SOCK_STATE_CALLBACK(channel, s, 1, 0); server->tcp_buffer_pos = 0; server->tcp_socket = s; return 0; @@ -604,6 +614,8 @@ static int open_udp_socket(ares_channel channel, struct server_state *server) return -1; } + SOCK_STATE_CALLBACK(channel, s, 1, 0); + server->udp_socket = s; return 0; } @@ -714,7 +726,7 @@ static struct query *end_query (ares_channel channel, struct query *query, int s if (!channel->queries && !(channel->flags & ARES_FLAG_STAYOPEN)) { for (i = 0; i < channel->nservers; i++) - ares__close_sockets(&channel->servers[i]); + ares__close_sockets(channel, &channel->servers[i]); } return (next); } -- cgit v1.2.3