diff options
Diffstat (limited to 'ares_process.c')
-rw-r--r-- | ares_process.c | 527 |
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); } |