Skip to content

Commit

Permalink
fix localhost resolution
Browse files Browse the repository at this point in the history
  • Loading branch information
bradh352 committed Dec 27, 2024
1 parent b494025 commit b47e055
Show file tree
Hide file tree
Showing 10 changed files with 165 additions and 101 deletions.
169 changes: 100 additions & 69 deletions src/lib/ares_addrinfo2hostent.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,119 +47,154 @@
# include <limits.h>
#endif

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; /* 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; /* LCOV_EXCL_LINE: DefensiveCoding */
}

*host = ares_malloc(sizeof(**host));
if (!(*host)) {
goto enomem; /* LCOV_EXCL_LINE: OutOfMemory */
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; /* 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]) {
(*host)->h_aliases[i] = ares_strdup(cname->alias);
if ((*host)->h_aliases[i] == NULL) {
goto enomem; /* LCOV_EXCL_LINE: OutOfMemory */
}
alias++;
i++;
}
}


(*host)->h_addr_list = ares_malloc((naddrs + 1) * sizeof(char *));
if (!(*host)->h_addr_list) {
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 */
}

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; /* 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 */
}
}

(*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);
}
(*host)->h_addr_list = addrs;

if (naddrs) {
addrs = ares_malloc(naddrs * (size_t)(*host)->h_length);
if (!addrs) {
goto enomem; /* LCOV_EXCL_LINE: OutOfMemory */
}

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],
&(CARES_INADDR_CAST(const struct sockaddr_in6 *, next->ai_addr)
Expand All @@ -172,15 +207,11 @@ ares_status_t ares_addrinfo2hostent(const struct ares_addrinfo *ai, int family,
->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;
Expand Down
54 changes: 33 additions & 21 deletions src/lib/ares_addrinfo_localhost.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,19 @@
# endif
#endif

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)
Expand Down Expand Up @@ -107,7 +120,8 @@ 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);
Expand All @@ -116,7 +130,8 @@ static ares_status_t
}
}

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);
Expand Down Expand Up @@ -150,11 +165,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);
Expand Down Expand Up @@ -195,8 +212,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) {
Expand All @@ -208,26 +224,22 @@ ares_status_t ares_addrinfo_localhost(const char *name, unsigned short port,
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; /* LCOV_EXCL_LINE: OutOfMemory */
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;

/* LCOV_EXCL_START: OutOfMemory */
enomem:
ares_freeaddrinfo_nodes(nodes);
ares_free(ai->name);
ai->name = NULL;
return ARES_ENOMEM;
/* LCOV_EXCL_STOP */
}
7 changes: 4 additions & 3 deletions src/lib/ares_free_hostent.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,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);
Expand Down
10 changes: 7 additions & 3 deletions src/lib/ares_getaddrinfo.c
Original file line number Diff line number Diff line change
Expand Up @@ -418,9 +418,13 @@ static ares_status_t file_lookup(struct host_query *hquery)
* 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);
}
Expand Down
2 changes: 1 addition & 1 deletion src/lib/ares_gethostbyaddr.c
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,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++) {
Expand Down
Loading

0 comments on commit b47e055

Please sign in to comment.