Compare commits

..

6 Commits

Author SHA1 Message Date
Brad House df52bdec61 Android: <= 7 might require sys/system_properties.h 2 weeks ago
Brad House 854ed6b0fb fix backport issue 2 weeks ago
Jiwoo Park 2800298ca6 Use `_GNU_SOURCE` macro on Android (#914) 2 weeks ago
Jiwoo Park 81b1f7d29b Silence TSAN, lock before checking `ares_event_thread::isup` (#915) 2 weeks ago
Brad House 897f4fa689 ares_getaddrinfo() for AF_UNSPEC should retry if ipv6 received 2 weeks ago
Brad House 67951a9799 rewrite EBADRESP to EBADQUERY on ares_send 2 weeks ago
  1. 2
      CMakeLists.txt
  2. 5
      configure.ac
  3. 8
      src/lib/ares_event_thread.c
  4. 34
      src/lib/ares_getaddrinfo.c
  5. 5
      src/lib/ares_send.c
  6. 66
      test/ares-test-mock-ai.cc

@ -262,7 +262,7 @@ ENDIF ()
# Set system-specific compiler flags
IF (CMAKE_SYSTEM_NAME STREQUAL "Darwin")
LIST (APPEND SYSFLAGS -D_DARWIN_C_SOURCE)
ELSEIF (CMAKE_SYSTEM_NAME STREQUAL "Linux")
ELSEIF (CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "Android")
LIST (APPEND SYSFLAGS -D_GNU_SOURCE -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700)
ELSEIF (CMAKE_SYSTEM_NAME STREQUAL "SunOS")
LIST (APPEND SYSFLAGS -D__EXTENSIONS__ -D_REENTRANT -D_XOPEN_SOURCE=700)

@ -397,7 +397,7 @@ AS_HELP_STRING([--enable-libgcc],[use libgcc when linking]),
dnl check for a few basic system headers we need. It would be nice if we could
dnl split these on separate lines, but for some reason autotools on Windows doesn't
dnl allow this, even tried ending lines with a backslash.
AC_CHECK_HEADERS([malloc.h memory.h AvailabilityMacros.h sys/types.h sys/time.h sys/select.h sys/socket.h sys/filio.h sys/ioctl.h sys/param.h sys/uio.h sys/random.h sys/event.h sys/epoll.h assert.h iphlpapi.h netioapi.h netdb.h netinet/in.h netinet6/in6.h netinet/tcp.h net/if.h ifaddrs.h fcntl.h errno.h socket.h strings.h stdbool.h time.h poll.h limits.h arpa/nameser.h arpa/nameser_compat.h arpa/inet.h ],
AC_CHECK_HEADERS([malloc.h memory.h AvailabilityMacros.h sys/types.h sys/time.h sys/select.h sys/socket.h sys/filio.h sys/ioctl.h sys/param.h sys/uio.h sys/random.h sys/event.h sys/epoll.h assert.h iphlpapi.h netioapi.h netdb.h netinet/in.h netinet6/in6.h netinet/tcp.h net/if.h ifaddrs.h fcntl.h errno.h socket.h strings.h stdbool.h time.h poll.h limits.h arpa/nameser.h arpa/nameser_compat.h arpa/inet.h sys/system_properties.h ],
dnl to do if not found
[],
dnl to do if found
@ -512,6 +512,9 @@ cares_all_includes="
#ifdef HAVE_RESOLV_H
# include <resolv.h>
#endif
#ifdef HAVE_SYS_SYSTEM_PROPERTIES_H
# include <sys/system_properties.h>
#endif
#ifdef HAVE_IPHLPAPI_H
# include <iphlpapi.h>
#endif

@ -328,13 +328,15 @@ static void *ares_event_thread(void *arg)
e->ev_sys->wait(e, timeout_ms);
/* Relock before we loop again */
ares__thread_mutex_lock(e->mutex);
/* Each iteration should do timeout processing */
if (e->isup) {
ares__thread_mutex_unlock(e->mutex);
ares_process_fd(e->channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD);
ares__thread_mutex_lock(e->mutex);
}
/* Relock before we loop again */
ares__thread_mutex_lock(e->mutex);
}
/* Lets cleanup while we're in the thread itself */

@ -486,6 +486,18 @@ static void terminate_retries(const struct host_query *hquery,
query->no_retries = ARES_TRUE;
}
static ares_bool_t ai_has_ipv4(struct ares_addrinfo *ai)
{
struct ares_addrinfo_node *node;
for (node = ai->nodes; node != NULL; node = node->ai_next) {
if (node->ai_family == AF_INET) {
return ARES_TRUE;
}
}
return ARES_FALSE;
}
static void host_callback(void *arg, ares_status_t status, size_t timeouts,
const ares_dns_record_t *dnsrec)
{
@ -501,7 +513,27 @@ static void host_callback(void *arg, ares_status_t status, size_t timeouts,
addinfostatus =
ares__parse_into_addrinfo(dnsrec, ARES_TRUE, hquery->port, hquery->ai);
}
if (addinfostatus == ARES_SUCCESS) {
/* We sent out ipv4 and ipv6 requests simultaneously. If we got a
* successful ipv4 response, we want to go ahead and tell the ipv6 request
* that if it fails or times out to not try again since we have the data
* we need.
*
* Our initial implementation of this would terminate retries if we got any
* successful response (ipv4 _or_ ipv6). But we did get some user-reported
* issues with this that had bad system configs and odd behavior:
* https://github.com/alpinelinux/docker-alpine/issues/366
*
* Essentially the ipv6 query succeeded but the ipv4 query failed or timed
* out, and so we only returned the ipv6 address, but the host couldn't
* use ipv6. If we continued to allow ipv4 retries it would have found a
* server that worked and returned both address classes (this is clearly
* unexpected behavior).
*
* At some point down the road if ipv6 actually becomes required and
* reliable we can drop this ipv4 check.
*/
if (addinfostatus == ARES_SUCCESS && ai_has_ipv4(hquery->ai)) {
terminate_retries(hquery, ares_dns_record_get_id(dnsrec));
}
}

@ -86,6 +86,11 @@ ares_status_t ares_send_nolock(ares_channel_t *channel,
status = ares_dns_write(dnsrec, &query->qbuf, &query->qlen);
if (status != ARES_SUCCESS) {
/* Sometimes we might get a EBADRESP response from duplicate due to
* the way it works (write and parse), rewrite it to EBADQUERY. */
if (status == ARES_EBADRESP) {
status = ARES_EBADQUERY;
}
ares_free(query);
callback(arg, status, 0, NULL);
return status;

@ -721,6 +721,72 @@ class NoRotateMultiMockTestAI : public MockMultiServerChannelTestAI {
NoRotateMultiMockTestAI() : MockMultiServerChannelTestAI(nullptr, ARES_OPT_NOROTATE) {}
};
/* We want to terminate retries of other address classes on getaddrinfo if one
* address class is returned already to return replies faster.
* UPDATE: actually we want to do this only if the address class we received
* was ipv4. We've seen issues if ipv6 was returned but the host was
* really only capable of ipv4.
*/
TEST_P(NoRotateMultiMockTestAI, v4Worksv6Timesout) {
std::vector<byte> nothing;
DNSPacket rsp4;
rsp4.set_response().set_aa()
.add_question(new DNSQuestion("www.example.com", T_A))
.add_answer(new DNSARR("www.example.com", 0x0100, {0x01, 0x02, 0x03, 0x04}));
EXPECT_CALL(*servers_[0], OnRequest("www.example.com", T_A))
.WillOnce(SetReply(servers_[0].get(), &rsp4));
EXPECT_CALL(*servers_[0], OnRequest("www.example.com", T_AAAA))
.WillOnce(SetReplyData(servers_[0].get(), nothing));
AddrInfoResult result;
struct ares_addrinfo_hints hints = {0, 0, 0, 0};
hints.ai_family = AF_UNSPEC;
hints.ai_flags = ARES_AI_NOSORT;
ares_getaddrinfo(channel_, "www.example.com.", NULL, &hints, AddrInfoCallback, &result);
Process();
EXPECT_TRUE(result.done_);
EXPECT_EQ(result.status_, ARES_SUCCESS);
EXPECT_THAT(result.ai_, IncludesNumAddresses(1));
EXPECT_THAT(result.ai_, IncludesV4Address("1.2.3.4"));
}
TEST_P(NoRotateMultiMockTestAI, v6Worksv4TimesoutFirst) {
std::vector<byte> nothing;
DNSPacket rsp4;
rsp4.set_response().set_aa()
.add_question(new DNSQuestion("www.example.com", T_A))
.add_answer(new DNSARR("www.example.com", 0x0100, {0x01, 0x02, 0x03, 0x04}));
DNSPacket rsp6;
rsp6.set_response().set_aa()
.add_question(new DNSQuestion("www.example.com", T_AAAA))
.add_answer(new DNSAaaaRR("www.example.com", 100,
{0x21, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03}));
EXPECT_CALL(*servers_[0], OnRequest("www.example.com", T_A))
.WillOnce(SetReplyData(servers_[0].get(), nothing));
EXPECT_CALL(*servers_[0], OnRequest("www.example.com", T_AAAA))
.WillOnce(SetReply(servers_[0].get(), &rsp6));
EXPECT_CALL(*servers_[1], OnRequest("www.example.com", T_A))
.WillOnce(SetReply(servers_[1].get(), &rsp4));
AddrInfoResult result;
struct ares_addrinfo_hints hints = {0, 0, 0, 0};
hints.ai_family = AF_UNSPEC;
hints.ai_flags = ARES_AI_NOSORT;
ares_getaddrinfo(channel_, "www.example.com.", NULL, &hints, AddrInfoCallback, &result);
Process();
EXPECT_TRUE(result.done_);
EXPECT_EQ(result.status_, ARES_SUCCESS);
EXPECT_THAT(result.ai_, IncludesNumAddresses(2));
EXPECT_THAT(result.ai_, IncludesV4Address("1.2.3.4"));
EXPECT_THAT(result.ai_, IncludesV6Address("2121:0000:0000:0000:0000:0000:0000:0303"));
}
TEST_P(NoRotateMultiMockTestAI, ThirdServer) {
struct ares_options opts;

Loading…
Cancel
Save