Localhost resolution can fail if only one address family is in /etc/hosts (#947)

Resolution of 'localhost' is required to be resolved locally as per
RFC6761, and c-ares implements fallback cases if `/etc/hosts` does not
contain an entry for `localhost` as is common for Windows. However, if
`/etc/hosts` does have an address entry for one family (e.g. ipv4) but
not the other (e.g. ipv6), the fallback wasn't being called since an
entry was found.

We need to enhance the logic to always fallback if an address family is
missing after reading `/etc/hosts`. We will also add specific test cases
for this scenario to ensure it doesn't regress in the future.

Fixes #946
Signed-off-by: Brad House (@bradh352)
v1.28
Brad House 2 months ago
parent 026aa23091
commit 6f0ee598fc
  1. 196
      src/lib/ares__addrinfo2hostent.c
  2. 75
      src/lib/ares__addrinfo_localhost.c
  3. 8
      src/lib/ares__hosts_file.c
  4. 7
      src/lib/ares_free_hostent.c
  5. 12
      src/lib/ares_getaddrinfo.c
  6. 2
      src/lib/ares_gethostbyaddr.c
  7. 12
      src/lib/ares_gethostbyname.c
  8. 1
      src/lib/ares_parse_a_reply.c
  9. 1
      src/lib/ares_parse_aaaa_reply.c
  10. 176
      test/ares-test-mock-ai.cc
  11. 5
      test/ares-test-mock.cc

@ -39,8 +39,6 @@
# include <arpa/inet.h>
#endif
#include "ares_nameser.h"
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
@ -50,122 +48,156 @@
#endif
#include "ares.h"
#include "ares_dns.h"
#include "ares_inet_net_pton.h"
#include "ares_private.h"
static size_t hostent_nalias(const struct hostent *host)
{
size_t i;
for (i=0; host->h_aliases != NULL && host->h_aliases[i] != NULL; i++)
;
return i;
}
static size_t ai_nalias(const struct ares_addrinfo *ai)
{
const struct ares_addrinfo_cname *cname;
size_t i = 0;
for (cname = ai->cnames; cname != NULL; cname=cname->next) {
i++;
}
return i;
}
static size_t hostent_naddr(const struct hostent *host)
{
size_t i;
for (i=0; host->h_addr_list != NULL && host->h_addr_list[i] != NULL; i++)
;
return i;
}
static size_t ai_naddr(const struct ares_addrinfo *ai, int af)
{
const struct ares_addrinfo_node *node;
size_t i = 0;
for (node = ai->nodes; node != NULL; node=node->ai_next) {
if (af != AF_UNSPEC && af != node->ai_family)
continue;
i++;
}
return i;
}
ares_status_t ares__addrinfo2hostent(const struct ares_addrinfo *ai, int family,
struct hostent **host)
{
struct ares_addrinfo_node *next;
struct ares_addrinfo_cname *next_cname;
char **aliases = NULL;
char *addrs = NULL;
char **addrs = NULL;
size_t naliases = 0;
size_t naddrs = 0;
size_t alias = 0;
size_t i;
size_t ealiases = 0;
size_t eaddrs = 0;
if (ai == NULL || host == NULL) {
return ARES_EBADQUERY;
return ARES_EBADQUERY; /* LCOV_EXCL_LINE: DefensiveCoding */
}
/* Use the first node of the response as the family, since hostent can only
/* Use either the host set in the passed in hosts to be filled in, or the
* first node of the response as the family, since hostent can only
* represent one family. We assume getaddrinfo() returned a sorted list if
* the user requested AF_UNSPEC. */
if (family == AF_UNSPEC && ai->nodes) {
family = ai->nodes->ai_family;
if (family == AF_UNSPEC) {
if (*host != NULL && (*host)->h_addrtype != AF_UNSPEC) {
family = (*host)->h_addrtype;
} else if (ai->nodes != NULL) {
family = ai->nodes->ai_family;
}
}
if (family != AF_INET && family != AF_INET6) {
return ARES_EBADQUERY;
return ARES_EBADQUERY; /* LCOV_EXCL_LINE: DefensiveCoding */
}
*host = ares_malloc(sizeof(**host));
if (!(*host)) {
goto enomem;
if (*host == NULL) {
*host = ares_malloc_zero(sizeof(**host));
if (!(*host)) {
goto enomem; /* LCOV_EXCL_LINE: OutOfMemory */
}
}
memset(*host, 0, sizeof(**host));
next = ai->nodes;
while (next) {
if (next->ai_family == family) {
++naddrs;
}
next = next->ai_next;
(*host)->h_addrtype = (HOSTENT_ADDRTYPE_TYPE)family;
if (family == AF_INET) {
(*host)->h_length = sizeof(struct in_addr);
} else if (family == AF_INET6) {
(*host)->h_length = sizeof(struct ares_in6_addr);
}
next_cname = ai->cnames;
while (next_cname) {
if (next_cname->alias) {
++naliases;
if ((*host)->h_name == NULL) {
if (ai->cnames) {
(*host)->h_name = ares_strdup(ai->cnames->name);
if ((*host)->h_name == NULL && ai->cnames->name) {
goto enomem; /* LCOV_EXCL_LINE: OutOfMemory */
}
} else {
(*host)->h_name = ares_strdup(ai->name);
if ((*host)->h_name == NULL && ai->name) {
goto enomem; /* LCOV_EXCL_LINE: OutOfMemory */
}
}
next_cname = next_cname->next;
}
aliases = ares_malloc((naliases + 1) * sizeof(char *));
naliases = ai_nalias(ai);
ealiases = hostent_nalias(*host);
aliases = ares_realloc_zero((*host)->h_aliases,
ealiases * sizeof(char *),
(naliases + ealiases + 1) * sizeof(char *));
if (!aliases) {
goto enomem;
goto enomem; /* LCOV_EXCL_LINE: OutOfMemory */
}
(*host)->h_aliases = aliases;
memset(aliases, 0, (naliases + 1) * sizeof(char *));
if (naliases) {
for (next_cname = ai->cnames; next_cname != NULL;
next_cname = next_cname->next) {
if (next_cname->alias == NULL) {
const struct ares_addrinfo_cname *cname;
i = ealiases;
for (cname = ai->cnames; cname != NULL; cname = cname->next) {
if (cname->alias == NULL) {
continue;
}
aliases[alias] = ares_strdup(next_cname->alias);
if (!aliases[alias]) {
goto enomem;
(*host)->h_aliases[i] = ares_strdup(cname->alias);
if ((*host)->h_aliases[i] == NULL) {
goto enomem; /* LCOV_EXCL_LINE: OutOfMemory */
}
alias++;
}
}
(*host)->h_addr_list = ares_malloc((naddrs + 1) * sizeof(char *));
if (!(*host)->h_addr_list) {
goto enomem;
}
memset((*host)->h_addr_list, 0, (naddrs + 1) * sizeof(char *));
if (ai->cnames) {
(*host)->h_name = ares_strdup(ai->cnames->name);
if ((*host)->h_name == NULL && ai->cnames->name) {
goto enomem;
}
} else {
(*host)->h_name = ares_strdup(ai->name);
if ((*host)->h_name == NULL && ai->name) {
goto enomem;
i++;
}
}
(*host)->h_addrtype = (HOSTENT_ADDRTYPE_TYPE)family;
if (family == AF_INET) {
(*host)->h_length = sizeof(struct in_addr);
}
if (family == AF_INET6) {
(*host)->h_length = sizeof(struct ares_in6_addr);
naddrs = ai_naddr(ai, family);
eaddrs = hostent_naddr(*host);
addrs = ares_realloc_zero((*host)->h_addr_list, eaddrs * sizeof(char *),
(naddrs + eaddrs + 1) * sizeof(char *));
if (addrs == NULL) {
goto enomem; /* LCOV_EXCL_LINE: OutOfMemory */
}
(*host)->h_addr_list = addrs;
if (naddrs) {
addrs = ares_malloc(naddrs * (size_t)(*host)->h_length);
if (!addrs) {
goto enomem;
}
i = 0;
i = eaddrs;
for (next = ai->nodes; next != NULL; next = next->ai_next) {
if (next->ai_family != family) {
continue;
}
(*host)->h_addr_list[i] = addrs + (i * (size_t)(*host)->h_length);
(*host)->h_addr_list[i] = ares_malloc_zero((size_t)(*host)->h_length);
if ((*host)->h_addr_list[i] == NULL) {
goto enomem; /* LCOV_EXCL_LINE: OutOfMemory */
}
if (family == AF_INET6) {
memcpy(
(*host)->h_addr_list[i],
@ -178,15 +210,11 @@ ares_status_t ares__addrinfo2hostent(const struct ares_addrinfo *ai, int family,
&(CARES_INADDR_CAST(struct sockaddr_in *, next->ai_addr)->sin_addr),
(size_t)(*host)->h_length);
}
++i;
}
if (i == 0) {
ares_free(addrs);
i++;
}
}
if (naddrs == 0 && naliases == 0) {
if (naddrs + eaddrs == 0 && naliases + ealiases == 0) {
ares_free_hostent(*host);
*host = NULL;
return ARES_ENODATA;
@ -194,10 +222,12 @@ ares_status_t ares__addrinfo2hostent(const struct ares_addrinfo *ai, int family,
return ARES_SUCCESS;
/* LCOV_EXCL_START: OutOfMemory */
enomem:
ares_free_hostent(*host);
*host = NULL;
return ARES_ENOMEM;
/* LCOV_EXCL_STOP */
}
ares_status_t ares__addrinfo2addrttl(const struct ares_addrinfo *ai, int family,
@ -211,23 +241,23 @@ ares_status_t ares__addrinfo2addrttl(const struct ares_addrinfo *ai, int family,
int cname_ttl = INT_MAX;
if (family != AF_INET && family != AF_INET6) {
return ARES_EBADQUERY;
return ARES_EBADQUERY; /* LCOV_EXCL_LINE: DefensiveCoding */
}
if (ai == NULL || naddrttls == NULL) {
return ARES_EBADQUERY;
return ARES_EBADQUERY; /* LCOV_EXCL_LINE: DefensiveCoding */
}
if (family == AF_INET && addrttls == NULL) {
return ARES_EBADQUERY;
return ARES_EBADQUERY; /* LCOV_EXCL_LINE: DefensiveCoding */
}
if (family == AF_INET6 && addr6ttls == NULL) {
return ARES_EBADQUERY;
return ARES_EBADQUERY; /* LCOV_EXCL_LINE: DefensiveCoding */
}
if (req_naddrttls == 0) {
return ARES_EBADQUERY;
return ARES_EBADQUERY; /* LCOV_EXCL_LINE: DefensiveCoding */
}
*naddrttls = 0;

@ -37,11 +37,10 @@
# include <arpa/inet.h>
#endif
#if defined(_WIN32) && defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0600
# include <ws2ipdef.h>
#endif
#if defined(USE_WINSOCK)
# if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0600
# include <ws2ipdef.h>
# endif
# if defined(HAVE_IPHLPAPI_H)
# include <iphlpapi.h>
# endif
@ -54,6 +53,19 @@
#include "ares_inet_net_pton.h"
#include "ares_private.h"
static ares_bool_t ares_ai_has_family(int aftype,
const struct ares_addrinfo_node *nodes)
{
const struct ares_addrinfo_node *node;
for (node = nodes; node != NULL; node = node->ai_next) {
if (node->ai_family == aftype)
return ARES_TRUE;
}
return ARES_FALSE;
}
ares_status_t ares_append_ai_node(int aftype, unsigned short port,
unsigned int ttl, const void *adata,
struct ares_addrinfo_node **nodes)
@ -62,7 +74,7 @@ ares_status_t ares_append_ai_node(int aftype, unsigned short port,
node = ares__append_addrinfo_node(nodes);
if (!node) {
return ARES_ENOMEM;
return ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
}
memset(node, 0, sizeof(*node));
@ -70,7 +82,7 @@ ares_status_t ares_append_ai_node(int aftype, unsigned short port,
if (aftype == AF_INET) {
struct sockaddr_in *sin = ares_malloc(sizeof(*sin));
if (!sin) {
return ARES_ENOMEM;
return ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
}
memset(sin, 0, sizeof(*sin));
@ -88,7 +100,7 @@ ares_status_t ares_append_ai_node(int aftype, unsigned short port,
if (aftype == AF_INET6) {
struct sockaddr_in6 *sin6 = ares_malloc(sizeof(*sin6));
if (!sin6) {
return ARES_ENOMEM;
return ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
}
memset(sin6, 0, sizeof(*sin6));
@ -112,21 +124,23 @@ static ares_status_t
{
ares_status_t status = ARES_SUCCESS;
if (aftype == AF_UNSPEC || aftype == AF_INET6) {
if ((aftype == AF_UNSPEC || aftype == AF_INET6) &&
!ares_ai_has_family(AF_INET6, *nodes)) {
struct ares_in6_addr addr6;
ares_inet_pton(AF_INET6, "::1", &addr6);
status = ares_append_ai_node(AF_INET6, port, 0, &addr6, nodes);
if (status != ARES_SUCCESS) {
return status;
return status; /* LCOV_EXCL_LINE: OutOfMemory */
}
}
if (aftype == AF_UNSPEC || aftype == AF_INET) {
if ((aftype == AF_UNSPEC || aftype == AF_INET) &&
!ares_ai_has_family(AF_INET, *nodes)) {
struct in_addr addr4;
ares_inet_pton(AF_INET, "127.0.0.1", &addr4);
status = ares_append_ai_node(AF_INET, port, 0, &addr4, nodes);
if (status != ARES_SUCCESS) {
return status;
return status; /* LCOV_EXCL_LINE: OutOfMemory */
}
}
@ -137,7 +151,7 @@ static ares_status_t
ares__system_loopback_addrs(int aftype, unsigned short port,
struct ares_addrinfo_node **nodes)
{
#if defined(_WIN32) && defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0600 && \
#if defined(USE_WINSOCK) && defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0600 && \
!defined(__WATCOMC__)
PMIB_UNICASTIPADDRESS_TABLE table;
unsigned int i;
@ -155,11 +169,13 @@ static ares_status_t
continue;
}
if (table->Table[i].Address.si_family == AF_INET) {
if (table->Table[i].Address.si_family == AF_INET &&
!ares_ai_has_family(AF_INET, *nodes)) {
status =
ares_append_ai_node(table->Table[i].Address.si_family, port, 0,
&table->Table[i].Address.Ipv4.sin_addr, nodes);
} else if (table->Table[i].Address.si_family == AF_INET6) {
} else if (table->Table[i].Address.si_family == AF_INET6 &&
!ares_ai_has_family(AF_INET6, *nodes)) {
status =
ares_append_ai_node(table->Table[i].Address.si_family, port, 0,
&table->Table[i].Address.Ipv6.sin6_addr, nodes);
@ -200,8 +216,7 @@ ares_status_t ares__addrinfo_localhost(const char *name, unsigned short port,
const struct ares_addrinfo_hints *hints,
struct ares_addrinfo *ai)
{
struct ares_addrinfo_node *nodes = NULL;
ares_status_t status;
ares_status_t status;
/* Validate family */
switch (hints->ai_family) {
@ -209,28 +224,26 @@ ares_status_t ares__addrinfo_localhost(const char *name, unsigned short port,
case AF_INET6:
case AF_UNSPEC:
break;
default:
return ARES_EBADFAMILY;
default: /* LCOV_EXCL_LINE: DefensiveCoding */
return ARES_EBADFAMILY; /* LCOV_EXCL_LINE: DefensiveCoding */
}
if (ai->name != NULL) {
ares_free(ai->name);
}
ai->name = ares_strdup(name);
if (!ai->name) {
goto enomem;
if (ai->name == NULL) {
status = ARES_ENOMEM;
goto done; /* LCOV_EXCL_LINE: OutOfMemory */
}
status = ares__system_loopback_addrs(hints->ai_family, port, &nodes);
if (status == ARES_ENOTFOUND) {
status = ares__default_loopback_addrs(hints->ai_family, port, &nodes);
status = ares__system_loopback_addrs(hints->ai_family, port, &ai->nodes);
if (status != ARES_SUCCESS && status != ARES_ENOTFOUND) {
goto done;
}
ares__addrinfo_cat_nodes(&ai->nodes, nodes);
status = ares__default_loopback_addrs(hints->ai_family, port, &ai->nodes);
done:
return status;
enomem:
ares__freeaddrinfo_nodes(nodes);
ares_free(ai->name);
ai->name = NULL;
return ARES_ENOMEM;
}

@ -847,7 +847,7 @@ ares_status_t ares__hosts_entry_to_addrinfo(const ares_hosts_entry_t *entry,
ares_bool_t want_cnames,
struct ares_addrinfo *ai)
{
ares_status_t status;
ares_status_t status = ARES_ENOTFOUND;
struct ares_addrinfo_cname *cnames = NULL;
struct ares_addrinfo_node *ainodes = NULL;
ares__llist_node_t *node;
@ -862,6 +862,7 @@ ares_status_t ares__hosts_entry_to_addrinfo(const ares_hosts_entry_t *entry,
}
if (name != NULL) {
ares_free(ai->name);
ai->name = ares_strdup(name);
if (ai->name == NULL) {
status = ARES_ENOMEM;
@ -890,6 +891,11 @@ ares_status_t ares__hosts_entry_to_addrinfo(const ares_hosts_entry_t *entry,
}
}
/* Might be ARES_ENOTFOUND here if no ips matched requested address family */
if (status != ARES_SUCCESS) {
goto done;
}
if (want_cnames) {
status = ares__hosts_ai_append_cnames(entry, &cnames);
if (status != ARES_SUCCESS) {

@ -48,9 +48,10 @@ void ares_free_hostent(struct hostent *host)
}
ares_free(host->h_aliases);
if (host->h_addr_list) {
ares_free(
host->h_addr_list[0]); /* no matter if there is one or many entries,
there is only one malloc for all of them */
size_t i;
for (i=0; host->h_addr_list[i] != NULL; i++) {
ares_free(host->h_addr_list[i]);
}
ares_free(host->h_addr_list);
}
ares_free(host);

@ -424,11 +424,15 @@ done:
* SHOULD recognize localhost names as special and SHOULD always return the
* IP loopback address for address queries".
* We will also ignore ALL errors when trying to resolve localhost, such
* as permissions errors reading /etc/hosts or a malformed /etc/hosts */
if (status != ARES_SUCCESS && status != ARES_ENOMEM &&
ares__is_localhost(hquery->name)) {
* as permissions errors reading /etc/hosts or a malformed /etc/hosts.
*
* Also, just because the query itself returned success from /etc/hosts
* lookup doesn't mean it returned everything it needed to for all requested
* address families. As long as we're not on a critical out of memory
* condition pass it through to fill in any other address classes. */
if (status != ARES_ENOMEM && ares__is_localhost(hquery->name)) {
return ares__addrinfo_localhost(hquery->name, hquery->port, &hquery->hints,
hquery->ai);
hquery->ai);
}
return status;

@ -126,7 +126,7 @@ static void next_lookup(struct addr_query *aquery)
{
const char *p;
ares_status_t status;
struct hostent *host;
struct hostent *host = NULL;
char *name;
for (p = aquery->remaining_lookups; *p; p++) {

@ -286,6 +286,8 @@ static ares_status_t ares_gethostbyname_file_int(ares_channel_t *channel,
return ARES_ENOTFOUND;
}
*host = NULL;
/* Per RFC 7686, reject queries for ".onion" domain names with NXDOMAIN. */
if (ares__is_onion_domain(name)) {
return ARES_ENOTFOUND;
@ -306,9 +308,13 @@ done:
* SHOULD recognize localhost names as special and SHOULD always return the
* IP loopback address for address queries".
* We will also ignore ALL errors when trying to resolve localhost, such
* as permissions errors reading /etc/hosts or a malformed /etc/hosts */
if (status != ARES_SUCCESS && status != ARES_ENOMEM &&
ares__is_localhost(name)) {
* as permissions errors reading /etc/hosts or a malformed /etc/hosts.
*
* Also, just because the query itself returned success from /etc/hosts
* lookup doesn't mean it returned everything it needed to for all requested
* address families. As long as we're not on a critical out of memory
* condition pass it through to fill in any other address classes. */
if (status != ARES_ENOMEM && ares__is_localhost(name)) {
return ares__hostent_localhost(name, family, host);
}

@ -83,6 +83,7 @@ int ares_parse_a_reply(const unsigned char *abuf, int alen,
}
if (host != NULL) {
*host = NULL;
status = ares__addrinfo2hostent(&ai, AF_INET, host);
if (status != ARES_SUCCESS && status != ARES_ENODATA) {
goto fail;

@ -85,6 +85,7 @@ int ares_parse_aaaa_reply(const unsigned char *abuf, int alen,
}
if (host != NULL) {
*host = NULL;
status = ares__addrinfo2hostent(&ai, AF_INET6, host);
if (status != ARES_SUCCESS && status != ARES_ENODATA) {
goto fail;

@ -792,6 +792,182 @@ TEST_P(MockChannelTestAI, FamilyV4ServiceName) {
EXPECT_EQ("{addr=[1.1.1.1:80], addr=[2.2.2.2:80]}", ss.str());
}
#ifdef HAVE_CONTAINER
class ContainedMockChannelAISysConfig
: public MockChannelOptsTest,
public ::testing::WithParamInterface<std::pair<int, bool>> {
public:
ContainedMockChannelAISysConfig()
: MockChannelOptsTest(1, GetParam().first, GetParam().second, true, nullptr, 0) {}
};
static NameContentList files_no_ndots = {
{"/etc/resolv.conf", "nameserver 1.2.3.4\n" // Will be replaced
"search example.com example.org\n"
"options edns0 trust-ad\n"}, // ndots:1 is default
{"/etc/hosts", "3.4.5.6 ahostname.com\n"},
{"/etc/nsswitch.conf", "hosts: files dns\n"}};
/* These tests should still work even with /etc/hosts not having any localhost
* entries */
CONTAINED_TEST_P(ContainedMockChannelAISysConfig, NoHostsLocalHostv4,
"myhostname", "mydomainname.org", files_no_ndots) {
AddrInfoResult result = {};
struct ares_addrinfo_hints hints = {0, 0, 0, 0};
hints.ai_family = AF_INET;
hints.ai_flags = ARES_AI_NOSORT;
ares_getaddrinfo(channel_, "localhost", 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("127.0.0.1"));
return HasFailure();
}
CONTAINED_TEST_P(ContainedMockChannelAISysConfig, NoHostsLocalHostv6,
"myhostname", "mydomainname.org", files_no_ndots) {
AddrInfoResult result = {};
struct ares_addrinfo_hints hints = {0, 0, 0, 0};
hints.ai_family = AF_INET6;
hints.ai_flags = ARES_AI_NOSORT;
ares_getaddrinfo(channel_, "localhost", 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_, IncludesV6Address("0000:0000:0000:0000:0000:0000:0000:0001"));
return HasFailure();
}
CONTAINED_TEST_P(ContainedMockChannelAISysConfig, NoHostsLocalHostUnspec,
"myhostname", "mydomainname.org", files_no_ndots) {
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_, "localhost", 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("127.0.0.1"));
EXPECT_THAT(result.ai_, IncludesV6Address("0000:0000:0000:0000:0000:0000:0000:0001"));
return HasFailure();
}
/* Issue #946 says if a v4 localhost entry exists, but not a v6 entry, v6
* isn't output correctly. */
static NameContentList files_localhost_v4localhostonly = {
{"/etc/resolv.conf", "nameserver 1.2.3.4\n" // Will be replaced
"search example.com example.org\n"
"options edns0 trust-ad\n"}, // ndots:1 is default
{"/etc/hosts", "127.0.0.1 localhost\n"},
{"/etc/nsswitch.conf", "hosts: files dns\n"}};
CONTAINED_TEST_P(ContainedMockChannelAISysConfig, v4OnlyLocalHostv4,
"myhostname", "mydomainname.org", files_localhost_v4localhostonly) {
AddrInfoResult result = {};
struct ares_addrinfo_hints hints = {0, 0, 0, 0};
hints.ai_family = AF_INET;
hints.ai_flags = ARES_AI_NOSORT;
ares_getaddrinfo(channel_, "localhost", 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("127.0.0.1"));
return HasFailure();
}
CONTAINED_TEST_P(ContainedMockChannelAISysConfig, v4OnlyLocalHostv6,
"myhostname", "mydomainname.org", files_localhost_v4localhostonly) {
AddrInfoResult result = {};
struct ares_addrinfo_hints hints = {0, 0, 0, 0};
hints.ai_family = AF_INET6;
hints.ai_flags = ARES_AI_NOSORT;
ares_getaddrinfo(channel_, "localhost", 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_, IncludesV6Address("0000:0000:0000:0000:0000:0000:0000:0001"));
return HasFailure();
}
CONTAINED_TEST_P(ContainedMockChannelAISysConfig, v4OnlyLocalHostUnspec,
"myhostname", "mydomainname.org", files_localhost_v4localhostonly) {
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_, "localhost", 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("127.0.0.1"));
EXPECT_THAT(result.ai_, IncludesV6Address("0000:0000:0000:0000:0000:0000:0000:0001"));
return HasFailure();
}
static NameContentList files_localhost_v6localhostonly = {
{"/etc/resolv.conf", "nameserver 1.2.3.4\n" // Will be replaced
"search example.com example.org\n"
"options edns0 trust-ad\n"}, // ndots:1 is default
{"/etc/hosts", "::1 localhost\n"},
{"/etc/nsswitch.conf", "hosts: files dns\n"}};
CONTAINED_TEST_P(ContainedMockChannelAISysConfig, v6OnlyLocalHostv4,
"myhostname", "mydomainname.org", files_localhost_v6localhostonly) {
AddrInfoResult result = {};
struct ares_addrinfo_hints hints = {0, 0, 0, 0};
hints.ai_family = AF_INET;
hints.ai_flags = ARES_AI_NOSORT;
ares_getaddrinfo(channel_, "localhost", 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("127.0.0.1"));
return HasFailure();
}
CONTAINED_TEST_P(ContainedMockChannelAISysConfig, v6OnlyLocalHostv6,
"myhostname", "mydomainname.org", files_localhost_v6localhostonly) {
AddrInfoResult result = {};
struct ares_addrinfo_hints hints = {0, 0, 0, 0};
hints.ai_family = AF_INET6;
hints.ai_flags = ARES_AI_NOSORT;
ares_getaddrinfo(channel_, "localhost", 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_, IncludesV6Address("0000:0000:0000:0000:0000:0000:0000:0001"));
return HasFailure();
}
CONTAINED_TEST_P(ContainedMockChannelAISysConfig, v6OnlyLocalHostUnspec,
"myhostname", "mydomainname.org", files_localhost_v6localhostonly) {
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_, "localhost", 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("127.0.0.1"));
EXPECT_THAT(result.ai_, IncludesV6Address("0000:0000:0000:0000:0000:0000:0000:0001"));
return HasFailure();
}
INSTANTIATE_TEST_SUITE_P(AddressFamiliesAI, ContainedMockChannelAISysConfig, ::testing::ValuesIn(ares::test::families_modes), PrintFamilyMode);
#endif
INSTANTIATE_TEST_SUITE_P(AddressFamiliesAI, MockChannelTestAI,
::testing::ValuesIn(ares::test::families_modes), PrintFamilyMode);

@ -627,7 +627,7 @@ class ContainedMockChannelSysConfig
: MockChannelOptsTest(1, GetParam().first, GetParam().second, true, nullptr, 0) {}
};
NameContentList files_no_ndots = {
static NameContentList files_no_ndots = {
{"/etc/resolv.conf", "nameserver 1.2.3.4\n" // Will be replaced
"search example.com example.org\n"
"options edns0 trust-ad\n"}, // ndots:1 is default
@ -652,7 +652,8 @@ CONTAINED_TEST_P(ContainedMockChannelSysConfig, SysConfigNdotsDefault,
return HasFailure();
}
NameContentList files_ndots0 = {
static NameContentList files_ndots0 = {
{"/etc/resolv.conf", "nameserver 1.2.3.4\n" // Will be replaced
"search example.com example.org\n"
"options edns0 trust-ad ndots:0\n"}, // ndots:1 is default

Loading…
Cancel
Save