aboutsummaryrefslogtreecommitdiff
path: root/ares_process.c
diff options
context:
space:
mode:
Diffstat (limited to 'ares_process.c')
-rw-r--r--ares_process.c527
1 files changed, 340 insertions, 187 deletions
diff --git a/ares_process.c b/ares_process.c
index e5efa5f..df85524 100644
--- a/ares_process.c
+++ b/ares_process.c
@@ -1,6 +1,6 @@
/* Copyright 1998 by the Massachusetts Institute of Technology.
- * Copyright (C) 2004-2010 by Daniel Stenberg
+ * Copyright (C) 2004-2017 by Daniel Stenberg
*
* Permission to use, copy, modify, and distribute this
* software and its documentation for any purpose and without
@@ -17,9 +17,6 @@
#include "ares_setup.h"
-#ifdef HAVE_SYS_SOCKET_H
-# include <sys/socket.h>
-#endif
#ifdef HAVE_SYS_UIO_H
# include <sys/uio.h>
#endif
@@ -32,6 +29,9 @@
#ifdef HAVE_NETDB_H
# include <netdb.h>
#endif
+#ifdef HAVE_ARPA_INET_H
+# include <arpa/inet.h>
+#endif
#ifdef HAVE_ARPA_NAMESER_H
# include <arpa/nameser.h>
#else
@@ -41,16 +41,9 @@
# include <arpa/nameser_compat.h>
#endif
-#ifdef HAVE_SYS_TIME_H
-# include <sys/time.h>
-#endif
-
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
-#ifdef HAVE_UNISTD_H
-# include <unistd.h>
-#endif
#ifdef HAVE_SYS_IOCTL_H
# include <sys/ioctl.h>
#endif
@@ -59,10 +52,7 @@
#endif
#include <assert.h>
-#include <string.h>
-#include <stdlib.h>
#include <fcntl.h>
-#include <time.h>
#include "ares.h"
#include "ares_dns.h"
@@ -78,7 +68,7 @@ static void read_tcp_data(ares_channel channel, fd_set *read_fds,
static void read_udp_packets(ares_channel channel, fd_set *read_fds,
ares_socket_t read_fd, struct timeval *now);
static void advance_tcp_send_queue(ares_channel channel, int whichserver,
- ssize_t num_bytes);
+ ares_ssize_t num_bytes);
static void process_timeouts(ares_channel channel, struct timeval *now);
static void process_broken_connections(ares_channel channel,
struct timeval *now);
@@ -115,8 +105,7 @@ int ares__timedout(struct timeval *now,
}
/* add the specific number of milliseconds to the time in the first argument */
-int ares__timeadd(struct timeval *now,
- int millisecs)
+static void timeadd(struct timeval *now, int millisecs)
{
now->tv_sec += millisecs/1000;
now->tv_usec += (millisecs%1000)*1000;
@@ -125,19 +114,8 @@ int ares__timeadd(struct timeval *now,
++(now->tv_sec);
now->tv_usec -= 1000000;
}
-
- return 0;
-}
-
-/* return time offset between now and (future) check, in milliseconds */
-long ares__timeoffset(struct timeval *now,
- struct timeval *check)
-{
- return (check->tv_sec - now->tv_sec)*1000 +
- (check->tv_usec - now->tv_usec)/1000;
}
-
/*
* generic process function
*/
@@ -200,6 +178,26 @@ static int try_again(int errnum)
return 0;
}
+static ares_ssize_t socket_writev(ares_channel channel, ares_socket_t s, const struct iovec * vec, int len)
+{
+ if (channel->sock_funcs)
+ return channel->sock_funcs->asendv(s, vec, len, channel->sock_func_cb_data);
+
+ return writev(s, vec, len);
+}
+
+static ares_ssize_t socket_write(ares_channel channel, ares_socket_t s, const void * data, size_t len)
+{
+ if (channel->sock_funcs)
+ {
+ struct iovec vec;
+ vec.iov_base = (void*)data;
+ vec.iov_len = len;
+ return channel->sock_funcs->asendv(s, &vec, 1, channel->sock_func_cb_data);
+ }
+ return swrite(s, data, len);
+}
+
/* If any TCP sockets select true for writing, write out queued data
* we have for them.
*/
@@ -212,8 +210,8 @@ static void write_tcp_data(ares_channel channel,
struct send_request *sendreq;
struct iovec *vec;
int i;
- ssize_t scount;
- ssize_t wcount;
+ ares_ssize_t scount;
+ ares_ssize_t wcount;
size_t n;
if(!write_fds && (write_fd == ARES_SOCKET_BAD))
@@ -252,7 +250,7 @@ static void write_tcp_data(ares_channel channel,
n++;
/* Allocate iovecs so we can send all our data at once. */
- vec = malloc(n * sizeof(struct iovec));
+ vec = ares_malloc(n * sizeof(struct iovec));
if (vec)
{
/* Fill in the iovecs and send. */
@@ -263,12 +261,12 @@ static void write_tcp_data(ares_channel channel,
vec[n].iov_len = sendreq->len;
n++;
}
- wcount = (ssize_t)writev(server->tcp_socket, vec, (int)n);
- free(vec);
+ wcount = socket_writev(channel, server->tcp_socket, vec, (int)n);
+ ares_free(vec);
if (wcount < 0)
{
if (!try_again(SOCKERRNO))
- handle_error(channel, i, now);
+ handle_error(channel, i, now);
continue;
}
@@ -280,11 +278,11 @@ static void write_tcp_data(ares_channel channel,
/* Can't allocate iovecs; just send the first request. */
sendreq = server->qhead;
- scount = swrite(server->tcp_socket, sendreq->data, sendreq->len);
+ scount = socket_write(channel, server->tcp_socket, sendreq->data, sendreq->len);
if (scount < 0)
{
if (!try_again(SOCKERRNO))
- handle_error(channel, i, now);
+ handle_error(channel, i, now);
continue;
}
@@ -296,7 +294,7 @@ static void write_tcp_data(ares_channel channel,
/* Consume the given number of bytes from the head of the TCP send queue. */
static void advance_tcp_send_queue(ares_channel channel, int whichserver,
- ssize_t num_bytes)
+ ares_ssize_t num_bytes)
{
struct send_request *sendreq;
struct server_state *server = &channel->servers[whichserver];
@@ -306,8 +304,8 @@ static void advance_tcp_send_queue(ares_channel channel, int whichserver,
num_bytes -= sendreq->len;
server->qhead = sendreq->next;
if (sendreq->data_storage)
- free(sendreq->data_storage);
- free(sendreq);
+ ares_free(sendreq->data_storage);
+ ares_free(sendreq);
if (server->qhead == NULL) {
SOCK_STATE_CALLBACK(channel, server->tcp_socket, 1, 0);
server->qtail = NULL;
@@ -324,6 +322,38 @@ static void advance_tcp_send_queue(ares_channel channel, int whichserver,
}
}
+static ares_ssize_t socket_recvfrom(ares_channel channel,
+ ares_socket_t s,
+ void * data,
+ size_t data_len,
+ int flags,
+ struct sockaddr *from,
+ ares_socklen_t *from_len)
+{
+ if (channel->sock_funcs)
+ return channel->sock_funcs->arecvfrom(s, data, data_len,
+ flags, from, from_len,
+ channel->sock_func_cb_data);
+
+#ifdef HAVE_RECVFROM
+ return recvfrom(s, data, data_len, flags, from, from_len);
+#else
+ return sread(s, data, data_len);
+#endif
+}
+
+static ares_ssize_t socket_recv(ares_channel channel,
+ ares_socket_t s,
+ void * data,
+ size_t data_len)
+{
+ if (channel->sock_funcs)
+ return channel->sock_funcs->arecvfrom(s, data, data_len, 0, 0, 0,
+ channel->sock_func_cb_data);
+
+ return sread(s, data, data_len);
+}
+
/* If any TCP socket selects true for reading, read some data,
* allocate a buffer if we finish reading the length word, and process
* a packet if we finish reading one.
@@ -333,7 +363,7 @@ static void read_tcp_data(ares_channel channel, fd_set *read_fds,
{
struct server_state *server;
int i;
- ssize_t count;
+ ares_ssize_t count;
if(!read_fds && (read_fd == ARES_SOCKET_BAD))
/* no possible action */
@@ -356,11 +386,11 @@ static void read_tcp_data(ares_channel channel, fd_set *read_fds,
}
if(read_fds)
- /* If there's an error and we close this socket, then open
- * another with the same fd to talk to another server, then we
- * don't want to think that it was the new socket that was
- * ready. This is not disastrous, but is likely to result in
- * extra system calls and confusion. */
+ /* If there's an error and we close this socket, then open another
+ * with the same fd to talk to another server, then we don't want to
+ * think that it was the new socket that was ready. This is not
+ * disastrous, but is likely to result in extra system calls and
+ * confusion. */
FD_CLR(server->tcp_socket, read_fds);
if (server->tcp_lenbuf_pos != 2)
@@ -368,13 +398,13 @@ static void read_tcp_data(ares_channel channel, fd_set *read_fds,
/* We haven't yet read a length word, so read that (or
* what's left to read of it).
*/
- count = sread(server->tcp_socket,
- server->tcp_lenbuf + server->tcp_lenbuf_pos,
- 2 - server->tcp_lenbuf_pos);
+ count = socket_recv(channel, server->tcp_socket,
+ server->tcp_lenbuf + server->tcp_lenbuf_pos,
+ 2 - server->tcp_lenbuf_pos);
if (count <= 0)
{
if (!(count == -1 && try_again(SOCKERRNO)))
- handle_error(channel, i, now);
+ handle_error(channel, i, now);
continue;
}
@@ -386,22 +416,25 @@ static void read_tcp_data(ares_channel channel, fd_set *read_fds,
*/
server->tcp_length = server->tcp_lenbuf[0] << 8
| server->tcp_lenbuf[1];
- server->tcp_buffer = malloc(server->tcp_length);
- if (!server->tcp_buffer)
+ server->tcp_buffer = ares_malloc(server->tcp_length);
+ if (!server->tcp_buffer) {
handle_error(channel, i, now);
+ return; /* bail out on malloc failure. TODO: make this
+ function return error codes */
+ }
server->tcp_buffer_pos = 0;
}
}
else
{
/* Read data into the allocated buffer. */
- count = sread(server->tcp_socket,
- server->tcp_buffer + server->tcp_buffer_pos,
- server->tcp_length - server->tcp_buffer_pos);
+ count = socket_recv(channel, server->tcp_socket,
+ server->tcp_buffer + server->tcp_buffer_pos,
+ server->tcp_length - server->tcp_buffer_pos);
if (count <= 0)
{
if (!(count == -1 && try_again(SOCKERRNO)))
- handle_error(channel, i, now);
+ handle_error(channel, i, now);
continue;
}
@@ -413,8 +446,7 @@ static void read_tcp_data(ares_channel channel, fd_set *read_fds,
*/
process_answer(channel, server->tcp_buffer, server->tcp_length,
i, 1, now);
- if (server->tcp_buffer)
- free(server->tcp_buffer);
+ ares_free(server->tcp_buffer);
server->tcp_buffer = NULL;
server->tcp_lenbuf_pos = 0;
server->tcp_buffer_pos = 0;
@@ -429,8 +461,8 @@ static void read_udp_packets(ares_channel channel, fd_set *read_fds,
{
struct server_state *server;
int i;
- ssize_t count;
- unsigned char buf[PACKETSZ + 1];
+ ares_ssize_t count;
+ unsigned char buf[MAXENDSSZ + 1];
#ifdef HAVE_RECVFROM
ares_socklen_t fromlen;
union {
@@ -472,25 +504,27 @@ static void read_udp_packets(ares_channel channel, fd_set *read_fds,
/* To reduce event loop overhead, read and process as many
* packets as we can. */
do {
-#ifdef HAVE_RECVFROM
- if (server->addr.family == AF_INET)
- fromlen = sizeof(from.sa4);
- else
- fromlen = sizeof(from.sa6);
- count = (ssize_t)recvfrom(server->udp_socket, (void *)buf, sizeof(buf),
- 0, &from.sa, &fromlen);
-#else
- count = sread(server->udp_socket, buf, sizeof(buf));
-#endif
+ if (server->udp_socket == ARES_SOCKET_BAD)
+ count = 0;
+
+ else {
+ if (server->addr.family == AF_INET)
+ fromlen = sizeof(from.sa4);
+ else
+ fromlen = sizeof(from.sa6);
+ count = socket_recvfrom(channel, server->udp_socket, (void *)buf,
+ sizeof(buf), 0, &from.sa, &fromlen);
+ }
+
if (count == -1 && try_again(SOCKERRNO))
continue;
else if (count <= 0)
handle_error(channel, i, now);
#ifdef HAVE_RECVFROM
else if (!same_address(&from.sa, &server->addr))
- /* The address the response comes from does not match
- * the address we sent the request to. Someone may be
- * attempting to perform a cache poisoning attack. */
+ /* The address the response comes from does not match the address we
+ * sent the request to. Someone may be attempting to perform a cache
+ * poisoning attack. */
break;
#endif
else
@@ -507,11 +541,10 @@ static void process_timeouts(ares_channel channel, struct timeval *now)
struct list_node* list_head;
struct list_node* list_node;
- /* Process all the timeouts that have fired since the last time we
- * processed timeouts. If things are going well, then we'll have
- * hundreds/thousands of queries that fall into future buckets, and
- * only a handful of requests that fall into the "now" bucket, so
- * this should be quite quick.
+ /* Process all the timeouts that have fired since the last time we processed
+ * timeouts. If things are going well, then we'll have hundreds/thousands of
+ * queries that fall into future buckets, and 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->tv_sec; t++)
{
@@ -536,7 +569,7 @@ static void process_answer(ares_channel channel, unsigned char *abuf,
int alen, int whichserver, int tcp,
struct timeval *now)
{
- int tc, rcode;
+ int tc, rcode, packetsz;
unsigned short id;
struct query *query;
struct list_node* list_head;
@@ -553,11 +586,10 @@ static void process_answer(ares_channel channel, unsigned char *abuf,
rcode = DNS_HEADER_RCODE(abuf);
/* Find the query corresponding to this packet. The queries are
- * hashed/bucketed by query id, so this lookup should be quick.
- * Note that both the query id and the questions must be the same;
- * when the query id wraps around we can have multiple outstanding
- * queries with the same query id, so we need to check both the id and
- * question.
+ * hashed/bucketed by query id, so this lookup should be quick. Note that
+ * both the query id and the questions must be the same; when the query id
+ * wraps around we can have multiple outstanding queries with the same query
+ * id, so we need to check both the id and question.
*/
query = NULL;
list_head = &(channel->queries_by_qid[id % ARES_QID_TABLE_SIZE]);
@@ -574,11 +606,35 @@ static void process_answer(ares_channel channel, unsigned char *abuf,
if (!query)
return;
+ packetsz = PACKETSZ;
+ /* If we use EDNS and server answers with one of these RCODES, the protocol
+ * extension is not understood by the responder. We must retry the query
+ * without EDNS enabled.
+ */
+ if (channel->flags & ARES_FLAG_EDNS)
+ {
+ packetsz = channel->ednspsz;
+ if (rcode == NOTIMP || rcode == FORMERR || rcode == SERVFAIL)
+ {
+ int qlen = (query->tcplen - 2) - EDNSFIXEDSZ;
+ channel->flags ^= ARES_FLAG_EDNS;
+ query->tcplen -= EDNSFIXEDSZ;
+ query->qlen -= EDNSFIXEDSZ;
+ query->tcpbuf[0] = (unsigned char)((qlen >> 8) & 0xff);
+ query->tcpbuf[1] = (unsigned char)(qlen & 0xff);
+ DNS_HEADER_SET_ARCOUNT(query->tcpbuf + 2, 0);
+ query->tcpbuf = ares_realloc(query->tcpbuf, query->tcplen);
+ query->qbuf = query->tcpbuf + 2;
+ ares__send_query(channel, query, now);
+ return;
+ }
+ }
+
/* If we got a truncated UDP packet and are not ignoring truncation,
* don't accept the packet, and switch the query to TCP if we hadn't
* done so already.
*/
- if ((tc || alen > PACKETSZ) && !tcp && !(channel->flags & ARES_FLAG_IGNTC))
+ if ((tc || alen > packetsz) && !tcp && !(channel->flags & ARES_FLAG_IGNTC))
{
if (!query->using_tcp)
{
@@ -591,8 +647,8 @@ static void process_answer(ares_channel channel, unsigned char *abuf,
/* Limit alen to PACKETSZ if we aren't using TCP (only relevant if we
* are ignoring truncation.
*/
- if (alen > PACKETSZ && !tcp)
- alen = PACKETSZ;
+ if (alen > packetsz && !tcp)
+ alen = packetsz;
/* If we aren't passing through all error packets, discard packets
* with SERVFAIL, NOTIMP, or REFUSED response codes.
@@ -626,6 +682,31 @@ static void process_broken_connections(ares_channel channel,
}
}
+/* Swap the contents of two lists */
+static void swap_lists(struct list_node* head_a,
+ struct list_node* head_b)
+{
+ int is_a_empty = ares__is_list_empty(head_a);
+ int is_b_empty = ares__is_list_empty(head_b);
+ struct list_node old_a = *head_a;
+ struct list_node old_b = *head_b;
+
+ if (is_a_empty) {
+ ares__init_list_head(head_b);
+ } else {
+ *head_b = old_a;
+ old_a.next->prev = head_b;
+ old_a.prev->next = head_b;
+ }
+ if (is_b_empty) {
+ ares__init_list_head(head_a);
+ } else {
+ *head_a = old_b;
+ old_b.next->prev = head_a;
+ old_b.prev->next = head_a;
+ }
+}
+
static void handle_error(ares_channel channel, int whichserver,
struct timeval *now)
{
@@ -639,15 +720,14 @@ static void handle_error(ares_channel channel, int whichserver,
/* Reset communications with this server. */
ares__close_sockets(channel, server);
- /* Tell all queries talking to this server to move on and not try
- * this server again. We steal the current list of queries that were
- * in-flight to this server, since when we call next_server this can
- * cause the queries to be re-sent to this server, which will
- * re-insert these queries in that same server->queries_to_server
- * list.
+ /* Tell all queries talking to this server to move on and not try this
+ * server again. We steal the current list of queries that were in-flight to
+ * this server, since when we call next_server this can cause the queries to
+ * be re-sent to this server, which will re-insert these queries in that
+ * same server->queries_to_server list.
*/
ares__init_list_head(&list_head);
- ares__swap_lists(&list_head, &(server->queries_to_server));
+ swap_lists(&list_head, &(server->queries_to_server));
for (list_node = list_head.next; list_node != &list_head; )
{
query = list_node->data;
@@ -663,14 +743,15 @@ static void handle_error(ares_channel channel, int whichserver,
}
static void skip_server(ares_channel channel, struct query *query,
- int whichserver) {
- /* The given server gave us problems with this query, so if we have
- * the luxury of using other servers, then let's skip the
- * potentially broken server and just use the others. If we only
- * have one server and we need to retry then we should just go ahead
- * and re-use that server, since it's our only hope; perhaps we
- * just got unlucky, and retrying will work (eg, the server timed
- * out our TCP connection just as we were sending another request).
+ int whichserver)
+{
+ /* The given server gave us problems with this query, so if we have the
+ * luxury of using other servers, then let's skip the potentially broken
+ * server and just use the others. If we only have one server and we need to
+ * retry then we should just go ahead and re-use that server, since it's our
+ * only hope; perhaps we just got unlucky, and retrying will work (eg, the
+ * server timed out our TCP connection just as we were sending another
+ * request).
*/
if (channel->nservers > 1)
{
@@ -693,11 +774,10 @@ static void next_server(ares_channel channel, struct query *query,
query->server = (query->server + 1) % channel->nservers;
server = &channel->servers[query->server];
- /* We don't want to use this server if (1) we decided this
- * connection is broken, and thus about to be closed, (2)
- * we've decided to skip this server because of earlier
- * errors we encountered, or (3) we already sent this query
- * over this exact connection.
+ /* We don't want to use this server if (1) we decided this connection is
+ * broken, and thus about to be closed, (2) we've decided to skip this
+ * server because of earlier errors we encountered, or (3) we already
+ * sent this query over this exact connection.
*/
if (!server->is_broken &&
!query->server_info[query->server].skip_server &&
@@ -709,11 +789,11 @@ static void next_server(ares_channel channel, struct query *query,
return;
}
- /* You might think that with TCP we only need one try. However,
- * even when using TCP, servers can time-out our connection just
- * as we're sending a request, or close our connection because
- * they die, or never send us a reply because they get wedged or
- * tickle a bug that drops our request.
+ /* You might think that with TCP we only need one try. However, even
+ * when using TCP, servers can time-out our connection just as we're
+ * sending a request, or close our connection because they die, or never
+ * send us a reply because they get wedged or tickle a bug that drops
+ * our request.
*/
}
@@ -743,17 +823,18 @@ void ares__send_query(ares_channel channel, struct query *query,
return;
}
}
- sendreq = calloc(1, sizeof(struct send_request));
+ sendreq = ares_malloc(sizeof(struct send_request));
if (!sendreq)
{
end_query(channel, query, ARES_ENOMEM, NULL, 0);
return;
}
- /* To make the common case fast, we avoid copies by using the
- * query's tcpbuf for as long as the query is alive. In the rare
- * case where the query ends while it's queued for transmission,
- * then we give the sendreq its own copy of the request packet
- * and put it in sendreq->data_storage.
+ memset(sendreq, 0, sizeof(struct send_request));
+ /* To make the common case fast, we avoid copies by using the query's
+ * tcpbuf for as long as the query is alive. In the rare case where the
+ * query ends while it's queued for transmission, then we give the
+ * sendreq its own copy of the request packet and put it in
+ * sendreq->data_storage.
*/
sendreq->data_storage = NULL;
sendreq->data = query->tcpbuf;
@@ -782,7 +863,7 @@ void ares__send_query(ares_channel channel, struct query *query,
return;
}
}
- if (swrite(server->udp_socket, query->qbuf, query->qlen) == -1)
+ if (socket_write(channel, server->udp_socket, query->qbuf, query->qlen) == -1)
{
/* FIXME: Handle EAGAIN here since it likely can happen. */
skip_server(channel, query, query->server);
@@ -793,8 +874,7 @@ void ares__send_query(ares_channel channel, struct query *query,
timeplus = channel->timeout << (query->try_count / channel->nservers);
timeplus = (timeplus * (9 + (rand () & 7))) / 16;
query->timeout = *now;
- ares__timeadd(&query->timeout,
- timeplus);
+ timeadd(&query->timeout, timeplus);
/* Keep track of queries bucketed by timeout, so we can process
* timeout events quickly.
*/
@@ -818,7 +898,7 @@ void ares__send_query(ares_channel channel, struct query *query,
* portable.
*/
static int setsocknonblock(ares_socket_t sockfd, /* operate on this */
- int nonblock /* TRUE or FALSE */)
+ int nonblock /* TRUE or FALSE */)
{
#if defined(USE_BLOCKING_SOCKETS)
@@ -832,35 +912,34 @@ static int setsocknonblock(ares_socket_t sockfd, /* operate on this */
if (FALSE != nonblock)
return fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
else
- return fcntl(sockfd, F_SETFL, flags & (~O_NONBLOCK));
+ return fcntl(sockfd, F_SETFL, flags & (~O_NONBLOCK)); /* LCOV_EXCL_LINE */
#elif defined(HAVE_IOCTL_FIONBIO)
/* older unix versions */
- int flags;
- flags = nonblock;
+ int flags = nonblock ? 1 : 0;
return ioctl(sockfd, FIONBIO, &flags);
#elif defined(HAVE_IOCTLSOCKET_FIONBIO)
#ifdef WATT32
- char flags;
+ char flags = nonblock ? 1 : 0;
#else
/* Windows */
- unsigned long flags;
+ unsigned long flags = nonblock ? 1UL : 0UL;
#endif
- flags = nonblock;
return ioctlsocket(sockfd, FIONBIO, &flags);
#elif defined(HAVE_IOCTLSOCKET_CAMEL_FIONBIO)
/* Amiga */
- return IoctlSocket(sockfd, FIONBIO, (long)nonblock);
+ long flags = nonblock ? 1L : 0L;
+ return IoctlSocket(sockfd, FIONBIO, flags);
#elif defined(HAVE_SETSOCKOPT_SO_NONBLOCK)
/* BeOS */
- long b = nonblock ? 1 : 0;
+ long b = nonblock ? 1L : 0L;
return setsockopt(sockfd, SOL_SOCKET, SO_NONBLOCK, &b, sizeof(b));
#else
@@ -876,12 +955,16 @@ static int configure_socket(ares_socket_t s, int family, ares_channel channel)
struct sockaddr_in6 sa6;
} local;
- setsocknonblock(s, TRUE);
+ /* do not set options for user-managed sockets */
+ if (channel->sock_funcs)
+ return 0;
+
+ (void)setsocknonblock(s, TRUE);
#if defined(FD_CLOEXEC) && !defined(MSDOS)
/* Configure the socket fd as close-on-exec. */
if (fcntl(s, F_SETFD, FD_CLOEXEC) == -1)
- return -1;
+ return -1; /* LCOV_EXCL_LINE */
#endif
/* Set the socket's send and receive buffer sizes. */
@@ -917,10 +1000,12 @@ static int configure_socket(ares_socket_t s, int family, ares_channel channel)
}
}
else if (family == AF_INET6) {
- if (memcmp(channel->local_ip6, &ares_in6addr_any, sizeof(channel->local_ip6)) != 0) {
+ if (memcmp(channel->local_ip6, &ares_in6addr_any,
+ sizeof(channel->local_ip6)) != 0) {
memset(&local.sa6, 0, sizeof(local.sa6));
local.sa6.sin6_family = AF_INET6;
- memcpy(&local.sa6.sin6_addr, channel->local_ip6, sizeof(channel->local_ip6));
+ memcpy(&local.sa6.sin6_addr, channel->local_ip6,
+ sizeof(channel->local_ip6));
if (bind(s, &local.sa, sizeof(local.sa6)) < 0)
return -1;
}
@@ -929,6 +1014,30 @@ static int configure_socket(ares_socket_t s, int family, ares_channel channel)
return 0;
}
+static ares_socket_t open_socket(ares_channel channel, int af, int type, int protocol)
+{
+ if (channel->sock_funcs != 0)
+ return channel->sock_funcs->asocket(af,
+ type,
+ protocol,
+ channel->sock_func_cb_data);
+
+ return socket(af, type, protocol);
+}
+
+static int connect_socket(ares_channel channel, ares_socket_t sockfd,
+ const struct sockaddr * addr,
+ ares_socklen_t addrlen)
+{
+ if (channel->sock_funcs != 0)
+ return channel->sock_funcs->aconnect(sockfd,
+ addr,
+ addrlen,
+ channel->sock_func_cb_data);
+
+ return connect(sockfd, addr, addrlen);
+}
+
static int open_tcp_socket(ares_channel channel, struct server_state *server)
{
ares_socket_t s;
@@ -947,7 +1056,11 @@ static int open_tcp_socket(ares_channel channel, struct server_state *server)
salen = sizeof(saddr.sa4);
memset(sa, 0, salen);
saddr.sa4.sin_family = AF_INET;
- saddr.sa4.sin_port = (unsigned short)(channel->tcp_port & 0xffff);
+ if (server->addr.tcp_port) {
+ saddr.sa4.sin_port = aresx_sitous(server->addr.tcp_port);
+ } else {
+ saddr.sa4.sin_port = aresx_sitous(channel->tcp_port);
+ }
memcpy(&saddr.sa4.sin_addr, &server->addr.addrV4,
sizeof(server->addr.addrV4));
break;
@@ -956,23 +1069,27 @@ static int open_tcp_socket(ares_channel channel, struct server_state *server)
salen = sizeof(saddr.sa6);
memset(sa, 0, salen);
saddr.sa6.sin6_family = AF_INET6;
- saddr.sa6.sin6_port = (unsigned short)(channel->tcp_port & 0xffff);
+ if (server->addr.tcp_port) {
+ saddr.sa6.sin6_port = aresx_sitous(server->addr.tcp_port);
+ } else {
+ saddr.sa6.sin6_port = aresx_sitous(channel->tcp_port);
+ }
memcpy(&saddr.sa6.sin6_addr, &server->addr.addrV6,
sizeof(server->addr.addrV6));
break;
default:
- return -1;
+ return -1; /* LCOV_EXCL_LINE */
}
/* Acquire a socket. */
- s = socket(server->addr.family, SOCK_STREAM, 0);
+ s = open_socket(channel, server->addr.family, SOCK_STREAM, 0);
if (s == ARES_SOCKET_BAD)
return -1;
/* Configure it. */
if (configure_socket(s, server->addr.family, channel) < 0)
{
- sclose(s);
+ ares__socket_close(channel, s);
return -1;
}
@@ -984,22 +1101,35 @@ static int open_tcp_socket(ares_channel channel, struct server_state *server)
* so batching isn't very interesting.
*/
opt = 1;
- if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY,
- (void *)&opt, sizeof(opt)) == -1)
+ if (channel->sock_funcs == 0
+ &&
+ setsockopt(s, IPPROTO_TCP, TCP_NODELAY,
+ (void *)&opt, sizeof(opt)) == -1)
{
- sclose(s);
+ ares__socket_close(channel, s);
return -1;
}
#endif
+ if (channel->sock_config_cb)
+ {
+ int err = channel->sock_config_cb(s, SOCK_STREAM,
+ channel->sock_config_cb_data);
+ if (err < 0)
+ {
+ ares__socket_close(channel, s);
+ return err;
+ }
+ }
+
/* Connect to the server. */
- if (connect(s, sa, salen) == -1)
+ if (connect_socket(channel, s, sa, salen) == -1)
{
int err = SOCKERRNO;
if (err != EINPROGRESS && err != EWOULDBLOCK)
{
- sclose(s);
+ ares__socket_close(channel, s);
return -1;
}
}
@@ -1010,7 +1140,7 @@ static int open_tcp_socket(ares_channel channel, struct server_state *server)
channel->sock_create_cb_data);
if (err < 0)
{
- sclose(s);
+ ares__socket_close(channel, s);
return err;
}
}
@@ -1039,7 +1169,11 @@ static int open_udp_socket(ares_channel channel, struct server_state *server)
salen = sizeof(saddr.sa4);
memset(sa, 0, salen);
saddr.sa4.sin_family = AF_INET;
- saddr.sa4.sin_port = (unsigned short)(channel->udp_port & 0xffff);
+ if (server->addr.udp_port) {
+ saddr.sa4.sin_port = aresx_sitous(server->addr.udp_port);
+ } else {
+ saddr.sa4.sin_port = aresx_sitous(channel->udp_port);
+ }
memcpy(&saddr.sa4.sin_addr, &server->addr.addrV4,
sizeof(server->addr.addrV4));
break;
@@ -1048,34 +1182,49 @@ static int open_udp_socket(ares_channel channel, struct server_state *server)
salen = sizeof(saddr.sa6);
memset(sa, 0, salen);
saddr.sa6.sin6_family = AF_INET6;
- saddr.sa6.sin6_port = (unsigned short)(channel->udp_port & 0xffff);
+ if (server->addr.udp_port) {
+ saddr.sa6.sin6_port = aresx_sitous(server->addr.udp_port);
+ } else {
+ saddr.sa6.sin6_port = aresx_sitous(channel->udp_port);
+ }
memcpy(&saddr.sa6.sin6_addr, &server->addr.addrV6,
sizeof(server->addr.addrV6));
break;
default:
- return -1;
+ return -1; /* LCOV_EXCL_LINE */
}
/* Acquire a socket. */
- s = socket(server->addr.family, SOCK_DGRAM, 0);
+ s = open_socket(channel, server->addr.family, SOCK_DGRAM, 0);
if (s == ARES_SOCKET_BAD)
return -1;
/* Set the socket non-blocking. */
if (configure_socket(s, server->addr.family, channel) < 0)
{
- sclose(s);
+ ares__socket_close(channel, s);
return -1;
}
+ if (channel->sock_config_cb)
+ {
+ int err = channel->sock_config_cb(s, SOCK_DGRAM,
+ channel->sock_config_cb_data);
+ if (err < 0)
+ {
+ ares__socket_close(channel, s);
+ return err;
+ }
+ }
+
/* Connect to the server. */
- if (connect(s, sa, salen) == -1)
+ if (connect_socket(channel, s, sa, salen) == -1)
{
int err = SOCKERRNO;
if (err != EINPROGRESS && err != EWOULDBLOCK)
{
- sclose(s);
+ ares__socket_close(channel, s);
return -1;
}
}
@@ -1086,7 +1235,7 @@ static int open_udp_socket(ares_channel channel, struct server_state *server)
channel->sock_create_cb_data);
if (err < 0)
{
- sclose(s);
+ ares__socket_close(channel, s);
return err;
}
}
@@ -1130,7 +1279,7 @@ static int same_questions(const unsigned char *qbuf, int qlen,
q.p += q.namelen;
if (q.p + QFIXEDSZ > qbuf + qlen)
{
- free(q.name);
+ ares_free(q.name);
return 0;
}
q.type = DNS_QUESTION_TYPE(q.p);
@@ -1145,14 +1294,14 @@ static int same_questions(const unsigned char *qbuf, int qlen,
if (ares_expand_name(a.p, abuf, alen, &a.name, &a.namelen)
!= ARES_SUCCESS)
{
- free(q.name);
+ ares_free(q.name);
return 0;
}
a.p += a.namelen;
if (a.p + QFIXEDSZ > abuf + alen)
{
- free(q.name);
- free(a.name);
+ ares_free(q.name);
+ ares_free(a.name);
return 0;
}
a.type = DNS_QUESTION_TYPE(a.p);
@@ -1163,13 +1312,13 @@ static int same_questions(const unsigned char *qbuf, int qlen,
if (strcasecmp(q.name, a.name) == 0 && q.type == a.type
&& q.dnsclass == a.dnsclass)
{
- free(a.name);
+ ares_free(a.name);
break;
}
- free(a.name);
+ ares_free(a.name);
}
- free(q.name);
+ ares_free(q.name);
if (j == a.qdcount)
return 0;
}
@@ -1198,7 +1347,7 @@ static int same_address(struct sockaddr *sa, struct ares_addr *aa)
return 1; /* match */
break;
default:
- break;
+ break; /* LCOV_EXCL_LINE */
}
}
return 0; /* different */
@@ -1223,21 +1372,19 @@ static void end_query (ares_channel channel, struct query *query, int status,
assert(sendreq->data_storage == NULL);
if (status == ARES_SUCCESS)
{
- /* We got a reply for this query, but this queued
- * sendreq points into this soon-to-be-gone query's
- * tcpbuf. Probably this means we timed out and queued
- * the query for retransmission, then received a
- * response before actually retransmitting. This is
- * perfectly fine, so we want to keep the connection
- * running smoothly if we can. But in the worst case
- * we may have sent only some prefix of the query,
- * with some suffix of the query left to send. Also,
- * the buffer may be queued on multiple queues. To
- * prevent dangling pointers to the query's tcpbuf and
- * handle these cases, we just give such sendreqs
- * their own copy of the query packet.
+ /* We got a reply for this query, but this queued sendreq
+ * points into this soon-to-be-gone query's tcpbuf. Probably
+ * this means we timed out and queued the query for
+ * retransmission, then received a response before actually
+ * retransmitting. This is perfectly fine, so we want to keep
+ * the connection running smoothly if we can. But in the worst
+ * case we may have sent only some prefix of the query, with
+ * some suffix of the query left to send. Also, the buffer may
+ * be queued on multiple queues. To prevent dangling pointers
+ * to the query's tcpbuf and handle these cases, we just give
+ * such sendreqs their own copy of the query packet.
*/
- sendreq->data_storage = malloc(sendreq->len);
+ sendreq->data_storage = ares_malloc(sendreq->len);
if (sendreq->data_storage != NULL)
{
memcpy(sendreq->data_storage, sendreq->data, sendreq->len);
@@ -1246,14 +1393,12 @@ static void end_query (ares_channel channel, struct query *query, int status,
}
if ((status != ARES_SUCCESS) || (sendreq->data_storage == NULL))
{
- /* We encountered an error (probably a timeout,
- * suggesting the DNS server we're talking to is
- * probably unreachable, wedged, or severely
- * overloaded) or we couldn't copy the request, so
- * mark the connection as broken. When we get to
- * process_broken_connections() we'll close the
- * connection and try to re-send requests to another
- * server.
+ /* We encountered an error (probably a timeout, suggesting the
+ * DNS server we're talking to is probably unreachable,
+ * wedged, or severely overloaded) or we couldn't copy the
+ * request, so mark the connection as broken. When we get to
+ * process_broken_connections() we'll close the connection and
+ * try to re-send requests to another server.
*/
server->is_broken = 1;
/* Just to be paranoid, zero out this sendreq... */
@@ -1267,8 +1412,8 @@ static void end_query (ares_channel channel, struct query *query, int status,
query->callback(query->arg, status, query->timeouts, abuf, alen);
ares__free_query(query);
- /* Simple cleanup policy: if no queries are remaining, close all
- * network sockets unless STAYOPEN is set.
+ /* Simple cleanup policy: if no queries are remaining, close all network
+ * sockets unless STAYOPEN is set.
*/
if (!(channel->flags & ARES_FLAG_STAYOPEN) &&
ares__is_list_empty(&(channel->all_queries)))
@@ -1289,7 +1434,15 @@ void ares__free_query(struct query *query)
query->callback = NULL;
query->arg = NULL;
/* Deallocate the memory associated with the query */
- free(query->tcpbuf);
- free(query->server_info);
- free(query);
+ ares_free(query->tcpbuf);
+ ares_free(query->server_info);
+ ares_free(query);
+}
+
+void ares__socket_close(ares_channel channel, ares_socket_t s)
+{
+ if (channel->sock_funcs)
+ channel->sock_funcs->aclose(s, channel->sock_func_cb_data);
+ else
+ sclose(s);
}