diff --git a/src/lib/ares_addrinfo2hostent.c b/src/lib/ares_addrinfo2hostent.c index 2bbc791157..a1d3c869db 100644 --- a/src/lib/ares_addrinfo2hostent.c +++ b/src/lib/ares_addrinfo2hostent.c @@ -47,119 +47,152 @@ # include #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) +{ + const struct ares_addrinfo_node *node; + size_t i = 0; + + for (node = ai->nodes; node != NULL; node=node->ai_next) { + 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); + 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) @@ -172,15 +205,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; diff --git a/src/lib/ares_addrinfo_localhost.c b/src/lib/ares_addrinfo_localhost.c index 8c6246a2eb..2abb0c48a6 100644 --- a/src/lib/ares_addrinfo_localhost.c +++ b/src/lib/ares_addrinfo_localhost.c @@ -224,8 +224,11 @@ 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) { + if (ai->name == NULL) { status = ARES_ENOMEM; goto done; /* LCOV_EXCL_LINE: OutOfMemory */ } diff --git a/src/lib/ares_free_hostent.c b/src/lib/ares_free_hostent.c index bf2037238b..dfcbdf4910 100644 --- a/src/lib/ares_free_hostent.c +++ b/src/lib/ares_free_hostent.c @@ -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); diff --git a/src/lib/ares_gethostbyaddr.c b/src/lib/ares_gethostbyaddr.c index a7acf3c45c..69c509ab11 100644 --- a/src/lib/ares_gethostbyaddr.c +++ b/src/lib/ares_gethostbyaddr.c @@ -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++) { diff --git a/src/lib/ares_gethostbyname.c b/src/lib/ares_gethostbyname.c index b3aada9449..d451b46851 100644 --- a/src/lib/ares_gethostbyname.c +++ b/src/lib/ares_gethostbyname.c @@ -287,6 +287,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; diff --git a/src/lib/ares_hosts_file.c b/src/lib/ares_hosts_file.c index 3c4656c0be..d18863b8f6 100644 --- a/src/lib/ares_hosts_file.c +++ b/src/lib/ares_hosts_file.c @@ -860,6 +860,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; /* LCOV_EXCL_LINE: OutOfMemory */ diff --git a/src/lib/ares_private.h b/src/lib/ares_private.h index e6d44e8b86..84f07caf1f 100644 --- a/src/lib/ares_private.h +++ b/src/lib/ares_private.h @@ -455,8 +455,10 @@ ares_status_t ares_parse_ptr_reply_dnsrec(const ares_dns_record_t *dnsrec, const void *addr, int addrlen, int family, struct hostent **host); +/* host address must be valid or NULL as will create or append */ ares_status_t ares_addrinfo2hostent(const struct ares_addrinfo *ai, int family, struct hostent **host); + ares_status_t ares_addrinfo2addrttl(const struct ares_addrinfo *ai, int family, size_t req_naddrttls, struct ares_addrttl *addrttls, diff --git a/src/lib/legacy/ares_parse_a_reply.c b/src/lib/legacy/ares_parse_a_reply.c index 870aaccf76..9fd4a07ac0 100644 --- a/src/lib/legacy/ares_parse_a_reply.c +++ b/src/lib/legacy/ares_parse_a_reply.c @@ -77,6 +77,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; /* LCOV_EXCL_LINE: DefensiveCoding */ diff --git a/src/lib/legacy/ares_parse_aaaa_reply.c b/src/lib/legacy/ares_parse_aaaa_reply.c index 278642f0b3..4c177ec9cb 100644 --- a/src/lib/legacy/ares_parse_aaaa_reply.c +++ b/src/lib/legacy/ares_parse_aaaa_reply.c @@ -80,6 +80,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; /* LCOV_EXCL_LINE: DefensiveCoding */