Refactor connection handling (#839)

Refactor some connection handling to reduce code duplication and to
unify the TCP and UDP codepaths a bit more. This will make some future
changes easier to make.

This also does some structure renaming to better conform with current
standards:
- `struct server_state` -> `ares_server_t`
- `struct server_connection` -> `ares_conn_t`
- `struct query` -> `ares_query_t`

Authored-by: Brad House (@bradh352)
pull/840/head
Brad House 4 months ago committed by GitHub
parent 2f9805fb61
commit ac33bdc7c2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 2
      .cirrus.yml
  2. 2
      .github/workflows/msys2.yml
  3. 33
      src/lib/ares__close_sockets.c
  4. 238
      src/lib/ares__socket.c
  5. 9
      src/lib/ares__sortaddrinfo.c
  6. 2
      src/lib/ares_cancel.c
  7. 31
      src/lib/ares_cookie.c
  8. 6
      src/lib/ares_destroy.c
  9. 2
      src/lib/ares_getaddrinfo.c
  10. 8
      src/lib/ares_init.c
  11. 6
      src/lib/ares_metrics.c
  12. 2
      src/lib/ares_options.c
  13. 146
      src/lib/ares_private.h
  14. 452
      src/lib/ares_process.c
  15. 2
      src/lib/ares_qcache.c
  16. 4
      src/lib/ares_send.c
  17. 2
      src/lib/ares_timeout.c
  18. 28
      src/lib/ares_update_servers.c
  19. 10
      src/lib/legacy/ares_fds.c
  20. 13
      src/lib/legacy/ares_getsock.c
  21. 15
      test/ares-test.h
  22. 22
      test/dns-proto.h

@ -57,7 +57,7 @@ task:
BUILD_ANALYZE: "yes"
TEST_SYMBOL_VISIBILITY: "yes"
freebsd_instance:
image_family: freebsd-13-3
image_family: freebsd-14-1
- name: "iOS"
env:
DIST: "iOS"

@ -10,7 +10,7 @@ concurrency:
cancel-in-progress: true
env:
TEST_FILTER: "-4 --gtest_filter=-*LiveSearchTXT*:*LiveSearchANY*:*LiveGetLocalhostByAddr*"
TEST_FILTER: "-4 --gtest_filter=-*LiveSearchTXT*:*LiveSearchANY*:*LiveSearchNS*:*LiveGetLocalhostByAddr*"
CMAKE_FLAGS: "-DCMAKE_BUILD_TYPE=DEBUG -DCARES_STATIC=ON -DCMAKE_INSTALL_PREFIX=C:/projects/build-cares/test_install -DCARES_STATIC_PIC=ON -G Ninja"
CONFIG_OPTS: "--disable-shared"
MAKE: make

@ -28,10 +28,10 @@
#include "ares_private.h"
#include <assert.h>
static void ares__requeue_queries(struct server_connection *conn,
ares_status_t requeue_status)
static void ares__requeue_queries(ares_conn_t *conn,
ares_status_t requeue_status)
{
struct query *query;
ares_query_t *query;
ares_timeval_t now;
ares__tvnow(&now);
@ -41,18 +41,17 @@ static void ares__requeue_queries(struct server_connection *conn,
}
}
void ares__close_connection(struct server_connection *conn,
ares_status_t requeue_status)
void ares__close_connection(ares_conn_t *conn, ares_status_t requeue_status)
{
struct server_state *server = conn->server;
ares_channel_t *channel = server->channel;
ares_server_t *server = conn->server;
ares_channel_t *channel = server->channel;
/* Unlink */
ares__llist_node_claim(
ares__htable_asvp_get_direct(channel->connnode_by_socket, conn->fd));
ares__htable_asvp_remove(channel->connnode_by_socket, conn->fd);
if (conn->is_tcp) {
if (conn->flags & ARES_CONN_FLAG_TCP) {
/* Reset any existing input and output buffer. */
ares__buf_consume(server->tcp_parser, ares__buf_len(server->tcp_parser));
ares__buf_consume(server->tcp_send, ares__buf_len(server->tcp_send));
@ -70,12 +69,12 @@ void ares__close_connection(struct server_connection *conn,
ares_free(conn);
}
void ares__close_sockets(struct server_state *server)
void ares__close_sockets(ares_server_t *server)
{
ares__llist_node_t *node;
while ((node = ares__llist_node_first(server->connections)) != NULL) {
struct server_connection *conn = ares__llist_node_val(node);
ares_conn_t *conn = ares__llist_node_val(node);
ares__close_connection(conn, ARES_SUCCESS);
}
}
@ -91,16 +90,16 @@ void ares__check_cleanup_conns(const ares_channel_t *channel)
/* Iterate across each server */
for (snode = ares__slist_node_first(channel->servers); snode != NULL;
snode = ares__slist_node_next(snode)) {
struct server_state *server = ares__slist_node_val(snode);
ares__llist_node_t *cnode;
ares_server_t *server = ares__slist_node_val(snode);
ares__llist_node_t *cnode;
/* Iterate across each connection */
cnode = ares__llist_node_first(server->connections);
while (cnode != NULL) {
ares__llist_node_t *next = ares__llist_node_next(cnode);
struct server_connection *conn = ares__llist_node_val(cnode);
ares_bool_t do_cleanup = ARES_FALSE;
cnode = next;
ares__llist_node_t *next = ares__llist_node_next(cnode);
ares_conn_t *conn = ares__llist_node_val(cnode);
ares_bool_t do_cleanup = ARES_FALSE;
cnode = next;
/* Has connections, not eligible */
if (ares__llist_len(conn->queries_to_conn)) {
@ -121,7 +120,7 @@ void ares__check_cleanup_conns(const ares_channel_t *channel)
}
/* If the udp connection hit its max queries, always close it */
if (!conn->is_tcp && channel->udp_max_queries > 0 &&
if (!(conn->flags & ARES_CONN_FLAG_TCP) && channel->udp_max_queries > 0 &&
conn->total_queries >= channel->udp_max_queries) {
do_cleanup = ARES_TRUE;
}

@ -158,7 +158,7 @@ static void set_ipv6_v6only(ares_socket_t sockfd, int on)
# define set_ipv6_v6only(s, v)
#endif
static int configure_socket(ares_socket_t s, struct server_state *server)
static ares_status_t configure_socket(ares_conn_t *conn)
{
union {
struct sockaddr sa;
@ -167,19 +167,20 @@ static int configure_socket(ares_socket_t s, struct server_state *server)
} local;
ares_socklen_t bindlen = 0;
ares_server_t *server = conn->server;
ares_channel_t *channel = server->channel;
/* do not set options for user-managed sockets */
if (channel->sock_funcs && channel->sock_funcs->asocket) {
return 0;
return ARES_SUCCESS;
}
(void)setsocknonblock(s, 1);
(void)setsocknonblock(conn->fd, 1);
#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 */
if (fcntl(conn->fd, F_SETFD, FD_CLOEXEC) != 0) {
return ARES_ECONNREFUSED; /* LCOV_EXCL_LINE */
}
#endif
@ -187,31 +188,47 @@ static int configure_socket(ares_socket_t s, struct server_state *server)
#if defined(SO_NOSIGPIPE)
{
int opt = 1;
setsockopt(s, SOL_SOCKET, SO_NOSIGPIPE, (void *)&opt, sizeof(opt));
setsockopt(conn->fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&opt, sizeof(opt));
}
#endif
#ifdef TCP_NODELAY
if (conn->flags & ARES_CONN_FLAG_TCP) {
/*
* 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.
*/
int opt = 1;
if (setsockopt(conn->fd, IPPROTO_TCP, TCP_NODELAY, (void *)&opt,
sizeof(opt)) != 0) {
return ARES_ECONNREFUSED;
}
}
#endif
/* Set the socket's send and receive buffer sizes. */
if ((channel->socket_send_buffer_size > 0) &&
setsockopt(s, SOL_SOCKET, SO_SNDBUF,
if (channel->socket_send_buffer_size > 0 &&
setsockopt(conn->fd, SOL_SOCKET, SO_SNDBUF,
(void *)&channel->socket_send_buffer_size,
sizeof(channel->socket_send_buffer_size)) == -1) {
return -1; /* LCOV_EXCL_LINE: UntestablePath */
sizeof(channel->socket_send_buffer_size)) != 0) {
return ARES_ECONNREFUSED; /* LCOV_EXCL_LINE: UntestablePath */
}
if ((channel->socket_receive_buffer_size > 0) &&
setsockopt(s, SOL_SOCKET, SO_RCVBUF,
if (channel->socket_receive_buffer_size > 0 &&
setsockopt(conn->fd, SOL_SOCKET, SO_RCVBUF,
(void *)&channel->socket_receive_buffer_size,
sizeof(channel->socket_receive_buffer_size)) == -1) {
return -1; /* LCOV_EXCL_LINE: UntestablePath */
sizeof(channel->socket_receive_buffer_size)) != 0) {
return ARES_ECONNREFUSED; /* LCOV_EXCL_LINE: UntestablePath */
}
#ifdef SO_BINDTODEVICE
if (channel->local_dev_name[0] &&
setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, channel->local_dev_name,
sizeof(channel->local_dev_name))) {
/* Only root can do this, and usually not fatal if it doesn't work, so */
/* just continue on. */
if (ares_strlen(channel->local_dev_name)) {
/* Only root can do this, and usually not fatal if it doesn't work, so
* just continue on. */
setsockopt(conn->fd, SOL_SOCKET, SO_BINDTODEVICE, channel->local_dev_name,
sizeof(channel->local_dev_name));
}
#endif
@ -231,15 +248,15 @@ static int configure_socket(ares_socket_t s, struct server_state *server)
bindlen = sizeof(local.sa6);
}
if (bindlen && bind(s, &local.sa, bindlen) < 0) {
return -1;
if (bindlen && bind(conn->fd, &local.sa, bindlen) < 0) {
return ARES_ECONNREFUSED;
}
if (server->addr.family == AF_INET6) {
set_ipv6_v6only(s, 0);
set_ipv6_v6only(conn->fd, 0);
}
return 0;
return ARES_SUCCESS;
}
ares_bool_t ares_sockaddr_to_ares_addr(struct ares_addr *ares_addr,
@ -280,7 +297,7 @@ ares_bool_t ares_sockaddr_to_ares_addr(struct ares_addr *ares_addr,
return ARES_FALSE;
}
static ares_status_t ares_conn_set_self_ip(struct server_connection *conn)
static ares_status_t ares_conn_set_self_ip(ares_conn_t *conn)
{
/* Some old systems might not have sockaddr_storage, so we make a union
* that's guaranteed to be large enough */
@ -289,6 +306,7 @@ static ares_status_t ares_conn_set_self_ip(struct server_connection *conn)
struct sockaddr_in sa4;
struct sockaddr_in6 sa6;
} from;
int rv;
ares_socklen_t len = sizeof(from);
@ -306,23 +324,40 @@ static ares_status_t ares_conn_set_self_ip(struct server_connection *conn)
return ARES_SUCCESS;
}
ares_status_t ares__open_connection(ares_channel_t *channel,
struct server_state *server,
ares_bool_t is_tcp)
ares_status_t ares__open_connection(ares_conn_t **conn_out,
ares_channel_t *channel,
ares_server_t *server, ares_query_t *query)
{
ares_socket_t s;
int opt;
ares_socklen_t salen;
ares_status_t status;
ares_bool_t is_tcp = query->using_tcp;
union {
struct sockaddr_in sa4;
struct sockaddr_in6 sa6;
} saddr;
struct sockaddr *sa;
struct server_connection *conn;
ares__llist_node_t *node;
int type = is_tcp ? SOCK_STREAM : SOCK_DGRAM;
struct sockaddr *sa;
ares_conn_t *conn;
ares__llist_node_t *node = NULL;
int sock_type = is_tcp ? SOCK_STREAM : SOCK_DGRAM;
*conn_out = NULL;
conn = ares_malloc(sizeof(*conn));
if (conn == NULL) {
return ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
}
memset(conn, 0, sizeof(*conn));
conn->fd = ARES_SOCKET_BAD;
conn->server = server;
conn->queries_to_conn = ares__llist_create(NULL);
conn->flags = is_tcp?ARES_CONN_FLAG_TCP:ARES_CONN_FLAG_NONE;
if (conn->queries_to_conn == NULL) {
/* LCOV_EXCL_START: OutOfMemory */
status = ARES_ENOMEM;
goto done;
/* LCOV_EXCL_STOP */
}
switch (server->addr.family) {
case AF_INET:
@ -347,91 +382,50 @@ ares_status_t ares__open_connection(ares_channel_t *channel,
#endif
break;
default:
return ARES_EBADFAMILY; /* LCOV_EXCL_LINE */
status = ARES_EBADFAMILY; /* LCOV_EXCL_LINE */
goto done;
}
/* Acquire a socket. */
s = ares__open_socket(channel, server->addr.family, type, 0);
if (s == ARES_SOCKET_BAD) {
return ARES_ECONNREFUSED;
conn->fd = ares__open_socket(channel, server->addr.family, sock_type, 0);
if (conn->fd == ARES_SOCKET_BAD) {
status = ARES_ECONNREFUSED;
goto done;
}
/* Configure it. */
if (configure_socket(s, server) < 0) {
ares__close_socket(channel, s);
return ARES_ECONNREFUSED;
}
#ifdef TCP_NODELAY
if (is_tcp) {
/*
* 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 ((!channel->sock_funcs || !channel->sock_funcs->asocket) &&
setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (void *)&opt, sizeof(opt)) ==
-1) {
ares__close_socket(channel, s); /* LCOV_EXCL_LINE: UntestablePath */
return ARES_ECONNREFUSED; /* LCOV_EXCL_LINE: UntestablePath */
}
status = configure_socket(conn);
if (status != ARES_SUCCESS) {
goto done;
}
#endif
if (channel->sock_config_cb) {
int err = channel->sock_config_cb(s, type, channel->sock_config_cb_data);
int err = channel->sock_config_cb(conn->fd, sock_type, channel->sock_config_cb_data);
if (err < 0) {
ares__close_socket(channel, s);
return ARES_ECONNREFUSED;
status = ARES_ECONNREFUSED;
goto done;
}
}
/* Connect to the server. */
if (ares__connect_socket(channel, s, sa, salen) == -1) {
int err = SOCKERRNO;
if (err != EINPROGRESS && err != EWOULDBLOCK) {
ares__close_socket(channel, s);
return ARES_ECONNREFUSED;
}
status = ares__connect_socket(channel, conn->fd, sa, salen);
if (status != ARES_SUCCESS) {
goto done;
}
if (channel->sock_create_cb) {
int err = channel->sock_create_cb(s, type, channel->sock_create_cb_data);
int err = channel->sock_create_cb(conn->fd, sock_type, channel->sock_create_cb_data);
if (err < 0) {
ares__close_socket(channel, s);
return ARES_ECONNREFUSED;
status = ARES_ECONNREFUSED;
goto done;
}
}
conn = ares_malloc(sizeof(*conn));
if (conn == NULL) {
ares__close_socket(channel, s); /* LCOV_EXCL_LINE: OutOfMemory */
return ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
}
memset(conn, 0, sizeof(*conn));
conn->fd = s;
conn->server = server;
conn->queries_to_conn = ares__llist_create(NULL);
conn->is_tcp = is_tcp;
if (conn->queries_to_conn == NULL) {
/* LCOV_EXCL_START: OutOfMemory */
ares__close_socket(channel, s);
ares_free(conn);
return ARES_ENOMEM;
/* LCOV_EXCL_STOP */
}
/* Need to store our own ip for DNS cookie support */
status = ares_conn_set_self_ip(conn);
if (status != ARES_SUCCESS) {
/* LCOV_EXCL_START: UntestablePath */
ares__close_socket(channel, s);
ares_free(conn);
return status;
/* LCOV_EXCL_STOP */
goto done; /* LCOV_EXCL_LINE: UntestablePath */
}
/* TCP connections are thrown to the end as we don't spawn multiple TCP
@ -444,32 +438,36 @@ ares_status_t ares__open_connection(ares_channel_t *channel,
}
if (node == NULL) {
/* LCOV_EXCL_START: OutOfMemory */
ares__close_socket(channel, s);
ares__llist_destroy(conn->queries_to_conn);
ares_free(conn);
return ARES_ENOMEM;
status = ARES_ENOMEM;
goto done;
/* LCOV_EXCL_STOP */
}
/* Register globally to quickly map event on file descriptor to connection
* node object */
if (!ares__htable_asvp_insert(channel->connnode_by_socket, s, node)) {
if (!ares__htable_asvp_insert(channel->connnode_by_socket, conn->fd, node)) {
/* LCOV_EXCL_START: OutOfMemory */
ares__close_socket(channel, s);
ares__llist_destroy(conn->queries_to_conn);
ares__llist_node_claim(node);
ares_free(conn);
return ARES_ENOMEM;
status = ARES_ENOMEM;
goto done;
/* LCOV_EXCL_STOP */
}
SOCK_STATE_CALLBACK(channel, s, 1, 0);
SOCK_STATE_CALLBACK(channel, conn->fd, 1, 0);
if (is_tcp) {
server->tcp_conn = conn;
}
return ARES_SUCCESS;
done:
if (status != ARES_SUCCESS) {
ares__llist_node_claim(node);
ares__llist_destroy(conn->queries_to_conn);
ares__close_socket(channel, conn->fd);
ares_free(conn);
} else {
*conn_out = conn;
}
return status;
}
ares_socket_t ares__open_socket(ares_channel_t *channel, int af, int type,
@ -483,15 +481,31 @@ ares_socket_t ares__open_socket(ares_channel_t *channel, int af, int type,
return socket(af, type, protocol);
}
int ares__connect_socket(ares_channel_t *channel, ares_socket_t sockfd,
const struct sockaddr *addr, ares_socklen_t addrlen)
ares_status_t ares__connect_socket(ares_channel_t *channel,
ares_socket_t sockfd,
const struct sockaddr *addr,
ares_socklen_t addrlen)
{
if (channel->sock_funcs && channel->sock_funcs->aconnect) {
return channel->sock_funcs->aconnect(sockfd, addr, addrlen,
int rv;
int err;
do {
if (channel->sock_funcs && channel->sock_funcs->aconnect) {
rv = channel->sock_funcs->aconnect(sockfd, addr, addrlen,
channel->sock_func_cb_data);
}
} else {
rv = connect(sockfd, addr, addrlen);
}
err = SOCKERRNO;
return connect(sockfd, addr, addrlen);
if (rv == -1 && err != EINPROGRESS && err != EWOULDBLOCK) {
return ARES_ECONNREFUSED;
}
} while (rv == -1 && err == EINTR);
return ARES_SUCCESS;
}
void ares__close_socket(ares_channel_t *channel, ares_socket_t s)

@ -346,7 +346,6 @@ static int find_src_addr(ares_channel_t *channel, const struct sockaddr *addr,
struct sockaddr *src_addr)
{
ares_socket_t sock;
int ret;
ares_socklen_t len;
switch (addr->sa_family) {
@ -363,18 +362,14 @@ static int find_src_addr(ares_channel_t *channel, const struct sockaddr *addr,
sock = ares__open_socket(channel, addr->sa_family, SOCK_DGRAM, IPPROTO_UDP);
if (sock == ARES_SOCKET_BAD) {
if (errno == EAFNOSUPPORT) {
if (SOCKERRNO == EAFNOSUPPORT) {
return 0;
} else {
return -1;
}
}
do {
ret = ares__connect_socket(channel, sock, addr, len);
} while (ret == -1 && errno == EINTR);
if (ret == -1) {
if (ares__connect_socket(channel, sock, addr, len) != ARES_SUCCESS) {
ares__close_socket(channel, sock);
return 0;
}

@ -59,7 +59,7 @@ void ares_cancel(ares_channel_t *channel)
node = ares__llist_node_first(list_copy);
while (node != NULL) {
struct query *query;
ares_query_t *query;
/* Cache next since this node is being deleted */
next = ares__llist_node_next(node);

@ -244,9 +244,8 @@ static void ares_cookie_clear(ares_cookie_t *cookie)
cookie->state = ARES_COOKIE_INITIAL;
}
static void ares_cookie_generate(ares_cookie_t *cookie,
struct server_connection *conn,
const ares_timeval_t *now)
static void ares_cookie_generate(ares_cookie_t *cookie, ares_conn_t *conn,
const ares_timeval_t *now)
{
ares_channel_t *channel = conn->server->channel;
@ -292,15 +291,14 @@ static ares_bool_t ares_addr_equal(const struct ares_addr *addr1,
return ARES_FALSE;
}
ares_status_t ares_cookie_apply(ares_dns_record_t *dnsrec,
struct server_connection *conn,
const ares_timeval_t *now)
ares_status_t ares_cookie_apply(ares_dns_record_t *dnsrec, ares_conn_t *conn,
const ares_timeval_t *now)
{
struct server_state *server = conn->server;
ares_cookie_t *cookie = &server->cookie;
ares_dns_rr_t *rr = ares_dns_get_opt_rr(dnsrec);
unsigned char c[40];
size_t c_len;
ares_server_t *server = conn->server;
ares_cookie_t *cookie = &server->cookie;
ares_dns_rr_t *rr = ares_dns_get_opt_rr(dnsrec);
unsigned char c[40];
size_t c_len;
/* If there is no OPT record, then EDNS isn't supported, and therefore
* cookies can't be supported */
@ -309,7 +307,7 @@ ares_status_t ares_cookie_apply(ares_dns_record_t *dnsrec,
}
/* No cookies on TCP, make sure we remove one if one is present */
if (conn->is_tcp) {
if (conn->flags & ARES_CONN_FLAG_TCP) {
ares_dns_rr_del_opt_byid(rr, ARES_RR_OPT_OPTIONS, ARES_OPT_PARAM_COOKIE);
return ARES_SUCCESS;
}
@ -369,12 +367,11 @@ ares_status_t ares_cookie_apply(ares_dns_record_t *dnsrec,
c_len);
}
ares_status_t ares_cookie_validate(struct query *query,
const ares_dns_record_t *dnsresp,
struct server_connection *conn,
const ares_timeval_t *now)
ares_status_t ares_cookie_validate(ares_query_t *query,
const ares_dns_record_t *dnsresp,
ares_conn_t *conn, const ares_timeval_t *now)
{
struct server_state *server = conn->server;
ares_server_t *server = conn->server;
ares_cookie_t *cookie = &server->cookie;
const ares_dns_record_t *dnsreq = query->query;
const unsigned char *resp_cookie;

@ -73,7 +73,7 @@ void ares_destroy(ares_channel_t *channel)
node = ares__llist_node_first(channel->all_queries);
while (node != NULL) {
ares__llist_node_t *next = ares__llist_node_next(node);
struct query *query = ares__llist_node_claim(node);
ares_query_t *query = ares__llist_node_claim(node);
query->node_all_queries = NULL;
query->callback(query->arg, ARES_EDESTRUCTION, 0, NULL);
@ -134,7 +134,7 @@ void ares_destroy(ares_channel_t *channel)
ares_free(channel);
}
void ares__destroy_server(struct server_state *server)
void ares__destroy_server(ares_server_t *server)
{
if (server == NULL) {
return; /* LCOV_EXCL_LINE: DefensiveCoding */
@ -152,7 +152,7 @@ void ares__destroy_servers_state(ares_channel_t *channel)
ares__slist_node_t *node;
while ((node = ares__slist_node_first(channel->servers)) != NULL) {
struct server_state *server = ares__slist_node_claim(node);
ares_server_t *server = ares__slist_node_claim(node);
ares__destroy_server(server);
}

@ -469,7 +469,7 @@ static void terminate_retries(const struct host_query *hquery,
unsigned short term_qid =
(qid == hquery->qid_a) ? hquery->qid_aaaa : hquery->qid_a;
const ares_channel_t *channel = hquery->channel;
struct query *query = NULL;
ares_query_t *query = NULL;
/* No other outstanding queries, nothing to do */
if (!hquery->remaining) {

@ -72,8 +72,8 @@ int ares_init(ares_channel_t **channelptr)
static int ares_query_timeout_cmp_cb(const void *arg1, const void *arg2)
{
const struct query *q1 = arg1;
const struct query *q2 = arg2;
const ares_query_t *q1 = arg1;
const ares_query_t *q2 = arg2;
if (q1->timeout.sec > q2->timeout.sec) {
return 1;
@ -94,8 +94,8 @@ static int ares_query_timeout_cmp_cb(const void *arg1, const void *arg2)
static int server_sort_cb(const void *data1, const void *data2)
{
const struct server_state *s1 = data1;
const struct server_state *s2 = data2;
const ares_server_t *s1 = data1;
const ares_server_t *s2 = data2;
if (s1->consec_failures < s2->consec_failures) {
return -1;

@ -145,7 +145,7 @@ static time_t ares_metric_timestamp(ares_server_bucket_t bucket,
return (time_t)(now->sec / divisor);
}
void ares_metrics_record(const struct query *query, struct server_state *server,
void ares_metrics_record(const ares_query_t *query, ares_server_t *server,
ares_status_t status, const ares_dns_record_t *dnsrec)
{
ares_timeval_t now;
@ -205,8 +205,8 @@ void ares_metrics_record(const struct query *query, struct server_state *server,
}
}
size_t ares_metrics_server_timeout(const struct server_state *server,
const ares_timeval_t *now)
size_t ares_metrics_server_timeout(const ares_server_t *server,
const ares_timeval_t *now)
{
const ares_channel_t *channel = server->channel;
ares_server_bucket_t i;

@ -66,7 +66,7 @@ static struct in_addr *ares_save_opt_servers(const ares_channel_t *channel,
for (snode = ares__slist_node_first(channel->servers); snode != NULL;
snode = ares__slist_node_next(snode)) {
const struct server_state *server = ares__slist_node_val(snode);
const ares_server_t *server = ares__slist_node_val(snode);
if (server->addr.family != AF_INET) {
continue;

@ -150,19 +150,29 @@ typedef struct ares_rand_state ares_rand_state;
#define DEFAULT_SERVER_RETRY_CHANCE 10
#define DEFAULT_SERVER_RETRY_DELAY 5000
struct query;
struct ares_query;
typedef struct ares_query ares_query_t;
struct server_state;
struct ares_server;
typedef struct ares_server ares_server_t;
struct server_connection {
struct server_state *server;
ares_socket_t fd;
struct ares_addr self_ip;
ares_bool_t is_tcp;
struct ares_conn;
typedef struct ares_conn ares_conn_t;
typedef enum {
ARES_CONN_FLAG_NONE = 0, /*!< No flags */
ARES_CONN_FLAG_TCP = 1 << 0 /*!< TCP not UDP */
} ares_conn_flags_t;
struct ares_conn {
ares_server_t *server;
ares_socket_t fd;
struct ares_addr self_ip;
ares_conn_flags_t flags;
/* total number of queries run on this connection since it was established */
size_t total_queries;
size_t total_queries;
/* list of outstanding queries to this connection */
ares__llist_t *queries_to_conn;
ares__llist_t *queries_to_conn;
};
#ifdef _MSC_VER
@ -235,65 +245,65 @@ typedef struct {
ares_timeval_t unsupported_ts;
} ares_cookie_t;
struct server_state {
struct ares_server {
/* Configuration */
size_t idx; /* index for server in system configuration */
struct ares_addr addr;
unsigned short udp_port; /* host byte order */
unsigned short tcp_port; /* host byte order */
char ll_iface[64]; /* IPv6 Link Local Interface */
unsigned int ll_scope; /* IPv6 Link Local Scope */
size_t consec_failures; /* Consecutive query failure count
* can be hard errors or timeouts
*/
ares__llist_t *connections;
struct server_connection *tcp_conn;
size_t idx; /* index for server in system configuration */
struct ares_addr addr;
unsigned short udp_port; /* host byte order */
unsigned short tcp_port; /* host byte order */
char ll_iface[64]; /* IPv6 Link Local Interface */
unsigned int ll_scope; /* IPv6 Link Local Scope */
size_t consec_failures; /* Consecutive query failure count
* can be hard errors or timeouts
*/
ares__llist_t *connections;
ares_conn_t *tcp_conn;
/* The next time when we will retry this server if it has hit failures */
ares_timeval_t next_retry_time;
ares_timeval_t next_retry_time;
/* TCP buffer since multiple responses can come back in one read, or partial
* in a read */
ares__buf_t *tcp_parser;
ares__buf_t *tcp_parser;
/* TCP output queue */
ares__buf_t *tcp_send;
ares__buf_t *tcp_send;
/*! Buckets for collecting metrics about the server */
ares_server_metrics_t metrics[ARES_METRIC_COUNT];
ares_server_metrics_t metrics[ARES_METRIC_COUNT];
/*! RFC 7873/9018 DNS Cookies */
ares_cookie_t cookie;
ares_cookie_t cookie;
/* Link back to owning channel */
ares_channel_t *channel;
ares_channel_t *channel;
};
/* State to represent a DNS query */
struct query {
struct ares_query {
/* Query ID from qbuf, for faster lookup, and current timeout */
unsigned short qid; /* host byte order */
ares_timeval_t ts; /*!< Timestamp query was sent */
ares_timeval_t timeout;
ares_channel_t *channel;
unsigned short qid; /* host byte order */
ares_timeval_t ts; /*!< Timestamp query was sent */
ares_timeval_t timeout;
ares_channel_t *channel;
/*
* Node object for each list entry the query belongs to in order to
* make removal operations O(1).
*/
ares__slist_node_t *node_queries_by_timeout;
ares__llist_node_t *node_queries_to_conn;
ares__llist_node_t *node_all_queries;
ares__slist_node_t *node_queries_by_timeout;
ares__llist_node_t *node_queries_to_conn;
ares__llist_node_t *node_all_queries;
/* connection handle query is associated with */
struct server_connection *conn;
ares_conn_t *conn;
/* Query */
ares_dns_record_t *query;
ares_dns_record_t *query;
ares_callback_dnsrec callback;
void *arg;
ares_callback_dnsrec callback;
void *arg;
/* Query status */
size_t try_count; /* Number of times we tried this query already. */
@ -443,8 +453,8 @@ ares_bool_t ares__timedout(const ares_timeval_t *now,
const ares_timeval_t *check);
/* Returns one of the normal ares status codes like ARES_SUCCESS */
ares_status_t ares__send_query(struct query *query, const ares_timeval_t *now);
ares_status_t ares__requeue_query(struct query *query,
ares_status_t ares__send_query(ares_query_t *query, const ares_timeval_t *now);
ares_status_t ares__requeue_query(ares_query_t *query,
const ares_timeval_t *now,
ares_status_t status,
ares_bool_t inc_try_count);
@ -475,11 +485,10 @@ void *ares__dnsrec_convert_arg(ares_callback callback, void *arg);
void ares__dnsrec_convert_cb(void *arg, ares_status_t status, size_t timeouts,
const ares_dns_record_t *dnsrec);
void ares__close_connection(struct server_connection *conn,
ares_status_t requeue_status);
void ares__close_sockets(struct server_state *server);
void ares__close_connection(ares_conn_t *conn, ares_status_t requeue_status);
void ares__close_sockets(ares_server_t *server);
void ares__check_cleanup_conns(const ares_channel_t *channel);
void ares__free_query(struct query *query);
void ares__free_query(ares_query_t *query);
ares_rand_state *ares__init_rand_state(void);
void ares__destroy_rand_state(ares_rand_state *state);
@ -586,9 +595,9 @@ ares_status_t ares__addrinfo2addrttl(const struct ares_addrinfo *ai, int family,
ares_status_t ares__addrinfo_localhost(const char *name, unsigned short port,
const struct ares_addrinfo_hints *hints,
struct ares_addrinfo *ai);
ares_status_t ares__open_connection(ares_channel_t *channel,
struct server_state *server,
ares_bool_t is_tcp);
ares_status_t ares__open_connection(ares_conn_t **conn_out,
ares_channel_t *channel,
ares_server_t *server, ares_query_t *query);
ares_bool_t ares_sockaddr_to_ares_addr(struct ares_addr *ares_addr,
unsigned short *port,
const struct sockaddr *sockaddr);
@ -602,10 +611,12 @@ ares_ssize_t ares__socket_recvfrom(ares_channel_t *channel, ares_socket_t s,
ares_socklen_t *from_len);
ares_ssize_t ares__socket_recv(ares_channel_t *channel, ares_socket_t s,
void *data, size_t data_len);
void ares__close_socket(ares_channel, ares_socket_t);
int ares__connect_socket(ares_channel_t *channel, ares_socket_t sockfd,
const struct sockaddr *addr, ares_socklen_t addrlen);
void ares__destroy_server(struct server_state *server);
void ares__close_socket(ares_channel_t *channel, ares_socket_t s);
ares_status_t ares__connect_socket(ares_channel_t *channel,
ares_socket_t sockfd,
const struct sockaddr *addr,
ares_socklen_t addrlen);
void ares__destroy_server(ares_server_t *server);
ares_status_t ares__servers_update(ares_channel_t *channel,
ares__llist_t *server_list,
@ -621,8 +632,8 @@ ares_status_t ares__sconfig_append_fromstr(ares__llist_t **sconfig,
ares_status_t ares_in_addr_to_server_config_llist(const struct in_addr *servers,
size_t nservers,
ares__llist_t **llist);
ares_status_t ares_get_server_addr(const struct server_state *server,
ares__buf_t *buf);
ares_status_t ares_get_server_addr(const ares_server_t *server,
ares__buf_t *buf);
struct ares_hosts_entry;
typedef struct ares_hosts_entry ares_hosts_entry_t;
@ -742,25 +753,24 @@ ares_status_t ares__qcache_create(ares_rand_state *rand_state,
void ares__qcache_flush(ares__qcache_t *cache);
ares_status_t ares_qcache_insert(ares_channel_t *channel,
const ares_timeval_t *now,
const struct query *query,
const ares_query_t *query,
ares_dns_record_t *dnsrec);
ares_status_t ares_qcache_fetch(ares_channel_t *channel,
const ares_timeval_t *now,
const ares_dns_record_t *dnsrec,
const ares_dns_record_t **dnsrec_resp);
void ares_metrics_record(const struct query *query, struct server_state *server,
ares_status_t status, const ares_dns_record_t *dnsrec);
size_t ares_metrics_server_timeout(const struct server_state *server,
const ares_timeval_t *now);
ares_status_t ares_cookie_apply(ares_dns_record_t *dnsrec,
struct server_connection *conn,
const ares_timeval_t *now);
ares_status_t ares_cookie_validate(struct query *query,
const ares_dns_record_t *dnsresp,
struct server_connection *conn,
const ares_timeval_t *now);
void ares_metrics_record(const ares_query_t *query, ares_server_t *server,
ares_status_t status, const ares_dns_record_t *dnsrec);
size_t ares_metrics_server_timeout(const ares_server_t *server,
const ares_timeval_t *now);
ares_status_t ares_cookie_apply(ares_dns_record_t *dnsrec, ares_conn_t *conn,
const ares_timeval_t *now);
ares_status_t ares_cookie_validate(ares_query_t *query,
const ares_dns_record_t *dnsresp,
ares_conn_t *conn,
const ares_timeval_t *now);
ares_status_t ares__channel_threading_init(ares_channel_t *channel);
void ares__channel_threading_destroy(ares_channel_t *channel);

@ -55,21 +55,20 @@ static void process_timeouts(ares_channel_t *channel,
const ares_timeval_t *now);
static ares_status_t process_answer(ares_channel_t *channel,
const unsigned char *abuf, size_t alen,
struct server_connection *conn,
ares_bool_t tcp, const ares_timeval_t *now);
static void handle_conn_error(struct server_connection *conn,
ares_bool_t critical_failure,
ares_status_t failure_status);
static ares_bool_t same_questions(const struct query *query,
const ares_dns_record_t *arec);
static ares_bool_t same_address(const struct sockaddr *sa,
const struct ares_addr *aa);
static void end_query(ares_channel_t *channel, struct server_state *server,
struct query *query, ares_status_t status,
const ares_dns_record_t *dnsrec);
static void ares__query_disassociate_from_conn(struct query *query)
ares_conn_t *conn, ares_bool_t tcp,
const ares_timeval_t *now);
static void handle_conn_error(ares_conn_t *conn, ares_bool_t critical_failure,
ares_status_t failure_status);
static ares_bool_t same_questions(const ares_query_t *query,
const ares_dns_record_t *arec);
static ares_bool_t same_address(const struct sockaddr *sa,
const struct ares_addr *aa);
static void end_query(ares_channel_t *channel, ares_server_t *server,
ares_query_t *query, ares_status_t status,
const ares_dns_record_t *dnsrec);
static void ares__query_disassociate_from_conn(ares_query_t *query)
{
/* If its not part of a connection, it can't be tracked for timeouts either */
ares__slist_node_destroy(query->node_queries_by_timeout);
@ -80,7 +79,7 @@ static void ares__query_disassociate_from_conn(struct query *query)
}
/* Invoke the server state callback after a success or failure */
static void invoke_server_state_cb(const struct server_state *server,
static void invoke_server_state_cb(const ares_server_t *server,
ares_bool_t success, int flags)
{
const ares_channel_t *channel = server->channel;
@ -114,8 +113,8 @@ static void invoke_server_state_cb(const struct server_state *server,
ares_free(server_string);
}
static void server_increment_failures(struct server_state *server,
ares_bool_t used_tcp)
static void server_increment_failures(ares_server_t *server,
ares_bool_t used_tcp)
{
ares__slist_node_t *node;
const ares_channel_t *channel = server->channel;
@ -138,7 +137,7 @@ static void server_increment_failures(struct server_state *server,
: ARES_SERV_STATE_UDP);
}
static void server_set_good(struct server_state *server, ares_bool_t used_tcp)
static void server_set_good(ares_server_t *server, ares_bool_t used_tcp)
{
ares__slist_node_t *node;
const ares_channel_t *channel = server->channel;
@ -280,7 +279,7 @@ static void write_tcp_data(ares_channel_t *channel, fd_set *write_fds,
for (node = ares__slist_node_first(channel->servers); node != NULL;
node = ares__slist_node_next(node)) {
struct server_state *server = ares__slist_node_val(node);
ares_server_t *server = ares__slist_node_val(node);
const unsigned char *data;
size_t data_len;
ares_ssize_t count;
@ -333,16 +332,15 @@ static void write_tcp_data(ares_channel_t *channel, fd_set *write_fds,
* 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_t *channel,
struct server_connection *conn,
const ares_timeval_t *now)
static void read_tcp_data(ares_channel_t *channel, ares_conn_t *conn,
const ares_timeval_t *now)
{
ares_ssize_t count;
struct server_state *server = conn->server;
ares_ssize_t count;
ares_server_t *server = conn->server;
/* Fetch buffer to store data we are reading */
size_t ptr_len = 65535;
unsigned char *ptr;
size_t ptr_len = 65535;
unsigned char *ptr;
ptr = ares__buf_append_start(server->tcp_parser, &ptr_len);
@ -445,12 +443,12 @@ static ares_socket_t *channel_socket_list(const ares_channel_t *channel,
for (snode = ares__slist_node_first(channel->servers); snode != NULL;
snode = ares__slist_node_next(snode)) {
struct server_state *server = ares__slist_node_val(snode);
ares__llist_node_t *node;
ares_server_t *server = ares__slist_node_val(snode);
ares__llist_node_t *node;
for (node = ares__llist_node_first(server->connections); node != NULL;
node = ares__llist_node_next(node)) {
const struct server_connection *conn = ares__llist_node_val(node);
const ares_conn_t *conn = ares__llist_node_val(node);
if (conn->fd == ARES_SOCKET_BAD) {
continue;
@ -471,9 +469,8 @@ fail:
}
/* If any UDP sockets select true for reading, process them. */
static void read_udp_packets_fd(ares_channel_t *channel,
struct server_connection *conn,
const ares_timeval_t *now)
static void read_udp_packets_fd(ares_channel_t *channel, ares_conn_t *conn,
const ares_timeval_t *now)
{
ares_ssize_t read_len;
unsigned char buf[MAXENDSSZ + 1];
@ -537,11 +534,11 @@ static void read_udp_packets_fd(ares_channel_t *channel,
static void read_packets(ares_channel_t *channel, fd_set *read_fds,
ares_socket_t read_fd, const ares_timeval_t *now)
{
size_t i;
ares_socket_t *socketlist = NULL;
size_t num_sockets = 0;
struct server_connection *conn = NULL;
ares__llist_node_t *node = NULL;
size_t i;
ares_socket_t *socketlist = NULL;
size_t num_sockets = 0;
ares_conn_t *conn = NULL;
ares__llist_node_t *node = NULL;
if (!read_fds && (read_fd == ARES_SOCKET_BAD)) {
/* no possible action */
@ -557,7 +554,7 @@ static void read_packets(ares_channel_t *channel, fd_set *read_fds,
conn = ares__llist_node_val(node);
if (conn->is_tcp) {
if (conn->flags & ARES_CONN_FLAG_TCP) {
read_tcp_data(channel, conn, now);
} else {
read_udp_packets_fd(channel, conn, now);
@ -591,7 +588,7 @@ static void read_packets(ares_channel_t *channel, fd_set *read_fds,
conn = ares__llist_node_val(node);
if (conn->is_tcp) {
if (conn->flags & ARES_CONN_FLAG_TCP) {
read_tcp_data(channel, conn, now);
} else {
read_udp_packets_fd(channel, conn, now);
@ -610,8 +607,8 @@ static void process_timeouts(ares_channel_t *channel, const ares_timeval_t *now)
* and go. We don't want to try to rely on 'next' as some operation might
* cause a cleanup of that pointer and would become invalid */
while ((node = ares__slist_node_first(channel->queries_by_timeout)) != NULL) {
struct query *query = ares__slist_node_val(node);
struct server_connection *conn;
ares_query_t *query = ares__slist_node_val(node);
ares_conn_t *conn;
/* Since this is sorted, as soon as we hit a query that isn't timed out,
* break */
@ -627,7 +624,7 @@ static void process_timeouts(ares_channel_t *channel, const ares_timeval_t *now)
}
}
static ares_status_t rewrite_without_edns(struct query *query)
static ares_status_t rewrite_without_edns(ares_query_t *query)
{
ares_status_t status = ARES_SUCCESS;
size_t i;
@ -659,16 +656,16 @@ done:
* the connection to be terminated after this call. */
static ares_status_t process_answer(ares_channel_t *channel,
const unsigned char *abuf, size_t alen,
struct server_connection *conn,
ares_bool_t tcp, const ares_timeval_t *now)
ares_conn_t *conn, ares_bool_t tcp,
const ares_timeval_t *now)
{
struct query *query;
ares_query_t *query;
/* Cache these as once ares__send_query() gets called, it may end up
* invalidating the connection all-together */
struct server_state *server = conn->server;
ares_dns_record_t *rdnsrec = NULL;
ares_status_t status;
ares_bool_t is_cached = ARES_FALSE;
ares_server_t *server = conn->server;
ares_dns_record_t *rdnsrec = NULL;
ares_status_t status;
ares_bool_t is_cached = ARES_FALSE;
/* Parse the response */
status = ares_dns_parse(abuf, alen, 0, &rdnsrec);
@ -792,23 +789,23 @@ cleanup:
return status;
}
static void handle_conn_error(struct server_connection *conn,
ares_bool_t critical_failure,
ares_status_t failure_status)
static void handle_conn_error(ares_conn_t *conn, ares_bool_t critical_failure,
ares_status_t failure_status)
{
struct server_state *server = conn->server;
ares_server_t *server = conn->server;
/* Increment failures first before requeue so it is unlikely to requeue
* to the same server */
if (critical_failure) {
server_increment_failures(server, conn->is_tcp);
server_increment_failures(server,
(conn->flags & ARES_CONN_FLAG_TCP)?ARES_TRUE:ARES_FALSE);
}
/* This will requeue any connections automatically */
ares__close_connection(conn, failure_status);
}
ares_status_t ares__requeue_query(struct query *query,
ares_status_t ares__requeue_query(ares_query_t *query,
const ares_timeval_t *now,
ares_status_t status,
ares_bool_t inc_try_count)
@ -842,7 +839,7 @@ ares_status_t ares__requeue_query(struct query *query,
/* Pick a random server from the list, we first get a random number in the
* range of the number of servers, then scan until we find that server in
* the list */
static struct server_state *ares__random_server(ares_channel_t *channel)
static ares_server_t *ares__random_server(ares_channel_t *channel)
{
unsigned char c;
size_t cnt;
@ -886,12 +883,11 @@ static struct server_state *ares__random_server(ares_channel_t *channel)
* To resolve this, with some probability we select a failed server to retry
* instead.
*/
static struct server_state *ares__failover_server(ares_channel_t *channel)
static ares_server_t *ares__failover_server(ares_channel_t *channel)
{
struct server_state *first_server = ares__slist_first_val(channel->servers);
const struct server_state *last_server =
ares__slist_last_val(channel->servers);
unsigned short r;
ares_server_t *first_server = ares__slist_first_val(channel->servers);
const ares_server_t *last_server = ares__slist_last_val(channel->servers);
unsigned short r;
/* Defensive code against no servers being available on the channel. */
if (first_server == NULL) {
@ -924,7 +920,7 @@ static struct server_state *ares__failover_server(ares_channel_t *channel)
ares__tvnow(&now);
for (node = ares__slist_node_first(channel->servers); node != NULL;
node = ares__slist_node_next(node)) {
struct server_state *node_val = ares__slist_node_val(node);
ares_server_t *node_val = ares__slist_node_val(node);
if (node_val != NULL && node_val->consec_failures > 0 &&
ares__timedout(&now, &node_val->next_retry_time)) {
return node_val;
@ -936,77 +932,10 @@ static struct server_state *ares__failover_server(ares_channel_t *channel)
return first_server;
}
static ares_status_t ares__append_tcpbuf(struct server_connection *conn,
const struct query *query,
const ares_timeval_t *now)
{
ares_status_t status;
unsigned char *qbuf = NULL;
size_t qbuf_len = 0;
status = ares_cookie_apply(query->query, conn, now);
if (status != ARES_SUCCESS) {
goto done;
}
status = ares_dns_write(query->query, &qbuf, &qbuf_len);
if (status != ARES_SUCCESS) {
goto done;
}
status =
ares__buf_append_be16(conn->server->tcp_send, (unsigned short)qbuf_len);
if (status != ARES_SUCCESS) {
goto done; /* LCOV_EXCL_LINE: OutOfMemory */
}
status = ares__buf_append(conn->server->tcp_send, qbuf, qbuf_len);
done:
ares_free(qbuf);
return status;
}
static ares_status_t ares__write_udpbuf(struct server_connection *conn,
const struct query *query,
const ares_timeval_t *now)
{
ares_status_t status;
unsigned char *qbuf = NULL;
size_t qbuf_len = 0;
status = ares_cookie_apply(query->query, conn, now);
if (status != ARES_SUCCESS) {
goto done;
}
status = ares_dns_write(query->query, &qbuf, &qbuf_len);
if (status != ARES_SUCCESS) {
goto done;
}
if (ares__socket_write(conn->server->channel, conn->fd, qbuf, qbuf_len) ==
-1) {
if (try_again(SOCKERRNO)) {
status = ARES_ESERVFAIL;
} else {
/* UDP is connection-less, but we might receive an ICMP unreachable which
* means we can't talk to the remote host at all and that will be
* reflected here */
status = ARES_ECONNREFUSED;
}
} else {
status = ARES_SUCCESS;
}
done:
ares_free(qbuf);
return status;
}
static size_t ares__calc_query_timeout(const struct query *query,
const struct server_state *server,
const ares_timeval_t *now)
static size_t ares__calc_query_timeout(const ares_query_t *query,
const ares_server_t *server,
const ares_timeval_t *now)
{
const ares_channel_t *channel = query->channel;
size_t timeout = ares_metrics_server_timeout(server, now);
@ -1056,150 +985,169 @@ static size_t ares__calc_query_timeout(const struct query *query,
return timeplus;
}
ares_status_t ares__send_query(struct query *query, const ares_timeval_t *now)
static ares_conn_t *ares__fetch_connection(ares_channel_t *channel,
ares_server_t *server,
const ares_query_t *query)
{
ares_channel_t *channel = query->channel;
struct server_state *server;
struct server_connection *conn;
size_t timeplus;
ares_status_t status;
ares_bool_t new_connection = ARES_FALSE;
ares__llist_node_t *node;
ares_conn_t *conn;
/* Choose the server to send the query to */
if (channel->rotate) {
/* Pull random server */
server = ares__random_server(channel);
} else {
/* Pull server with failover behavior */
server = ares__failover_server(channel);
if (query->using_tcp) {
return server->tcp_conn;
}
if (server == NULL) {
end_query(channel, server, query, ARES_ENOSERVER /* ? */, NULL);
return ARES_ENOSERVER;
/* Fetch existing UDP connection */
node = ares__llist_node_first(server->connections);
if (node == NULL) {
return NULL;
}
if (query->using_tcp) {
size_t prior_len = 0;
/* Make sure the TCP socket for this server is set up and queue
* a send request.
*/
if (server->tcp_conn == NULL) {
new_connection = ARES_TRUE;
status = ares__open_connection(channel, server, ARES_TRUE);
switch (status) {
/* Good result, continue on */
case ARES_SUCCESS:
break;
conn = ares__llist_node_val(node);
/* Not UDP, skip */
if (conn->flags & ARES_CONN_FLAG_TCP) {
return NULL;
}
/* These conditions are retryable as they are server-specific
* error codes */
case ARES_ECONNREFUSED:
case ARES_EBADFAMILY:
server_increment_failures(server, query->using_tcp);
return ares__requeue_query(query, now, status, ARES_TRUE);
/* Used too many times */
if (channel->udp_max_queries > 0 &&
conn->total_queries >= channel->udp_max_queries) {
return NULL;
}
/* Anything else is not retryable, likely ENOMEM */
default:
end_query(channel, server, query, status, NULL);
return status;
}
}
return conn;
}
conn = server->tcp_conn;
static ares_status_t ares__query_write(ares_conn_t *conn, ares_query_t *query,
const ares_timeval_t *now)
{
unsigned char *qbuf = NULL;
size_t qbuf_len = 0;
ares_server_t *server = conn->server;
ares_channel_t *channel = server->channel;
ares_status_t status;
prior_len = ares__buf_len(server->tcp_send);
status = ares_cookie_apply(query->query, conn, now);
if (status != ARES_SUCCESS) {
goto done;
}
status = ares_dns_write(query->query, &qbuf, &qbuf_len);
if (status != ARES_SUCCESS) {
goto done;
}
if (conn->flags & ARES_CONN_FLAG_TCP) {
size_t prior_len = ares__buf_len(server->tcp_send);
status = ares__append_tcpbuf(conn, query, now);
status =
ares__buf_append_be16(server->tcp_send, (unsigned short)qbuf_len);
if (status != ARES_SUCCESS) {
end_query(channel, server, query, status, NULL);
goto done; /* LCOV_EXCL_LINE: OutOfMemory */
}
/* Only safe to kill connection if it was new, otherwise it should be
* cleaned up by another process later */
if (new_connection) {
ares__close_connection(conn, status);
}
return status;
status = ares__buf_append(server->tcp_send, qbuf, qbuf_len);
if (status != ARES_SUCCESS) {
goto done;
}
if (prior_len == 0) {
SOCK_STATE_CALLBACK(channel, conn->fd, 1, 1);
}
} else {
ares__llist_node_t *node = ares__llist_node_first(server->connections);
/* Don't use the found connection if we've gone over the maximum number
* of queries. Also, skip over the TCP connection if it is the first in
* the list */
if (node != NULL) {
conn = ares__llist_node_val(node);
if (conn->is_tcp) {
node = NULL;
} else if (channel->udp_max_queries > 0 &&
conn->total_queries >= channel->udp_max_queries) {
node = NULL;
if (ares__socket_write(channel, conn->fd, qbuf, qbuf_len) ==
-1) {
if (try_again(SOCKERRNO)) {
status = ARES_ESERVFAIL;
goto done;
} else {
/* UDP is connection-less, but we might receive an ICMP unreachable which
* means we can't talk to the remote host at all and that will be
* reflected here */
status = ARES_ECONNREFUSED;
goto done;
}
}
}
if (node == NULL) {
new_connection = ARES_TRUE;
status = ares__open_connection(channel, server, ARES_FALSE);
switch (status) {
/* Good result, continue on */
case ARES_SUCCESS:
break;
status = ARES_SUCCESS;
/* These conditions are retryable as they are server-specific
* error codes */
case ARES_ECONNREFUSED:
case ARES_EBADFAMILY:
server_increment_failures(server, query->using_tcp);
return ares__requeue_query(query, now, status, ARES_TRUE);
done:
ares_free(qbuf);
return status;
}
/* Anything else is not retryable, likely ENOMEM */
default:
end_query(channel, server, query, status, NULL);
return status;
}
node = ares__llist_node_first(server->connections);
}
ares_status_t ares__send_query(ares_query_t *query, const ares_timeval_t *now)
{
ares_channel_t *channel = query->channel;
ares_server_t *server;
ares_conn_t *conn;
size_t timeplus;
ares_status_t status;
conn = ares__llist_node_val(node);
/* Choose the server to send the query to */
if (channel->rotate) {
/* Pull random server */
server = ares__random_server(channel);
} else {
/* Pull server with failover behavior */
server = ares__failover_server(channel);
}
status = ares__write_udpbuf(conn, query, now);
if (status != ARES_SUCCESS) {
if (status == ARES_ENOMEM) {
/* Not retryable */
if (server == NULL) {
end_query(channel, server, query, ARES_ENOSERVER /* ? */, NULL);
return ARES_ENOSERVER;
}
conn = ares__fetch_connection(channel, server, query);
if (conn == NULL) {
status = ares__open_connection(&conn, channel, server, query);
switch (status) {
/* Good result, continue on */
case ARES_SUCCESS:
break;
/* These conditions are retryable as they are server-specific
* error codes */
case ARES_ECONNREFUSED:
case ARES_EBADFAMILY:
server_increment_failures(server, query->using_tcp);
return ares__requeue_query(query, now, status, ARES_TRUE);
/* Anything else is not retryable, likely ENOMEM */
default:
end_query(channel, server, query, status, NULL);
return status;
}
}
}
if (status == ARES_ECONNREFUSED) {
handle_conn_error(conn, ARES_TRUE, status);
status = ares__query_write(conn, query, now);
switch (status) {
/* Good result, continue on */
case ARES_SUCCESS:
break;
/* This query wasn't yet bound to the connection, need to manually
* requeue it and return an appropriate error */
status = ares__requeue_query(query, now, status, ARES_TRUE);
if (status == ARES_ETIMEOUT) {
status = ARES_ECONNREFUSED;
}
return status;
}
case ARES_ENOMEM:
/* Not retryable */
end_query(channel, server, query, status, NULL);
return status;
/* FIXME: Handle EAGAIN here since it likely can happen. Right now we
* just requeue to a different server/connection. */
server_increment_failures(server, query->using_tcp);
/* These conditions are retryable as they are server-specific
* error codes */
case ARES_ECONNREFUSED:
case ARES_EBADFAMILY:
handle_conn_error(conn, ARES_TRUE, status);
status = ares__requeue_query(query, now, status, ARES_TRUE);
/* Only safe to kill connection if it was new, otherwise it should be
* cleaned up by another process later */
if (new_connection) {
ares__close_connection(conn, status);
if (status == ARES_ETIMEOUT) {
status = ARES_ECONNREFUSED;
}
return status;
/* FIXME: Handle EAGAIN here since it likely can happen. Right now we
* just requeue to a different server/connection. */
default:
server_increment_failures(server, query->using_tcp);
status = ares__requeue_query(query, now, status, ARES_TRUE);
return status;
}
}
timeplus = ares__calc_query_timeout(query, server, now);
@ -1215,11 +1163,6 @@ ares_status_t ares__send_query(struct query *query, const ares_timeval_t *now)
if (!query->node_queries_by_timeout) {
/* LCOV_EXCL_START: OutOfMemory */
end_query(channel, server, query, ARES_ENOMEM, NULL);
/* Only safe to kill connection if it was new, otherwise it should be
* cleaned up by another process later */
if (new_connection) {
ares__close_connection(conn, ARES_SUCCESS);
}
return ARES_ENOMEM;
/* LCOV_EXCL_STOP */
}
@ -1233,11 +1176,6 @@ ares_status_t ares__send_query(struct query *query, const ares_timeval_t *now)
if (query->node_queries_to_conn == NULL) {
/* LCOV_EXCL_START: OutOfMemory */
end_query(channel, server, query, ARES_ENOMEM, NULL);
/* Only safe to kill connection if it was new, otherwise it should be
* cleaned up by another process later */
if (new_connection) {
ares__close_connection(conn, ARES_SUCCESS);
}
return ARES_ENOMEM;
/* LCOV_EXCL_STOP */
}
@ -1247,7 +1185,7 @@ ares_status_t ares__send_query(struct query *query, const ares_timeval_t *now)
return ARES_SUCCESS;
}
static ares_bool_t same_questions(const struct query *query,
static ares_bool_t same_questions(const ares_query_t *query,
const ares_dns_record_t *arec)
{
size_t i;
@ -1337,7 +1275,7 @@ static ares_bool_t same_address(const struct sockaddr *sa,
return ARES_FALSE; /* different */
}
static void ares_detach_query(struct query *query)
static void ares_detach_query(ares_query_t *query)
{
/* Remove the query from all the lists in which it is linked */
ares__query_disassociate_from_conn(query);
@ -1346,8 +1284,8 @@ static void ares_detach_query(struct query *query)
query->node_all_queries = NULL;
}
static void end_query(ares_channel_t *channel, struct server_state *server,
struct query *query, ares_status_t status,
static void end_query(ares_channel_t *channel, ares_server_t *server,
ares_query_t *query, ares_status_t status,
const ares_dns_record_t *dnsrec)
{
ares_metrics_record(query, server, status, dnsrec);
@ -1364,7 +1302,7 @@ static void end_query(ares_channel_t *channel, struct server_state *server,
ares_queue_notify_empty(channel);
}
void ares__free_query(struct query *query)
void ares__free_query(ares_query_t *query)
{
ares_detach_query(query);
/* Zero out some important stuff, to help catch bugs */

@ -422,7 +422,7 @@ done:
ares_status_t ares_qcache_insert(ares_channel_t *channel,
const ares_timeval_t *now,
const struct query *query,
const ares_query_t *query,
ares_dns_record_t *dnsrec)
{
return ares__qcache_insert(channel->qcache, dnsrec, query->query, now);

@ -106,7 +106,7 @@ ares_status_t ares_send_nolock(ares_channel_t *channel,
ares_callback_dnsrec callback, void *arg,
unsigned short *qid)
{
struct query *query;
ares_query_t *query;
ares_timeval_t now;
ares_status_t status;
unsigned short id = generate_unique_qid(channel);
@ -129,7 +129,7 @@ ares_status_t ares_send_nolock(ares_channel_t *channel,
}
/* Allocate space for query and allocated fields. */
query = ares_malloc(sizeof(struct query));
query = ares_malloc(sizeof(ares_query_t));
if (!query) {
callback(arg, ARES_ENOMEM, 0, NULL); /* LCOV_EXCL_LINE: OutOfMemory */
return ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */

@ -88,7 +88,7 @@ static struct timeval *ares_timeout_int(const ares_channel_t *channel,
struct timeval *maxtv,
struct timeval *tvbuf)
{
const struct query *query;
const ares_query_t *query;
ares__slist_node_t *node;
ares_timeval_t now;
ares_timeval_t atvbuf;

@ -525,7 +525,7 @@ static ares__slist_node_t *ares__server_find(ares_channel_t *channel,
for (node = ares__slist_node_first(channel->servers); node != NULL;
node = ares__slist_node_next(node)) {
const struct server_state *server = ares__slist_node_val(node);
const ares_server_t *server = ares__slist_node_val(node);
if (!ares__addr_match(&server->addr, &s->addr)) {
continue;
@ -579,8 +579,8 @@ static ares_status_t ares__server_create(ares_channel_t *channel,
const ares_sconfig_t *sconfig,
size_t idx)
{
ares_status_t status;
struct server_state *server = ares_malloc_zero(sizeof(*server));
ares_status_t status;
ares_server_t *server = ares_malloc_zero(sizeof(*server));
if (server == NULL) {
return ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
@ -641,8 +641,8 @@ done:
return status;
}
static ares_bool_t ares__server_in_newconfig(const struct server_state *server,
ares__llist_t *srvlist)
static ares_bool_t ares__server_in_newconfig(const ares_server_t *server,
ares__llist_t *srvlist)
{
ares__llist_node_t *node;
const ares_channel_t *channel = server->channel;
@ -676,8 +676,8 @@ static ares_bool_t ares__servers_remove_stale(ares_channel_t *channel,
ares__slist_node_t *snode = ares__slist_node_first(channel->servers);
while (snode != NULL) {
ares__slist_node_t *snext = ares__slist_node_next(snode);
const struct server_state *server = ares__slist_node_val(snode);
ares__slist_node_t *snext = ares__slist_node_next(snode);
const ares_server_t *server = ares__slist_node_val(snode);
if (!ares__server_in_newconfig(server, srvlist)) {
/* This will clean up all server state via the destruction callback and
* move any queries to new servers */
@ -726,7 +726,7 @@ ares_status_t ares__servers_update(ares_channel_t *channel,
snode = ares__server_find(channel, sconfig);
if (snode != NULL) {
struct server_state *server = ares__slist_node_val(snode);
ares_server_t *server = ares__slist_node_val(snode);
/* Copy over link-local settings. Its possible some of this data has
* changed, maybe ... */
@ -928,8 +928,8 @@ fail:
}
/* Write out the details of a server to a buffer */
ares_status_t ares_get_server_addr(const struct server_state *server,
ares__buf_t *buf)
ares_status_t ares_get_server_addr(const ares_server_t *server,
ares__buf_t *buf)
{
ares_status_t status;
char addr[INET6_ADDRSTRLEN];
@ -1000,7 +1000,7 @@ int ares_get_servers(const ares_channel_t *channel,
for (node = ares__slist_node_first(channel->servers); node != NULL;
node = ares__slist_node_next(node)) {
const struct server_state *server = ares__slist_node_val(node);
const ares_server_t *server = ares__slist_node_val(node);
/* Allocate storage for this server node appending it to the list */
srvr_curr = ares_malloc_data(ARES_DATATYPE_ADDR_NODE);
@ -1055,7 +1055,7 @@ int ares_get_servers_ports(const ares_channel_t *channel,
for (node = ares__slist_node_first(channel->servers); node != NULL;
node = ares__slist_node_next(node)) {
const struct server_state *server = ares__slist_node_val(node);
const ares_server_t *server = ares__slist_node_val(node);
/* Allocate storage for this server node appending it to the list */
srvr_curr = ares_malloc_data(ARES_DATATYPE_ADDR_PORT_NODE);
@ -1203,8 +1203,8 @@ char *ares_get_servers_csv(const ares_channel_t *channel)
for (node = ares__slist_node_first(channel->servers); node != NULL;
node = ares__slist_node_next(node)) {
ares_status_t status;
const struct server_state *server = ares__slist_node_val(node);
ares_status_t status;
const ares_server_t *server = ares__slist_node_val(node);
if (ares__buf_len(buf)) {
status = ares__buf_append_byte(buf, ',');

@ -45,14 +45,14 @@ int ares_fds(const ares_channel_t *channel, fd_set *read_fds, fd_set *write_fds)
nfds = 0;
for (snode = ares__slist_node_first(channel->servers); snode != NULL;
snode = ares__slist_node_next(snode)) {
struct server_state *server = ares__slist_node_val(snode);
ares__llist_node_t *node;
ares_server_t *server = ares__slist_node_val(snode);
ares__llist_node_t *node;
for (node = ares__llist_node_first(server->connections); node != NULL;
node = ares__llist_node_next(node)) {
const struct server_connection *conn = ares__llist_node_val(node);
const ares_conn_t *conn = ares__llist_node_val(node);
if (!active_queries && !conn->is_tcp) {
if (!active_queries && !(conn->flags & ARES_CONN_FLAG_TCP)) {
continue;
}
@ -69,7 +69,7 @@ int ares_fds(const ares_channel_t *channel, fd_set *read_fds, fd_set *write_fds)
}
/* TCP only wait on write if we have buffered data */
if (conn->is_tcp && ares__buf_len(server->tcp_send)) {
if (conn->flags & ARES_CONN_FLAG_TCP && ares__buf_len(server->tcp_send)) {
FD_SET(conn->fd, write_fds);
}
}

@ -47,12 +47,12 @@ int ares_getsock(const ares_channel_t *channel, ares_socket_t *socks,
for (snode = ares__slist_node_first(channel->servers); snode != NULL;
snode = ares__slist_node_next(snode)) {
struct server_state *server = ares__slist_node_val(snode);
ares__llist_node_t *node;
ares_server_t *server = ares__slist_node_val(snode);
ares__llist_node_t *node;
for (node = ares__llist_node_first(server->connections); node != NULL;
node = ares__llist_node_next(node)) {
const struct server_connection *conn = ares__llist_node_val(node);
const ares_conn_t *conn = ares__llist_node_val(node);
if (sockindex >= (size_t)numsocks || sockindex >= ARES_GETSOCK_MAXNUM) {
break;
@ -61,17 +61,18 @@ int ares_getsock(const ares_channel_t *channel, ares_socket_t *socks,
/* We only need to register interest in UDP sockets if we have
* outstanding queries.
*/
if (!active_queries && !conn->is_tcp) {
if (!active_queries && !(conn->flags & ARES_CONN_FLAG_TCP)) {
continue;
}
socks[sockindex] = conn->fd;
if (active_queries || conn->is_tcp) {
if (active_queries || (conn->flags & ARES_CONN_FLAG_TCP)) {
bitmap |= ARES_GETSOCK_READABLE(setbits, sockindex);
}
if (conn->is_tcp && ares__buf_len(server->tcp_send)) {
if ((conn->flags & ARES_CONN_FLAG_TCP) &&
ares__buf_len(server->tcp_send)) {
/* then the tcp socket is also writable! */
bitmap |= ARES_GETSOCK_WRITABLE(setbits, sockindex);
}

15
test/ares-test.h vendored

@ -291,8 +291,8 @@ public:
private:
void ProcessRequest(ares_socket_t fd, struct sockaddr_storage *addr,
ares_socklen_t addrlen, const std::vector<byte> &req,
const std::string &reqstr,
int qid, const char *name, int rrtype);
const std::string &reqstr, int qid, const char *name,
int rrtype);
void ProcessPacket(ares_socket_t fd, struct sockaddr_storage *addr,
ares_socklen_t addrlen, byte *data, int len);
unsigned short udpport_;
@ -497,7 +497,6 @@ struct HostResult {
std::ostream &operator<<(std::ostream &os, const HostResult &result);
// C++ wrapper for ares_dns_record_t.
struct AresDnsRecord {
~AresDnsRecord()
@ -510,7 +509,8 @@ struct AresDnsRecord {
{
}
void SetDnsRecord(const ares_dns_record_t *dnsrec) {
void SetDnsRecord(const ares_dns_record_t *dnsrec)
{
if (dnsrec_ != NULL) {
ares_dns_record_destroy(dnsrec_);
}
@ -532,7 +532,7 @@ struct QueryResult {
}
// Whether the callback has been invoked.
bool done_;
bool done_;
// Explicitly provided result information.
ares_status_t status_;
size_t timeouts_;
@ -542,7 +542,6 @@ struct QueryResult {
std::ostream &operator<<(std::ostream &os, const QueryResult &result);
// Structure that describes the result of an ares_callback invocation.
struct SearchResult {
// Whether the callback has been invoked.
@ -603,8 +602,8 @@ std::ostream &operator<<(std::ostream &os, const AddrInfoResult &result);
// structures.
void HostCallback(void *data, int status, int timeouts,
struct hostent *hostent);
void QueryCallback(void *data, ares_status_t status, size_t timeouts,
const ares_dns_record_t *dnsrec);
void QueryCallback(void *data, ares_status_t status, size_t timeouts,
const ares_dns_record_t *dnsrec);
void SearchCallback(void *data, int status, int timeouts, unsigned char *abuf,
int alen);
void SearchCallbackDnsRec(void *data, ares_status_t status, size_t timeouts,

22
test/dns-proto.h vendored

@ -53,7 +53,7 @@ std::string RRTypeToString(int rrtype);
std::string ClassToString(int qclass);
std::string AddressToString(const void *addr, int len);
const ares_dns_rr_t *fetch_rr_opt(const ares_dns_record_t *rec);
const ares_dns_rr_t *fetch_rr_opt(const ares_dns_record_t *rec);
// Convert DNS protocol data to strings.
// Note that these functions are not defensive; they assume
@ -86,7 +86,8 @@ struct DNSQuestion {
{
}
virtual std::vector<byte> data(const char *request_name, const ares_dns_record_t *dnsrec) const;
virtual std::vector<byte> data(const char *request_name,
const ares_dns_record_t *dnsrec) const;
virtual std::vector<byte> data(const ares_dns_record_t *dnsrec) const
{
@ -288,11 +289,15 @@ struct DNSOption {
};
struct DNSOptRR : public DNSRR {
DNSOptRR(unsigned char extrcode, unsigned char version, unsigned short flags, int udpsize, std::vector<byte> client_cookie, std::vector<byte> server_cookie, bool expect_server_cookie)
: DNSRR("", T_OPT, static_cast<int>(udpsize), ((int)extrcode) << 24 | ((int)version) << 16 | ((int)flags)/* ttl */)
{
client_cookie_ = client_cookie;
server_cookie_ = server_cookie;
DNSOptRR(unsigned char extrcode, unsigned char version, unsigned short flags,
int udpsize, std::vector<byte> client_cookie,
std::vector<byte> server_cookie, bool expect_server_cookie)
: DNSRR("", T_OPT, static_cast<int>(udpsize),
((int)extrcode) << 24 | ((int)version) << 16 |
((int)flags) /* ttl */)
{
client_cookie_ = client_cookie;
server_cookie_ = server_cookie;
expect_server_cookie_ = expect_server_cookie;
}
@ -397,7 +402,8 @@ struct DNSPacket {
}
// Return the encoded packet.
std::vector<byte> data(const char *request_name, const ares_dns_record_t *dnsrec) const;
std::vector<byte> data(const char *request_name,
const ares_dns_record_t *dnsrec) const;
std::vector<byte> data() const
{

Loading…
Cancel
Save