propagate actual error condition on requeue

v1.28
Brad House 4 months ago
parent 2728b48871
commit 7584c93b28
  1. 14
      src/lib/ares__close_sockets.c
  2. 6
      src/lib/ares_private.h
  3. 54
      src/lib/ares_process.c
  4. 2
      test/ares-test-mock-ai.cc
  5. 2
      test/ares-test-mock-et.cc
  6. 2
      test/ares-test-mock.cc

@ -31,17 +31,19 @@
#include "ares_private.h" #include "ares_private.h"
#include <assert.h> #include <assert.h>
static void ares__requeue_queries(struct server_connection *conn) static void ares__requeue_queries(struct server_connection *conn,
ares_status_t requeue_status)
{ {
struct query *query; struct query *query;
struct timeval now = ares__tvnow(); struct timeval now = ares__tvnow();
while ((query = ares__llist_first_val(conn->queries_to_conn)) != NULL) { while ((query = ares__llist_first_val(conn->queries_to_conn)) != NULL) {
ares__requeue_query(query, &now); ares__requeue_query(query, &now, requeue_status);
} }
} }
void ares__close_connection(struct server_connection *conn) void ares__close_connection(struct server_connection *conn,
ares_status_t requeue_status)
{ {
struct server_state *server = conn->server; struct server_state *server = conn->server;
ares_channel_t *channel = server->channel; ares_channel_t *channel = server->channel;
@ -59,7 +61,7 @@ void ares__close_connection(struct server_connection *conn)
} }
/* Requeue queries to other connections */ /* Requeue queries to other connections */
ares__requeue_queries(conn); ares__requeue_queries(conn, requeue_status);
ares__llist_destroy(conn->queries_to_conn); ares__llist_destroy(conn->queries_to_conn);
@ -75,7 +77,7 @@ void ares__close_sockets(struct server_state *server)
while ((node = ares__llist_node_first(server->connections)) != NULL) { while ((node = ares__llist_node_first(server->connections)) != NULL) {
struct server_connection *conn = ares__llist_node_val(node); struct server_connection *conn = ares__llist_node_val(node);
ares__close_connection(conn); ares__close_connection(conn, ARES_SUCCESS);
} }
} }
@ -107,5 +109,5 @@ void ares__check_cleanup_conn(const ares_channel_t *channel,
return; return;
} }
ares__close_connection(conn); ares__close_connection(conn, ARES_SUCCESS);
} }

