|  |  | 
|  | /* Copyright 1998 by the Massachusetts Institute of Technology. | 
|  | * Copyright (C) 2004-2016 by Daniel Stenberg | 
|  | * | 
|  | * Permission to use, copy, modify, and distribute this | 
|  | * software and its documentation for any purpose and without | 
|  | * fee is hereby granted, provided that the above copyright | 
|  | * notice appear in all copies and that both that copyright | 
|  | * notice and this permission notice appear in supporting | 
|  | * documentation, and that the name of M.I.T. not be used in | 
|  | * advertising or publicity pertaining to distribution of the | 
|  | * software without specific, written prior permission. | 
|  | * M.I.T. makes no representations about the suitability of | 
|  | * this software for any purpose.  It is provided "as is" | 
|  | * without express or implied warranty. | 
|  | */ | 
|  |  | 
|  | #include "ares_setup.h" | 
|  |  | 
|  | #ifdef HAVE_SYS_UIO_H | 
|  | #  include <sys/uio.h> | 
|  | #endif | 
|  | #ifdef HAVE_NETINET_IN_H | 
|  | #  include <netinet/in.h> | 
|  | #endif | 
|  | #ifdef HAVE_NETINET_TCP_H | 
|  | #  include <netinet/tcp.h> | 
|  | #endif | 
|  | #ifdef HAVE_NETDB_H | 
|  | #  include <netdb.h> | 
|  | #endif | 
|  | #ifdef HAVE_ARPA_NAMESER_H | 
|  | #  include <arpa/nameser.h> | 
|  | #else | 
|  | #  include "nameser.h" | 
|  | #endif | 
|  | #ifdef HAVE_ARPA_NAMESER_COMPAT_H | 
|  | #  include <arpa/nameser_compat.h> | 
|  | #endif | 
|  |  | 
|  | #ifdef HAVE_STRINGS_H | 
|  | #  include <strings.h> | 
|  | #endif | 
|  | #ifdef HAVE_SYS_IOCTL_H | 
|  | #  include <sys/ioctl.h> | 
|  | #endif | 
|  | #ifdef NETWARE | 
|  | #  include <sys/filio.h> | 
|  | #endif | 
|  |  | 
|  | #include <assert.h> | 
|  | #include <fcntl.h> | 
|  |  | 
|  | #include "ares.h" | 
|  | #include "ares_dns.h" | 
|  | #include "ares_nowarn.h" | 
|  | #include "ares_private.h" | 
|  |  | 
|  |  | 
|  | static int try_again(int errnum); | 
|  | static void write_tcp_data(ares_channel channel, fd_set *write_fds, | 
|  | 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, struct timeval *now); | 
|  | 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); | 
|  | 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, | 
|  | 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, | 
|  | struct timeval *now); | 
|  | static int open_tcp_socket(ares_channel channel, struct server_state *server); | 
|  | static int open_udp_socket(ares_channel channel, struct server_state *server); | 
|  | static int same_questions(const unsigned char *qbuf, int qlen, | 
|  | const unsigned char *abuf, int alen); | 
|  | static int same_address(struct sockaddr *sa, struct ares_addr *aa); | 
|  | 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) | 
|  | { | 
|  | long secs = (now->tv_sec - check->tv_sec); | 
|  |  | 
|  | if(secs > 0) | 
|  | return 1; /* yes, timed out */ | 
|  | if(secs < 0) | 
|  | 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 */ | 
|  | static void 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; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * generic process function | 
|  | */ | 
|  | static void processfds(ares_channel channel, | 
|  | fd_set *read_fds, ares_socket_t read_fd, | 
|  | fd_set *write_fds, ares_socket_t write_fd) | 
|  | { | 
|  | struct timeval now = ares__tvnow(); | 
|  |  | 
|  | write_tcp_data(channel, write_fds, write_fd, &now); | 
|  | read_tcp_data(channel, read_fds, read_fd, &now); | 
|  | read_udp_packets(channel, read_fds, read_fd, &now); | 
|  | process_timeouts(channel, &now); | 
|  | process_broken_connections(channel, &now); | 
|  | } | 
|  |  | 
|  | /* 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) | 
|  | { | 
|  | processfds(channel, read_fds, ARES_SOCKET_BAD, write_fds, ARES_SOCKET_BAD); | 
|  | } | 
|  |  | 
|  | /* 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) | 
|  | { | 
|  | processfds(channel, NULL, read_fd, NULL, write_fd); | 
|  | } | 
|  |  | 
|  |  | 
|  | /* 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 | 
|  | * | 
|  | * http://devrsrc1.external.hp.com/STKS/cgi-bin/man2html? | 
|  | *     manpage=/usr/share/man/man2.Z/send.2 | 
|  | */ | 
|  | static int try_again(int errnum) | 
|  | { | 
|  | #if !defined EWOULDBLOCK && !defined EAGAIN | 
|  | #error "Neither EWOULDBLOCK nor EAGAIN defined" | 
|  | #endif | 
|  | switch (errnum) | 
|  | { | 
|  | #ifdef EWOULDBLOCK | 
|  | case EWOULDBLOCK: | 
|  | return 1; | 
|  | #endif | 
|  | #if defined EAGAIN && EAGAIN != EWOULDBLOCK | 
|  | case EAGAIN: | 
|  | return 1; | 
|  | #endif | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* 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, | 
|  | ares_socket_t write_fd, | 
|  | struct timeval *now) | 
|  | { | 
|  | struct server_state *server; | 
|  | struct send_request *sendreq; | 
|  | struct iovec *vec; | 
|  | int i; | 
|  | ssize_t scount; | 
|  | 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 or | 
|  | write_fd. */ | 
|  | server = &channel->servers[i]; | 
|  | if (!server->qhead || server->tcp_socket == ARES_SOCKET_BAD || | 
|  | server->is_broken) | 
|  | continue; | 
|  |  | 
|  | if(write_fds) { | 
|  | if(!FD_ISSET(server->tcp_socket, write_fds)) | 
|  | continue; | 
|  | } | 
|  | else { | 
|  | if(server->tcp_socket != write_fd) | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if(write_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. */ | 
|  | FD_CLR(server->tcp_socket, write_fds); | 
|  |  | 
|  | /* Count the number of send queue items. */ | 
|  | n = 0; | 
|  | for (sendreq = server->qhead; sendreq; sendreq = sendreq->next) | 
|  | n++; | 
|  |  | 
|  | /* Allocate iovecs so we can send all our data at once. */ | 
|  | vec = ares_malloc(n * sizeof(struct iovec)); | 
|  | if (vec) | 
|  | { | 
|  | /* Fill in the iovecs and send. */ | 
|  | n = 0; | 
|  | for (sendreq = server->qhead; sendreq; sendreq = sendreq->next) | 
|  | { | 
|  | vec[n].iov_base = (char *) sendreq->data; | 
|  | vec[n].iov_len = sendreq->len; | 
|  | n++; | 
|  | } | 
|  | wcount = (ssize_t)writev(server->tcp_socket, vec, (int)n); | 
|  | ares_free(vec); | 
|  | if (wcount < 0) | 
|  | { | 
|  | if (!try_again(SOCKERRNO)) | 
|  | handle_error(channel, i, now); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | /* Advance the send queue by as many bytes as we sent. */ | 
|  | advance_tcp_send_queue(channel, i, wcount); | 
|  | } | 
|  | else | 
|  | { | 
|  | /* Can't allocate iovecs; just send the first request. */ | 
|  | sendreq = server->qhead; | 
|  |  | 
|  | scount = swrite(server->tcp_socket, sendreq->data, sendreq->len); | 
|  | if (scount < 0) | 
|  | { | 
|  | if (!try_again(SOCKERRNO)) | 
|  | handle_error(channel, i, now); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | /* Advance the send queue by as many bytes as we sent. */ | 
|  | advance_tcp_send_queue(channel, i, scount); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* 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) | 
|  | { | 
|  | struct send_request *sendreq; | 
|  | struct server_state *server = &channel->servers[whichserver]; | 
|  | while (num_bytes > 0) { | 
|  | sendreq = server->qhead; | 
|  | if ((size_t)num_bytes >= sendreq->len) { | 
|  | num_bytes -= sendreq->len; | 
|  | server->qhead = sendreq->next; | 
|  | if (sendreq->data_storage) | 
|  | ares_free(sendreq->data_storage); | 
|  | ares_free(sendreq); | 
|  | if (server->qhead == NULL) { | 
|  | SOCK_STATE_CALLBACK(channel, server->tcp_socket, 1, 0); | 
|  | server->qtail = NULL; | 
|  |  | 
|  | /* qhead is NULL so we cannot continue this loop */ | 
|  | break; | 
|  | } | 
|  | } | 
|  | else { | 
|  | sendreq->data += num_bytes; | 
|  | sendreq->len -= num_bytes; | 
|  | num_bytes = 0; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* 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. | 
|  | */ | 
|  | static void read_tcp_data(ares_channel channel, fd_set *read_fds, | 
|  | ares_socket_t read_fd, struct timeval *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 || server->is_broken) | 
|  | continue; | 
|  |  | 
|  | if(read_fds) { | 
|  | if(!FD_ISSET(server->tcp_socket, read_fds)) | 
|  | continue; | 
|  | } | 
|  | else { | 
|  | if(server->tcp_socket != read_fd) | 
|  | continue; | 
|  | } | 
|  |  | 
|  | 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. */ | 
|  | FD_CLR(server->tcp_socket, read_fds); | 
|  |  | 
|  | if (server->tcp_lenbuf_pos != 2) | 
|  | { | 
|  | /* 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); | 
|  | if (count <= 0) | 
|  | { | 
|  | if (!(count == -1 && try_again(SOCKERRNO))) | 
|  | handle_error(channel, i, now); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | server->tcp_lenbuf_pos += (int)count; | 
|  | if (server->tcp_lenbuf_pos == 2) | 
|  | { | 
|  | /* We finished reading the length word.  Decode the | 
|  | * length and allocate a buffer for the data. | 
|  | */ | 
|  | server->tcp_length = server->tcp_lenbuf[0] << 8 | 
|  | | server->tcp_lenbuf[1]; | 
|  | 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); | 
|  | if (count <= 0) | 
|  | { | 
|  | if (!(count == -1 && try_again(SOCKERRNO))) | 
|  | handle_error(channel, i, now); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | server->tcp_buffer_pos += (int)count; | 
|  | if (server->tcp_buffer_pos == server->tcp_length) | 
|  | { | 
|  | /* We finished reading this answer; process it and | 
|  | * prepare to read another length word. | 
|  | */ | 
|  | process_answer(channel, server->tcp_buffer, server->tcp_length, | 
|  | i, 1, now); | 
|  | ares_free(server->tcp_buffer); | 
|  | server->tcp_buffer = NULL; | 
|  | server->tcp_lenbuf_pos = 0; | 
|  | server->tcp_buffer_pos = 0; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* 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, struct timeval *now) | 
|  | { | 
|  | struct server_state *server; | 
|  | int i; | 
|  | ssize_t count; | 
|  | unsigned char buf[MAXENDSSZ + 1]; | 
|  | #ifdef HAVE_RECVFROM | 
|  | ares_socklen_t fromlen; | 
|  | union { | 
|  | struct sockaddr     sa; | 
|  | struct sockaddr_in  sa4; | 
|  | struct sockaddr_in6 sa6; | 
|  | } from; | 
|  | #endif | 
|  |  | 
|  | 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 || server->is_broken) | 
|  | continue; | 
|  |  | 
|  | if(read_fds) { | 
|  | if(!FD_ISSET(server->udp_socket, read_fds)) | 
|  | continue; | 
|  | } | 
|  | else { | 
|  | if(server->udp_socket != read_fd) | 
|  | continue; | 
|  | } | 
|  |  | 
|  | 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. */ | 
|  | FD_CLR(server->udp_socket, read_fds); | 
|  |  | 
|  | /* To reduce event loop overhead, read and process as many | 
|  | * packets as we can. */ | 
|  | do { | 
|  | if (server->udp_socket == ARES_SOCKET_BAD) | 
|  | count = 0; | 
|  |  | 
|  | else { | 
|  | #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 (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. */ | 
|  | break; | 
|  | #endif | 
|  | else | 
|  | process_answer(channel, buf, (int)count, i, 0, now); | 
|  | } while (count > 0); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* If any queries have timed out, note the timeout and move them on. */ | 
|  | static void process_timeouts(ares_channel channel, struct timeval *now) | 
|  | { | 
|  | time_t t;  /* the time of the timeouts we're processing */ | 
|  | struct query *query; | 
|  | 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. | 
|  | */ | 
|  | 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.tv_sec && ares__timedout(now, &query->timeout)) | 
|  | { | 
|  | query->error_status = ARES_ETIMEOUT; | 
|  | ++query->timeouts; | 
|  | next_server(channel, query, 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, | 
|  | struct timeval *now) | 
|  | { | 
|  | int tc, rcode, packetsz; | 
|  | unsigned short id; | 
|  | struct query *query; | 
|  | struct list_node* list_head; | 
|  | struct list_node* list_node; | 
|  |  | 
|  | /* If there's no room in the answer for a header, we can't do much | 
|  | * with it. */ | 
|  | if (alen < HFIXEDSZ) | 
|  | return; | 
|  |  | 
|  | /* Grab the query ID, truncate bit, and response code from the packet. */ | 
|  | id = DNS_HEADER_QID(abuf); | 
|  | tc = DNS_HEADER_TC(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. | 
|  | */ | 
|  | query = NULL; | 
|  | list_head = &(channel->queries_by_qid[id % ARES_QID_TABLE_SIZE]); | 
|  | for (list_node = list_head->next; list_node != list_head; | 
|  | list_node = list_node->next) | 
|  | { | 
|  | struct query *q = list_node->data; | 
|  | if ((q->qid == id) && same_questions(q->qbuf, q->qlen, abuf, alen)) | 
|  | { | 
|  | query = q; | 
|  | break; | 
|  | } | 
|  | } | 
|  | 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 (!query->using_tcp) | 
|  | { | 
|  | query->using_tcp = 1; | 
|  | ares__send_query(channel, query, now); | 
|  | } | 
|  | return; | 
|  | } | 
|  |  | 
|  | /* Limit alen to PACKETSZ if we aren't using TCP (only relevant if we | 
|  | * are ignoring truncation. | 
|  | */ | 
|  | if (alen > packetsz && !tcp) | 
|  | alen = packetsz; | 
|  |  | 
|  | /* If we aren't passing through all error packets, discard packets | 
|  | * with SERVFAIL, NOTIMP, or REFUSED response codes. | 
|  | */ | 
|  | if (!(channel->flags & ARES_FLAG_NOCHECKRESP)) | 
|  | { | 
|  | if (rcode == SERVFAIL || rcode == NOTIMP || rcode == REFUSED) | 
|  | { | 
|  | skip_server(channel, query, whichserver); | 
|  | if (query->server == whichserver) | 
|  | next_server(channel, query, now); | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | end_query(channel, query, ARES_SUCCESS, abuf, alen); | 
|  | } | 
|  |  | 
|  | /* Close all the connections that are no longer usable. */ | 
|  | static void process_broken_connections(ares_channel channel, | 
|  | struct timeval *now) | 
|  | { | 
|  | int i; | 
|  | for (i = 0; i < channel->nservers; i++) | 
|  | { | 
|  | struct server_state *server = &channel->servers[i]; | 
|  | if (server->is_broken) | 
|  | { | 
|  | handle_error(channel, i, now); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* 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) | 
|  | { | 
|  | struct server_state *server; | 
|  | struct query *query; | 
|  | struct list_node list_head; | 
|  | struct list_node* list_node; | 
|  |  | 
|  | server = &channel->servers[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. | 
|  | */ | 
|  | ares__init_list_head(&list_head); | 
|  | swap_lists(&list_head, &(server->queries_to_server)); | 
|  | 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 */ | 
|  | assert(query->server == whichserver); | 
|  | skip_server(channel, query, whichserver); | 
|  | next_server(channel, query, now); | 
|  | } | 
|  | /* Each query should have removed itself from our temporary list as | 
|  | * it re-sent itself or finished up... | 
|  | */ | 
|  | assert(ares__is_list_empty(&list_head)); | 
|  | } | 
|  |  | 
|  | 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). | 
|  | */ | 
|  | if (channel->nservers > 1) | 
|  | { | 
|  | query->server_info[whichserver].skip_server = 1; | 
|  | } | 
|  | } | 
|  |  | 
|  | static void next_server(ares_channel channel, struct query *query, | 
|  | struct timeval *now) | 
|  | { | 
|  | /* We need to try each server channel->tries times. We have channel->nservers | 
|  | * servers to try. In total, we need to do channel->nservers * channel->tries | 
|  | * attempts. Use query->try to remember how many times we already attempted | 
|  | * this query. Use modular arithmetic to find the next server to try. */ | 
|  | while (++(query->try_count) < (channel->nservers * channel->tries)) | 
|  | { | 
|  | struct server_state *server; | 
|  |  | 
|  | /* Move on to the next server. */ | 
|  | 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. | 
|  | */ | 
|  | if (!server->is_broken && | 
|  | !query->server_info[query->server].skip_server && | 
|  | !(query->using_tcp && | 
|  | (query->server_info[query->server].tcp_connection_generation == | 
|  | server->tcp_connection_generation))) | 
|  | { | 
|  | ares__send_query(channel, query, now); | 
|  | 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. | 
|  | */ | 
|  | } | 
|  |  | 
|  | /* If we are here, all attempts to perform query failed. */ | 
|  | end_query(channel, query, query->error_status, NULL, 0); | 
|  | } | 
|  |  | 
|  | void ares__send_query(ares_channel channel, struct query *query, | 
|  | struct timeval *now) | 
|  | { | 
|  | struct send_request *sendreq; | 
|  | struct server_state *server; | 
|  | int timeplus; | 
|  |  | 
|  | server = &channel->servers[query->server]; | 
|  | if (query->using_tcp) | 
|  | { | 
|  | /* Make sure the TCP socket for this server is set up and queue | 
|  | * a send request. | 
|  | */ | 
|  | if (server->tcp_socket == ARES_SOCKET_BAD) | 
|  | { | 
|  | if (open_tcp_socket(channel, server) == -1) | 
|  | { | 
|  | skip_server(channel, query, query->server); | 
|  | next_server(channel, query, now); | 
|  | return; | 
|  | } | 
|  | } | 
|  | sendreq = ares_malloc(sizeof(struct send_request)); | 
|  | if (!sendreq) | 
|  | { | 
|  | end_query(channel, query, ARES_ENOMEM, NULL, 0); | 
|  | return; | 
|  | } | 
|  | 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; | 
|  | sendreq->len = query->tcplen; | 
|  | sendreq->owner_query = query; | 
|  | sendreq->next = NULL; | 
|  | if (server->qtail) | 
|  | server->qtail->next = sendreq; | 
|  | else | 
|  | { | 
|  | SOCK_STATE_CALLBACK(channel, server->tcp_socket, 1, 1); | 
|  | server->qhead = sendreq; | 
|  | } | 
|  | server->qtail = sendreq; | 
|  | query->server_info[query->server].tcp_connection_generation = | 
|  | server->tcp_connection_generation; | 
|  | } | 
|  | else | 
|  | { | 
|  | if (server->udp_socket == ARES_SOCKET_BAD) | 
|  | { | 
|  | if (open_udp_socket(channel, server) == -1) | 
|  | { | 
|  | skip_server(channel, query, query->server); | 
|  | next_server(channel, query, now); | 
|  | return; | 
|  | } | 
|  | } | 
|  | if (swrite(server->udp_socket, query->qbuf, query->qlen) == -1) | 
|  | { | 
|  | /* FIXME: Handle EAGAIN here since it likely can happen. */ | 
|  | skip_server(channel, query, query->server); | 
|  | next_server(channel, query, now); | 
|  | return; | 
|  | } | 
|  | } | 
|  | timeplus = channel->timeout << (query->try_count / channel->nservers); | 
|  | timeplus = (timeplus * (9 + (rand () & 7))) / 16; | 
|  | query->timeout = *now; | 
|  | timeadd(&query->timeout, timeplus); | 
|  | /* 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.tv_sec % | 
|  | ARES_TIMEOUT_TABLE_SIZE])); | 
|  |  | 
|  | /* Keep track of queries bucketed by server, so we can process server | 
|  | * errors quickly. | 
|  | */ | 
|  | ares__remove_from_list(&(query->queries_to_server)); | 
|  | ares__insert_in_list(&(query->queries_to_server), | 
|  | &(server->queries_to_server)); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * setsocknonblock sets the given socket to either blocking or non-blocking | 
|  | * mode based on the 'nonblock' boolean argument. This function is highly | 
|  | * portable. | 
|  | */ | 
|  | static int setsocknonblock(ares_socket_t sockfd,    /* operate on this */ | 
|  | int nonblock   /* TRUE or FALSE */) | 
|  | { | 
|  | #if defined(USE_BLOCKING_SOCKETS) | 
|  |  | 
|  | return 0; /* returns success */ | 
|  |  | 
|  | #elif defined(HAVE_FCNTL_O_NONBLOCK) | 
|  |  | 
|  | /* most recent unix versions */ | 
|  | int flags; | 
|  | flags = fcntl(sockfd, F_GETFL, 0); | 
|  | if (FALSE != nonblock) | 
|  | return fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); | 
|  | else | 
|  | return fcntl(sockfd, F_SETFL, flags & (~O_NONBLOCK));  /* LCOV_EXCL_LINE */ | 
|  |  | 
|  | #elif defined(HAVE_IOCTL_FIONBIO) | 
|  |  | 
|  | /* older unix versions */ | 
|  | int flags = nonblock ? 1 : 0; | 
|  | return ioctl(sockfd, FIONBIO, &flags); | 
|  |  | 
|  | #elif defined(HAVE_IOCTLSOCKET_FIONBIO) | 
|  |  | 
|  | #ifdef WATT32 | 
|  | char flags = nonblock ? 1 : 0; | 
|  | #else | 
|  | /* Windows */ | 
|  | unsigned long flags = nonblock ? 1UL : 0UL; | 
|  | #endif | 
|  | return ioctlsocket(sockfd, FIONBIO, &flags); | 
|  |  | 
|  | #elif defined(HAVE_IOCTLSOCKET_CAMEL_FIONBIO) | 
|  |  | 
|  | /* Amiga */ | 
|  | long flags = nonblock ? 1L : 0L; | 
|  | return IoctlSocket(sockfd, FIONBIO, flags); | 
|  |  | 
|  | #elif defined(HAVE_SETSOCKOPT_SO_NONBLOCK) | 
|  |  | 
|  | /* BeOS */ | 
|  | long b = nonblock ? 1L : 0L; | 
|  | return setsockopt(sockfd, SOL_SOCKET, SO_NONBLOCK, &b, sizeof(b)); | 
|  |  | 
|  | #else | 
|  | #  error "no non-blocking method was found/used/set" | 
|  | #endif | 
|  | } | 
|  | #define SO_BINDTODEVICE 1 | 
|  | static int configure_socket(ares_socket_t s, int family, ares_channel channel) | 
|  | { | 
|  | union { | 
|  | struct sockaddr     sa; | 
|  | struct sockaddr_in  sa4; | 
|  | struct sockaddr_in6 sa6; | 
|  | } local; | 
|  |  | 
|  | (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;  /* LCOV_EXCL_LINE */ | 
|  | #endif | 
|  |  | 
|  | /* Set the socket's send and receive buffer sizes. */ | 
|  | if ((channel->socket_send_buffer_size > 0) && | 
|  | setsockopt(s, SOL_SOCKET, SO_SNDBUF, | 
|  | (void *)&channel->socket_send_buffer_size, | 
|  | sizeof(channel->socket_send_buffer_size)) == -1) | 
|  | return -1; | 
|  |  | 
|  | if ((channel->socket_receive_buffer_size > 0) && | 
|  | setsockopt(s, SOL_SOCKET, SO_RCVBUF, | 
|  | (void *)&channel->socket_receive_buffer_size, | 
|  | sizeof(channel->socket_receive_buffer_size)) == -1) | 
|  | return -1; | 
|  | fprintf(stderr, "configure_socket before\n"); | 
|  | #ifdef SO_BINDTODEVICE | 
|  | if (channel->local_dev_name[0]) { | 
|  | if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, | 
|  | channel->local_dev_name, sizeof(channel->local_dev_name)) == -1) { | 
|  | /* Only root can do this, and usually not fatal if it doesn't work, so */ | 
|  | /* just continue on. */ | 
|  | fprintf(stderr, "configure_socket failed\n"); | 
|  | } | 
|  | } | 
|  | #endif | 
|  | fprintf(stderr, "configure_socket good\n"); | 
|  | if (family == AF_INET) { | 
|  | if (channel->local_ip4) { | 
|  | memset(&local.sa4, 0, sizeof(local.sa4)); | 
|  | local.sa4.sin_family = AF_INET; | 
|  | local.sa4.sin_addr.s_addr = htonl(channel->local_ip4); | 
|  | if (bind(s, &local.sa, sizeof(local.sa4)) < 0) | 
|  | return -1; | 
|  | } | 
|  | } | 
|  | else if (family == AF_INET6) { | 
|  | 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)); | 
|  | if (bind(s, &local.sa, sizeof(local.sa6)) < 0) | 
|  | return -1; | 
|  | } | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int open_tcp_socket(ares_channel channel, struct server_state *server) | 
|  | { | 
|  | ares_socket_t s; | 
|  | int opt; | 
|  | ares_socklen_t salen; | 
|  | union { | 
|  | struct sockaddr_in  sa4; | 
|  | struct sockaddr_in6 sa6; | 
|  | } saddr; | 
|  | struct sockaddr *sa; | 
|  |  | 
|  | switch (server->addr.family) | 
|  | { | 
|  | case AF_INET: | 
|  | sa = (void *)&saddr.sa4; | 
|  | salen = sizeof(saddr.sa4); | 
|  | memset(sa, 0, salen); | 
|  | saddr.sa4.sin_family = AF_INET; | 
|  | 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; | 
|  | case AF_INET6: | 
|  | sa = (void *)&saddr.sa6; | 
|  | salen = sizeof(saddr.sa6); | 
|  | memset(sa, 0, salen); | 
|  | saddr.sa6.sin6_family = AF_INET6; | 
|  | 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;  /* LCOV_EXCL_LINE */ | 
|  | } | 
|  |  | 
|  | /* Acquire a socket. */ | 
|  | s = socket(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); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | #ifdef TCP_NODELAY | 
|  | /* | 
|  | * Disable the Nagle algorithm (only relevant for TCP sockets, and thus not | 
|  | * in configure_socket). In general, in DNS lookups we're pretty much | 
|  | * interested in firing off a single request and then waiting for a reply, | 
|  | * so batching isn't very interesting. | 
|  | */ | 
|  | opt = 1; | 
|  | if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, | 
|  | (void *)&opt, sizeof(opt)) == -1) | 
|  | { | 
|  | sclose(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) | 
|  | { | 
|  | sclose(s); | 
|  | return err; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Connect to the server. */ | 
|  | if (connect(s, sa, salen) == -1) | 
|  | { | 
|  | int err = SOCKERRNO; | 
|  |  | 
|  | if (err != EINPROGRESS && err != EWOULDBLOCK) | 
|  | { | 
|  | sclose(s); | 
|  | return -1; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (channel->sock_create_cb) | 
|  | { | 
|  | int err = channel->sock_create_cb(s, SOCK_STREAM, | 
|  | channel->sock_create_cb_data); | 
|  | if (err < 0) | 
|  | { | 
|  | sclose(s); | 
|  | return err; | 
|  | } | 
|  | } | 
|  |  | 
|  | SOCK_STATE_CALLBACK(channel, s, 1, 0); | 
|  | server->tcp_buffer_pos = 0; | 
|  | server->tcp_socket = s; | 
|  | server->tcp_connection_generation = ++channel->tcp_connection_generation; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int open_udp_socket(ares_channel channel, struct server_state *server) | 
|  | { | 
|  | ares_socket_t s; | 
|  | ares_socklen_t salen; | 
|  | union { | 
|  | struct sockaddr_in  sa4; | 
|  | struct sockaddr_in6 sa6; | 
|  | } saddr; | 
|  | struct sockaddr *sa; | 
|  |  | 
|  | switch (server->addr.family) | 
|  | { | 
|  | case AF_INET: | 
|  | sa = (void *)&saddr.sa4; | 
|  | salen = sizeof(saddr.sa4); | 
|  | memset(sa, 0, salen); | 
|  | saddr.sa4.sin_family = AF_INET; | 
|  | 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; | 
|  | case AF_INET6: | 
|  | sa = (void *)&saddr.sa6; | 
|  | salen = sizeof(saddr.sa6); | 
|  | memset(sa, 0, salen); | 
|  | saddr.sa6.sin6_family = AF_INET6; | 
|  | 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;  /* LCOV_EXCL_LINE */ | 
|  | } | 
|  |  | 
|  | /* Acquire a socket. */ | 
|  | s = socket(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); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | if (channel->sock_config_cb) | 
|  | { | 
|  | int err = channel->sock_config_cb(s, SOCK_DGRAM, | 
|  | channel->sock_config_cb_data); | 
|  | if (err < 0) | 
|  | { | 
|  | sclose(s); | 
|  | return err; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Connect to the server. */ | 
|  | if (connect(s, sa, salen) == -1) | 
|  | { | 
|  | int err = SOCKERRNO; | 
|  |  | 
|  | if (err != EINPROGRESS && err != EWOULDBLOCK) | 
|  | { | 
|  | sclose(s); | 
|  | return -1; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (channel->sock_create_cb) | 
|  | { | 
|  | int err = channel->sock_create_cb(s, SOCK_DGRAM, | 
|  | channel->sock_create_cb_data); | 
|  | if (err < 0) | 
|  | { | 
|  | sclose(s); | 
|  | return err; | 
|  | } | 
|  | } | 
|  |  | 
|  | SOCK_STATE_CALLBACK(channel, s, 1, 0); | 
|  |  | 
|  | server->udp_socket = s; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int same_questions(const unsigned char *qbuf, int qlen, | 
|  | const unsigned char *abuf, int alen) | 
|  | { | 
|  | struct { | 
|  | const unsigned char *p; | 
|  | int qdcount; | 
|  | char *name; | 
|  | long namelen; | 
|  | int type; | 
|  | int dnsclass; | 
|  | } q, a; | 
|  | int i, j; | 
|  |  | 
|  | if (qlen < HFIXEDSZ || alen < HFIXEDSZ) | 
|  | return 0; | 
|  |  | 
|  | /* Extract qdcount from the request and reply buffers and compare them. */ | 
|  | q.qdcount = DNS_HEADER_QDCOUNT(qbuf); | 
|  | a.qdcount = DNS_HEADER_QDCOUNT(abuf); | 
|  | if (q.qdcount != a.qdcount) | 
|  | return 0; | 
|  |  | 
|  | /* For each question in qbuf, find it in abuf. */ | 
|  | q.p = qbuf + HFIXEDSZ; | 
|  | for (i = 0; i < q.qdcount; i++) | 
|  | { | 
|  | /* Decode the question in the query. */ | 
|  | if (ares_expand_name(q.p, qbuf, qlen, &q.name, &q.namelen) | 
|  | != ARES_SUCCESS) | 
|  | return 0; | 
|  | q.p += q.namelen; | 
|  | if (q.p + QFIXEDSZ > qbuf + qlen) | 
|  | { | 
|  | ares_free(q.name); | 
|  | return 0; | 
|  | } | 
|  | q.type = DNS_QUESTION_TYPE(q.p); | 
|  | q.dnsclass = DNS_QUESTION_CLASS(q.p); | 
|  | q.p += QFIXEDSZ; | 
|  |  | 
|  | /* Search for this question in the answer. */ | 
|  | a.p = abuf + HFIXEDSZ; | 
|  | for (j = 0; j < a.qdcount; j++) | 
|  | { | 
|  | /* Decode the question in the answer. */ | 
|  | if (ares_expand_name(a.p, abuf, alen, &a.name, &a.namelen) | 
|  | != ARES_SUCCESS) | 
|  | { | 
|  | ares_free(q.name); | 
|  | return 0; | 
|  | } | 
|  | a.p += a.namelen; | 
|  | if (a.p + QFIXEDSZ > abuf + alen) | 
|  | { | 
|  | ares_free(q.name); | 
|  | ares_free(a.name); | 
|  | return 0; | 
|  | } | 
|  | a.type = DNS_QUESTION_TYPE(a.p); | 
|  | a.dnsclass = DNS_QUESTION_CLASS(a.p); | 
|  | a.p += QFIXEDSZ; | 
|  |  | 
|  | /* Compare the decoded questions. */ | 
|  | if (strcasecmp(q.name, a.name) == 0 && q.type == a.type | 
|  | && q.dnsclass == a.dnsclass) | 
|  | { | 
|  | ares_free(a.name); | 
|  | break; | 
|  | } | 
|  | ares_free(a.name); | 
|  | } | 
|  |  | 
|  | ares_free(q.name); | 
|  | if (j == a.qdcount) | 
|  | return 0; | 
|  | } | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | static int same_address(struct sockaddr *sa, struct ares_addr *aa) | 
|  | { | 
|  | void *addr1; | 
|  | void *addr2; | 
|  |  | 
|  | if (sa->sa_family == aa->family) | 
|  | { | 
|  | switch (aa->family) | 
|  | { | 
|  | case AF_INET: | 
|  | addr1 = &aa->addrV4; | 
|  | addr2 = &((struct sockaddr_in *)sa)->sin_addr; | 
|  | if (memcmp(addr1, addr2, sizeof(aa->addrV4)) == 0) | 
|  | return 1; /* match */ | 
|  | break; | 
|  | case AF_INET6: | 
|  | addr1 = &aa->addrV6; | 
|  | addr2 = &((struct sockaddr_in6 *)sa)->sin6_addr; | 
|  | if (memcmp(addr1, addr2, sizeof(aa->addrV6)) == 0) | 
|  | return 1; /* match */ | 
|  | break; | 
|  | default: | 
|  | break;  /* LCOV_EXCL_LINE */ | 
|  | } | 
|  | } | 
|  | return 0; /* different */ | 
|  | } | 
|  |  | 
|  | static void end_query (ares_channel channel, struct query *query, int status, | 
|  | unsigned char *abuf, int alen) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | /* First we check to see if this query ended while one of our send | 
|  | * queues still has pointers to it. | 
|  | */ | 
|  | for (i = 0; i < channel->nservers; i++) | 
|  | { | 
|  | struct server_state *server = &channel->servers[i]; | 
|  | struct send_request *sendreq; | 
|  | for (sendreq = server->qhead; sendreq; sendreq = sendreq->next) | 
|  | if (sendreq->owner_query == query) | 
|  | { | 
|  | sendreq->owner_query = NULL; | 
|  | 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. | 
|  | */ | 
|  | sendreq->data_storage = ares_malloc(sendreq->len); | 
|  | if (sendreq->data_storage != NULL) | 
|  | { | 
|  | memcpy(sendreq->data_storage, sendreq->data, sendreq->len); | 
|  | sendreq->data = sendreq->data_storage; | 
|  | } | 
|  | } | 
|  | 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. | 
|  | */ | 
|  | server->is_broken = 1; | 
|  | /* Just to be paranoid, zero out this sendreq... */ | 
|  | sendreq->data = NULL; | 
|  | sendreq->len = 0; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Invoke the callback */ | 
|  | 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. | 
|  | */ | 
|  | if (!(channel->flags & ARES_FLAG_STAYOPEN) && | 
|  | ares__is_list_empty(&(channel->all_queries))) | 
|  | { | 
|  | for (i = 0; i < channel->nservers; i++) | 
|  | ares__close_sockets(channel, &channel->servers[i]); | 
|  | } | 
|  | } | 
|  |  | 
|  | void ares__free_query(struct query *query) | 
|  | { | 
|  | /* Remove the query from all the lists in which it is linked */ | 
|  | ares__remove_from_list(&(query->queries_by_qid)); | 
|  | ares__remove_from_list(&(query->queries_by_timeout)); | 
|  | ares__remove_from_list(&(query->queries_to_server)); | 
|  | ares__remove_from_list(&(query->all_queries)); | 
|  | /* Zero out some important stuff, to help catch bugs */ | 
|  | query->callback = NULL; | 
|  | query->arg = NULL; | 
|  | /* Deallocate the memory associated with the query */ | 
|  | ares_free(query->tcpbuf); | 
|  | ares_free(query->server_info); | 
|  | ares_free(query); | 
|  | } |