diff --git a/src/lib/ares__hosts_file.c b/src/lib/ares__hosts_file.c index 761abfeb..83d77032 100644 --- a/src/lib/ares__hosts_file.c +++ b/src/lib/ares__hosts_file.c @@ -866,6 +866,7 @@ ares_status_t ares__hosts_entry_to_hostent(const ares_hosts_entry_t *entry, char **temp = NULL; memset(&addr, 0, sizeof(addr)); + addr.family = family; ptr = ares__parse_ipaddr(ipaddr, &addr, &ptr_len); if (ptr == NULL) @@ -873,6 +874,10 @@ ares_status_t ares__hosts_entry_to_hostent(const ares_hosts_entry_t *entry, /* If family == AF_UNSPEC, then we want to inherit this for future * conversions as we can only support a single address class */ + if (family == AF_UNSPEC) { + family = addr.family; + (*hostent)->h_addrtype = addr.family; + } temp = ares_realloc_zero((*hostent)->h_addr_list, (idx + 1) * sizeof(*(*hostent)->h_addr_list), diff --git a/src/lib/ares_getaddrinfo.c b/src/lib/ares_getaddrinfo.c index 809b3f7d..55809371 100644 --- a/src/lib/ares_getaddrinfo.c +++ b/src/lib/ares_getaddrinfo.c @@ -349,7 +349,7 @@ static void end_hquery(struct host_query *hquery, ares_status_t status) ares_free(hquery); } -static ares_bool_t is_localhost(const char *name) +ares_bool_t ares__is_localhost(const char *name) { /* RFC6761 6.3 says : The domain "localhost." and any names falling within * ".localhost." */ @@ -411,7 +411,7 @@ done: * 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 && - is_localhost(hquery->name)) { + ares__is_localhost(hquery->name)) { return ares__addrinfo_localhost(hquery->name, hquery->port, &hquery->hints, hquery->ai); } @@ -427,7 +427,7 @@ static void next_lookup(struct host_query *hquery, ares_status_t status) * queries for localhost names to their configured caching DNS * server(s)." * Otherwise, DNS lookup. */ - if (!is_localhost(hquery->name) && next_dns_lookup(hquery)) { + if (!ares__is_localhost(hquery->name) && next_dns_lookup(hquery)) { break; } diff --git a/src/lib/ares_gethostbyname.c b/src/lib/ares_gethostbyname.c index 2d10a074..12f5732c 100644 --- a/src/lib/ares_gethostbyname.c +++ b/src/lib/ares_gethostbyname.c @@ -231,6 +231,37 @@ static size_t get6_address_index(const struct ares_in6_addr *addr, } +static ares_status_t ares__hostent_localhost(const char *name, int family, + struct hostent **host_out) +{ + ares_status_t status; + struct ares_addrinfo *ai = NULL; + struct ares_addrinfo_hints hints; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = family; + + ai = ares_malloc_zero(sizeof(*ai)); + if (ai == NULL) { + status = ARES_ENOMEM; + goto done; + } + + status = ares__addrinfo_localhost(name, 0, &hints, ai); + if (status != ARES_SUCCESS) { + goto done; + } + + status = ares__addrinfo2hostent(ai, family, host_out); + if (status != ARES_SUCCESS) { + goto done; + } + +done: + ares_freeaddrinfo(ai); + return status; +} + /* I really have no idea why this is exposed as a public function, but since * it is, we can't kill this legacy function. */ int ares_gethostbyname_file(ares_channel channel, const char *name, int family, @@ -241,11 +272,12 @@ int ares_gethostbyname_file(ares_channel channel, const char *name, int family, ares_status_t status; /* We only take the channel to ensure that ares_init() been called. */ - if (channel == NULL) { + if (channel == NULL || name == NULL || host == NULL) { /* Anything will do, really. This seems fine, and is consistent with other error cases. */ - *host = NULL; - return (int)ARES_ENOTFOUND; + if (host != NULL) + *host = NULL; + return ARES_ENOTFOUND; } /* Per RFC 7686, reject queries for ".onion" domain names with NXDOMAIN. */ @@ -254,13 +286,26 @@ int ares_gethostbyname_file(ares_channel channel, const char *name, int family, } status = ares__hosts_search_host(channel, ARES_FALSE, name, &entry); - if (status != ARES_SUCCESS) - return (int)status; + if (status != ARES_SUCCESS) { + goto done; + } status = ares__hosts_entry_to_hostent(entry, family, host); - if (status != ARES_SUCCESS) - return (int)status; + if (status != ARES_SUCCESS) { + goto done; + } + +done: + /* RFC6761 section 6.3 #3 states that "Name resolution APIs and libraries + * 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)) { + return (int)ares__hostent_localhost(name, family, host); + } - return (int)ARES_SUCCESS; + return (int)status; } diff --git a/src/lib/ares_private.h b/src/lib/ares_private.h index b9346f64..79f18e12 100644 --- a/src/lib/ares_private.h +++ b/src/lib/ares_private.h @@ -413,6 +413,7 @@ ares_status_t ares__sortaddrinfo(ares_channel channel, struct ares_addrinfo_node *ai_node); void ares__freeaddrinfo_nodes(struct ares_addrinfo_node *ai_node); +ares_bool_t ares__is_localhost(const char *name); struct ares_addrinfo_node * ares__append_addrinfo_node(struct ares_addrinfo_node **ai_node);