@ -334,7 +334,8 @@ ares_bool_t ares__timedout(const struct timeval *now,
/* Returns one of the normal ares status codes like ARES_SUCCESS */ /* Returns one of the normal ares status codes like ARES_SUCCESS */
ares_status_t ares__send_query(struct query *query, struct timeval *now); ares_status_t ares__send_query(struct query *query, struct timeval *now);
ares_status_t ares__requeue_query(struct query *query, struct timeval *now); ares_status_t ares__requeue_query(struct query *query, struct timeval *now,
ares_status_t status);
/*! Retrieve a list of names to use for searching. The first successful /*! Retrieve a list of names to use for searching. The first successful
* query in the list wins. This function also uses the HOSTSALIASES file * query in the list wins. This function also uses the HOSTSALIASES file
@ -362,7 +363,8 @@ void *ares__dnsrec_convert_arg(ares_callback callback, void *arg);
void ares__dnsrec_convert_cb(void *arg, ares_status_t status, size_t timeouts, void ares__dnsrec_convert_cb(void *arg, ares_status_t status, size_t timeouts,
const ares_dns_record_t *dnsrec); const ares_dns_record_t *dnsrec);
void ares__close_connection(struct server_connection *conn); void ares__close_connection(struct server_connection *conn,
ares_status_t requeue_status);
void ares__close_sockets(struct server_state *server); void ares__close_sockets(struct server_state *server);
void ares__check_cleanup_conn(const ares_channel_t *channel, void ares__check_cleanup_conn(const ares_channel_t *channel,
struct server_connection *conn); struct server_connection *conn);

@ -61,7 +61,8 @@ static ares_status_t process_answer(ares_channel_t *channel,
struct server_connection *conn, struct server_connection *conn,
ares_bool_t tcp, struct timeval *now); ares_bool_t tcp, struct timeval *now);
static void handle_conn_error(struct server_connection *conn, static void handle_conn_error(struct server_connection *conn,
ares_bool_t critical_failure); ares_bool_t critical_failure,
ares_status_t failure_status);
static ares_bool_t same_questions(const ares_dns_record_t *qrec, static ares_bool_t same_questions(const ares_dns_record_t *qrec,
const ares_dns_record_t *arec); const ares_dns_record_t *arec);
@ -252,7 +253,7 @@ static void write_tcp_data(ares_channel_t *channel, fd_set *write_fds,
count = ares__socket_write(channel, server->tcp_conn->fd, data, data_len); count = ares__socket_write(channel, server->tcp_conn->fd, data, data_len);
if (count <= 0) { if (count <= 0) {
if (!try_again(SOCKERRNO)) { if (!try_again(SOCKERRNO)) {
handle_conn_error(server->tcp_conn, ARES_TRUE); handle_conn_error(server->tcp_conn, ARES_TRUE, ARES_ECONNREFUSED);
} }
continue; continue;
} }
@ -284,7 +285,7 @@ static void read_tcp_data(ares_channel_t *channel,
ptr = ares__buf_append_start(server->tcp_parser, &ptr_len); ptr = ares__buf_append_start(server->tcp_parser, &ptr_len);
if (ptr == NULL) { if (ptr == NULL) {
handle_conn_error(conn, ARES_FALSE /* not critical to connection */); handle_conn_error(conn, ARES_FALSE /* not critical to connection */, ARES_SUCCESS);
return; /* bail out on malloc failure. TODO: make this return; /* bail out on malloc failure. TODO: make this
function return error codes */ function return error codes */
} }
@ -294,7 +295,7 @@ static void read_tcp_data(ares_channel_t *channel,
if (count <= 0) { if (count <= 0) {
ares__buf_append_finish(server->tcp_parser, 0); ares__buf_append_finish(server->tcp_parser, 0);
if (!(count == -1 && try_again(SOCKERRNO))) { if (!(count == -1 && try_again(SOCKERRNO))) {
handle_conn_error(conn, ARES_TRUE); handle_conn_error(conn, ARES_TRUE, ARES_ECONNREFUSED);
} }
return; return;
} }
@ -338,7 +339,7 @@ static void read_tcp_data(ares_channel_t *channel,
/* We finished reading this answer; process it */ /* We finished reading this answer; process it */
status = process_answer(channel, data, data_len, conn, ARES_TRUE, now); status = process_answer(channel, data, data_len, conn, ARES_TRUE, now);
if (status != ARES_SUCCESS) { if (status != ARES_SUCCESS) {
handle_conn_error(conn, ARES_TRUE); handle_conn_error(conn, ARES_TRUE, status);
return; return;
} }
@ -453,7 +454,7 @@ static void read_udp_packets_fd(ares_channel_t *channel,
break; break;
} }
handle_conn_error(conn, ARES_TRUE); handle_conn_error(conn, ARES_TRUE, ARES_ECONNREFUSED);
return; return;
#ifdef HAVE_RECVFROM #ifdef HAVE_RECVFROM
} else if (!same_address(&from.sa, &conn->server->addr)) { } else if (!same_address(&from.sa, &conn->server->addr)) {
@ -557,12 +558,11 @@ static void process_timeouts(ares_channel_t *channel, struct timeval *now)
break; break;
} }
query->error_status = ARES_ETIMEOUT;
query->timeouts++; query->timeouts++;
conn = query->conn; conn = query->conn;
server_increment_failures(conn->server); server_increment_failures(conn->server);
ares__requeue_query(query, now); ares__requeue_query(query, now, ARES_ETIMEOUT);
ares__check_cleanup_conn(channel, conn); ares__check_cleanup_conn(channel, conn);
node = next; node = next;
@ -704,20 +704,20 @@ static ares_status_t process_answer(ares_channel_t *channel,
rcode == ARES_RCODE_REFUSED) { rcode == ARES_RCODE_REFUSED) {
switch (rcode) { switch (rcode) {
case ARES_RCODE_SERVFAIL: case ARES_RCODE_SERVFAIL:
query->error_status = ARES_ESERVFAIL; status = ARES_ESERVFAIL;
break; break;
case ARES_RCODE_NOTIMP: case ARES_RCODE_NOTIMP:
query->error_status = ARES_ENOTIMP; status = ARES_ENOTIMP;
break; break;
case ARES_RCODE_REFUSED: case ARES_RCODE_REFUSED:
query->error_status = ARES_EREFUSED; status = ARES_EREFUSED;
break; break;
default: default:
break; break;
} }
server_increment_failures(server); server_increment_failures(server);
ares__requeue_query(query, now); ares__requeue_query(query, now, status);
/* Should any of these cause a connection termination? /* Should any of these cause a connection termination?
* Maybe SERVER_FAILURE? */ * Maybe SERVER_FAILURE? */
@ -748,7 +748,8 @@ cleanup:
} }
static void handle_conn_error(struct server_connection *conn, static void handle_conn_error(struct server_connection *conn,
ares_bool_t critical_failure) ares_bool_t critical_failure,
ares_status_t failure_status)
{ {
struct server_state *server = conn->server; struct server_state *server = conn->server;
@ -759,16 +760,20 @@ static void handle_conn_error(struct server_connection *conn,
} }
/* This will requeue any connections automatically */ /* This will requeue any connections automatically */
ares__close_connection(conn); ares__close_connection(conn, failure_status);
} }
ares_status_t ares__requeue_query(struct query *query, struct timeval *now) ares_status_t ares__requeue_query(struct query *query, struct timeval *now,
ares_status_t status)
{ {
ares_channel_t *channel = query->channel; ares_channel_t *channel = query->channel;
size_t max_tries = ares__slist_len(channel->servers) * channel->tries; size_t max_tries = ares__slist_len(channel->servers) * channel->tries;
query->try_count++; if (status != ARES_SUCCESS) {
query->error_status = status;
}
query->try_count++;
if (query->try_count < max_tries && !query->no_retries) { if (query->try_count < max_tries && !query->no_retries) {
return ares__send_query(query, now); return ares__send_query(query, now);
} }
@ -919,8 +924,7 @@ ares_status_t ares__send_query(struct query *query, struct timeval *now)
case ARES_ECONNREFUSED: case ARES_ECONNREFUSED:
case ARES_EBADFAMILY: case ARES_EBADFAMILY:
server_increment_failures(server); server_increment_failures(server);
query->error_status = status; return ares__requeue_query(query, now, status);
return ares__requeue_query(query, now);
/* Anything else is not retryable, likely ENOMEM */ /* Anything else is not retryable, likely ENOMEM */
default: default:
@ -940,7 +944,7 @@ ares_status_t ares__send_query(struct query *query, struct timeval *now)
/* Only safe to kill connection if it was new, otherwise it should be /* Only safe to kill connection if it was new, otherwise it should be
* cleaned up by another process later */ * cleaned up by another process later */
if (new_connection) { if (new_connection) {
ares__close_connection(conn); ares__close_connection(conn, status);
} }
return status; return status;
} }
@ -978,8 +982,7 @@ ares_status_t ares__send_query(struct query *query, struct timeval *now)
case ARES_ECONNREFUSED: case ARES_ECONNREFUSED:
case ARES_EBADFAMILY: case ARES_EBADFAMILY:
server_increment_failures(server); server_increment_failures(server);
query->error_status = status; return ares__requeue_query(query, now, status);
return ares__requeue_query(query, now);
/* Anything else is not retryable, likely ENOMEM */ /* Anything else is not retryable, likely ENOMEM */
default: default:
@ -992,13 +995,14 @@ ares_status_t ares__send_query(struct query *query, struct timeval *now)
conn = ares__llist_node_val(node); conn = ares__llist_node_val(node);
if (ares__socket_write(channel, conn->fd, query->qbuf, query->qlen) == -1) { if (ares__socket_write(channel, conn->fd, query->qbuf, query->qlen) == -1) {
/* FIXME: Handle EAGAIN here since it likely can happen. */ /* FIXME: Handle EAGAIN here since it likely can happen. */
status = ARES_ESERVFAIL;
server_increment_failures(server); server_increment_failures(server);
status = ares__requeue_query(query, now); status = ares__requeue_query(query, now, status);
/* Only safe to kill connection if it was new, otherwise it should be /* Only safe to kill connection if it was new, otherwise it should be
* cleaned up by another process later */ * cleaned up by another process later */
if (new_connection) { if (new_connection) {
ares__close_connection(conn); ares__close_connection(conn, status);
} }
return status; return status;
@ -1019,7 +1023,7 @@ ares_status_t ares__send_query(struct query *query, struct timeval *now)
/* Only safe to kill connection if it was new, otherwise it should be /* Only safe to kill connection if it was new, otherwise it should be
* cleaned up by another process later */ * cleaned up by another process later */
if (new_connection) { if (new_connection) {
ares__close_connection(conn); ares__close_connection(conn, ARES_SUCCESS);
} }
return ARES_ENOMEM; return ARES_ENOMEM;
} }
@ -1035,7 +1039,7 @@ ares_status_t ares__send_query(struct query *query, struct timeval *now)
/* Only safe to kill connection if it was new, otherwise it should be /* Only safe to kill connection if it was new, otherwise it should be
* cleaned up by another process later */ * cleaned up by another process later */
if (new_connection) { if (new_connection) {
ares__close_connection(conn); ares__close_connection(conn, ARES_SUCCESS);
} }
return ARES_ENOMEM; return ARES_ENOMEM;
} }

@ -165,7 +165,7 @@ TEST_P(MockTCPChannelTestAI, MalformedResponse) {
ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result); ares_getaddrinfo(channel_, "www.google.com.", NULL, &hints, AddrInfoCallback, &result);
Process(); Process();
EXPECT_TRUE(result.done_); EXPECT_TRUE(result.done_);
EXPECT_EQ(ARES_ETIMEOUT, result.status_); EXPECT_EQ(ARES_EBADRESP, result.status_);
} }
TEST_P(MockTCPChannelTestAI, FormErrResponse) { TEST_P(MockTCPChannelTestAI, FormErrResponse) {

@ -366,7 +366,7 @@ TEST_P(MockTCPEventThreadTest, MalformedResponse) {
ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result); ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result);
Process(); Process();
EXPECT_TRUE(result.done_); EXPECT_TRUE(result.done_);
EXPECT_EQ(ARES_ETIMEOUT, result.status_); EXPECT_EQ(ARES_EBADRESP, result.status_);
} }
TEST_P(MockTCPEventThreadTest, FormErrResponse) { TEST_P(MockTCPEventThreadTest, FormErrResponse) {

@ -387,7 +387,7 @@ TEST_P(MockTCPChannelTest, MalformedResponse) {
ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result); ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result);
Process(); Process();
EXPECT_TRUE(result.done_); EXPECT_TRUE(result.done_);
EXPECT_EQ(ARES_ETIMEOUT, result.status_); EXPECT_EQ(ARES_EBADRESP, result.status_);
} }
TEST_P(MockTCPChannelTest, FormErrResponse) { TEST_P(MockTCPChannelTest, FormErrResponse) {

Loading…
Cancel
Save