diff options
author | robot-contrib <robot-contrib@yandex-team.com> | 2024-10-17 11:21:35 +0300 |
---|---|---|
committer | robot-contrib <robot-contrib@yandex-team.com> | 2024-10-17 11:35:23 +0300 |
commit | 3682ce7f2626a0e22e9cf839b808faed3c5d70d6 (patch) | |
tree | f7d409766375889badc8397a73ec0571a74b5ee8 /contrib/libs/c-ares/src/lib/ares_hosts_file.c | |
parent | 09691831380ca7c15cfed3445465e27837c4130d (diff) | |
download | ydb-3682ce7f2626a0e22e9cf839b808faed3c5d70d6.tar.gz |
Update contrib/libs/c-ares to 1.34.2
commit_hash:abd65f143c484666e1545e6bbfb9d947df95ecd6
Diffstat (limited to 'contrib/libs/c-ares/src/lib/ares_hosts_file.c')
-rw-r--r-- | contrib/libs/c-ares/src/lib/ares_hosts_file.c | 946 |
1 files changed, 946 insertions, 0 deletions
diff --git a/contrib/libs/c-ares/src/lib/ares_hosts_file.c b/contrib/libs/c-ares/src/lib/ares_hosts_file.c new file mode 100644 index 0000000000..0439b8e1d4 --- /dev/null +++ b/contrib/libs/c-ares/src/lib/ares_hosts_file.c @@ -0,0 +1,946 @@ +/* MIT License + * + * Copyright (c) 2023 Brad House + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * SPDX-License-Identifier: MIT + */ +#include "ares_private.h" +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif +#ifdef HAVE_SYS_STAT_H +# include <sys/stat.h> +#endif +#ifdef HAVE_NETINET_IN_H +# include <netinet/in.h> +#endif +#ifdef HAVE_NETDB_H +# include <netdb.h> +#endif +#ifdef HAVE_ARPA_INET_H +# include <arpa/inet.h> +#endif +#include <time.h> + +/* HOSTS FILE PROCESSING OVERVIEW + * ============================== + * The hosts file on the system contains static entries to be processed locally + * rather than querying the nameserver. Each row is an IP address followed by + * a list of space delimited hostnames that match the ip address. This is used + * for both forward and reverse lookups. + * + * We are caching the entire parsed hosts file for performance reasons. Some + * files may be quite sizable and as per Issue #458 can approach 1/2MB in size, + * and the parse overhead on a rapid succession of queries can be quite large. + * The entries are stored in forwards and backwards hashtables so we can get + * O(1) performance on lookup. The file is cached until the file modification + * timestamp changes. + * + * The hosts file processing is quite unique. It has to merge all related hosts + * and ips into a single entry due to file formatting requirements. For + * instance take the below: + * + * 127.0.0.1 localhost.localdomain localhost + * ::1 localhost.localdomain localhost + * 192.168.1.1 host.example.com host + * 192.168.1.5 host.example.com host + * 2620:1234::1 host.example.com host6.example.com host6 host + * + * This will yield 2 entries. + * 1) ips: 127.0.0.1,::1 + * hosts: localhost.localdomain,localhost + * 2) ips: 192.168.1.1,192.168.1.5,2620:1234::1 + * hosts: host.example.com,host,host6.example.com,host6 + * + * It could be argued that if searching for 192.168.1.1 that the 'host6' + * hostnames should not be returned, but this implementation will return them + * since they are related. It is unlikely this will matter in the real world. + */ + +struct ares_hosts_file { + time_t ts; + /*! cache the filename so we know if the filename changes it automatically + * invalidates the cache */ + char *filename; + /*! iphash is the owner of the 'entry' object as there is only ever a single + * match to the object. */ + ares_htable_strvp_t *iphash; + /*! hosthash does not own the entry so won't free on destruction */ + ares_htable_strvp_t *hosthash; +}; + +struct ares_hosts_entry { + size_t refcnt; /*! If the entry is stored multiple times in the + * ip address hash, we have to reference count it */ + ares_llist_t *ips; + ares_llist_t *hosts; +}; + +const void *ares_dns_pton(const char *ipaddr, struct ares_addr *addr, + size_t *out_len) +{ + const void *ptr = NULL; + size_t ptr_len = 0; + + if (ipaddr == NULL || addr == NULL || out_len == NULL) { + return NULL; /* LCOV_EXCL_LINE: DefensiveCoding */ + } + + *out_len = 0; + + if (addr->family == AF_INET && + ares_inet_pton(AF_INET, ipaddr, &addr->addr.addr4) > 0) { + ptr = &addr->addr.addr4; + ptr_len = sizeof(addr->addr.addr4); + } else if (addr->family == AF_INET6 && + ares_inet_pton(AF_INET6, ipaddr, &addr->addr.addr6) > 0) { + ptr = &addr->addr.addr6; + ptr_len = sizeof(addr->addr.addr6); + } else if (addr->family == AF_UNSPEC) { + if (ares_inet_pton(AF_INET, ipaddr, &addr->addr.addr4) > 0) { + addr->family = AF_INET; + ptr = &addr->addr.addr4; + ptr_len = sizeof(addr->addr.addr4); + } else if (ares_inet_pton(AF_INET6, ipaddr, &addr->addr.addr6) > 0) { + addr->family = AF_INET6; + ptr = &addr->addr.addr6; + ptr_len = sizeof(addr->addr.addr6); + } + } + + *out_len = ptr_len; + return ptr; +} + +static ares_bool_t ares_normalize_ipaddr(const char *ipaddr, char *out, + size_t out_len) +{ + struct ares_addr data; + const void *addr; + size_t addr_len = 0; + + memset(&data, 0, sizeof(data)); + data.family = AF_UNSPEC; + + addr = ares_dns_pton(ipaddr, &data, &addr_len); + if (addr == NULL) { + return ARES_FALSE; + } + + if (!ares_inet_ntop(data.family, addr, out, (ares_socklen_t)out_len)) { + return ARES_FALSE; /* LCOV_EXCL_LINE: DefensiveCoding */ + } + + return ARES_TRUE; +} + +static void ares_hosts_entry_destroy(ares_hosts_entry_t *entry) +{ + if (entry == NULL) { + return; + } + + /* Honor reference counting */ + if (entry->refcnt != 0) { + entry->refcnt--; + } + + if (entry->refcnt > 0) { + return; + } + + ares_llist_destroy(entry->hosts); + ares_llist_destroy(entry->ips); + ares_free(entry); +} + +static void ares_hosts_entry_destroy_cb(void *entry) +{ + ares_hosts_entry_destroy(entry); +} + +void ares_hosts_file_destroy(ares_hosts_file_t *hf) +{ + if (hf == NULL) { + return; + } + + ares_free(hf->filename); + ares_htable_strvp_destroy(hf->hosthash); + ares_htable_strvp_destroy(hf->iphash); + ares_free(hf); +} + +static ares_hosts_file_t *ares_hosts_file_create(const char *filename) +{ + ares_hosts_file_t *hf = ares_malloc_zero(sizeof(*hf)); + if (hf == NULL) { + goto fail; + } + + hf->ts = time(NULL); + + hf->filename = ares_strdup(filename); + if (hf->filename == NULL) { + goto fail; + } + + hf->iphash = ares_htable_strvp_create(ares_hosts_entry_destroy_cb); + if (hf->iphash == NULL) { + goto fail; + } + + hf->hosthash = ares_htable_strvp_create(NULL); + if (hf->hosthash == NULL) { + goto fail; + } + + return hf; + +fail: + ares_hosts_file_destroy(hf); + return NULL; +} + +typedef enum { + ARES_MATCH_NONE = 0, + ARES_MATCH_IPADDR = 1, + ARES_MATCH_HOST = 2 +} ares_hosts_file_match_t; + +static ares_status_t ares_hosts_file_merge_entry( + const ares_hosts_file_t *hf, ares_hosts_entry_t *existing, + ares_hosts_entry_t *entry, ares_hosts_file_match_t matchtype) +{ + ares_llist_node_t *node; + + /* If we matched on IP address, we know there can only be 1, so there's no + * reason to do anything */ + if (matchtype != ARES_MATCH_IPADDR) { + while ((node = ares_llist_node_first(entry->ips)) != NULL) { + const char *ipaddr = ares_llist_node_val(node); + + if (ares_htable_strvp_get_direct(hf->iphash, ipaddr) != NULL) { + ares_llist_node_destroy(node); + continue; + } + + ares_llist_node_mvparent_last(node, existing->ips); + } + } + + + while ((node = ares_llist_node_first(entry->hosts)) != NULL) { + const char *hostname = ares_llist_node_val(node); + + if (ares_htable_strvp_get_direct(hf->hosthash, hostname) != NULL) { + ares_llist_node_destroy(node); + continue; + } + + ares_llist_node_mvparent_last(node, existing->hosts); + } + + ares_hosts_entry_destroy(entry); + return ARES_SUCCESS; +} + +static ares_hosts_file_match_t + ares_hosts_file_match(const ares_hosts_file_t *hf, ares_hosts_entry_t *entry, + ares_hosts_entry_t **match) +{ + ares_llist_node_t *node; + *match = NULL; + + for (node = ares_llist_node_first(entry->ips); node != NULL; + node = ares_llist_node_next(node)) { + const char *ipaddr = ares_llist_node_val(node); + *match = ares_htable_strvp_get_direct(hf->iphash, ipaddr); + if (*match != NULL) { + return ARES_MATCH_IPADDR; + } + } + + for (node = ares_llist_node_first(entry->hosts); node != NULL; + node = ares_llist_node_next(node)) { + const char *host = ares_llist_node_val(node); + *match = ares_htable_strvp_get_direct(hf->hosthash, host); + if (*match != NULL) { + return ARES_MATCH_HOST; + } + } + + return ARES_MATCH_NONE; +} + +/*! entry is invalidated upon calling this function, always, even on error */ +static ares_status_t ares_hosts_file_add(ares_hosts_file_t *hosts, + ares_hosts_entry_t *entry) +{ + ares_hosts_entry_t *match = NULL; + ares_status_t status = ARES_SUCCESS; + ares_llist_node_t *node; + ares_hosts_file_match_t matchtype; + size_t num_hostnames; + + /* Record the number of hostnames in this entry file. If we merge into an + * existing record, these will be *appended* to the entry, so we'll count + * backwards when adding to the hosts hashtable */ + num_hostnames = ares_llist_len(entry->hosts); + + matchtype = ares_hosts_file_match(hosts, entry, &match); + + if (matchtype != ARES_MATCH_NONE) { + status = ares_hosts_file_merge_entry(hosts, match, entry, matchtype); + if (status != ARES_SUCCESS) { + ares_hosts_entry_destroy(entry); /* LCOV_EXCL_LINE: DefensiveCoding */ + return status; /* LCOV_EXCL_LINE: DefensiveCoding */ + } + /* entry was invalidated above by merging */ + entry = match; + } + + if (matchtype != ARES_MATCH_IPADDR) { + const char *ipaddr = ares_llist_last_val(entry->ips); + + if (!ares_htable_strvp_get(hosts->iphash, ipaddr, NULL)) { + if (!ares_htable_strvp_insert(hosts->iphash, ipaddr, entry)) { + ares_hosts_entry_destroy(entry); + return ARES_ENOMEM; + } + entry->refcnt++; + } + } + + /* Go backwards, on a merge, hostnames are appended. Breakout once we've + * consumed all the hosts that we appended */ + for (node = ares_llist_node_last(entry->hosts); node != NULL; + node = ares_llist_node_prev(node)) { + const char *val = ares_llist_node_val(node); + + if (num_hostnames == 0) { + break; + } + + num_hostnames--; + + /* first hostname match wins. If we detect a duplicate hostname for another + * ip it will automatically be added to the same entry */ + if (ares_htable_strvp_get(hosts->hosthash, val, NULL)) { + continue; + } + + if (!ares_htable_strvp_insert(hosts->hosthash, val, entry)) { + return ARES_ENOMEM; + } + } + + return ARES_SUCCESS; +} + +static ares_bool_t ares_hosts_entry_isdup(ares_hosts_entry_t *entry, + const char *host) +{ + ares_llist_node_t *node; + + for (node = ares_llist_node_first(entry->ips); node != NULL; + node = ares_llist_node_next(node)) { + const char *myhost = ares_llist_node_val(node); + if (ares_strcaseeq(myhost, host)) { + return ARES_TRUE; + } + } + + return ARES_FALSE; +} + +static ares_status_t ares_parse_hosts_hostnames(ares_buf_t *buf, + ares_hosts_entry_t *entry) +{ + entry->hosts = ares_llist_create(ares_free); + if (entry->hosts == NULL) { + return ARES_ENOMEM; + } + + /* Parse hostnames and aliases */ + while (ares_buf_len(buf)) { + char hostname[256]; + char *temp; + ares_status_t status; + unsigned char comment = '#'; + + ares_buf_consume_whitespace(buf, ARES_FALSE); + + if (ares_buf_len(buf) == 0) { + break; + } + + /* See if it is a comment, if so stop processing */ + if (ares_buf_begins_with(buf, &comment, 1)) { + break; + } + + ares_buf_tag(buf); + + /* Must be at end of line */ + if (ares_buf_consume_nonwhitespace(buf) == 0) { + break; + } + + status = ares_buf_tag_fetch_string(buf, hostname, sizeof(hostname)); + if (status != ARES_SUCCESS) { + /* Bad entry, just ignore as long as its not the first. If its the first, + * it must be valid */ + if (ares_llist_len(entry->hosts) == 0) { + return ARES_EBADSTR; + } + + continue; + } + + /* Validate it is a valid hostname characterset */ + if (!ares_is_hostname(hostname)) { + continue; + } + + /* Don't add a duplicate to the same entry */ + if (ares_hosts_entry_isdup(entry, hostname)) { + continue; + } + + /* Add to list */ + temp = ares_strdup(hostname); + if (temp == NULL) { + return ARES_ENOMEM; + } + + if (ares_llist_insert_last(entry->hosts, temp) == NULL) { + ares_free(temp); + return ARES_ENOMEM; + } + } + + /* Must have at least 1 entry */ + if (ares_llist_len(entry->hosts) == 0) { + return ARES_EBADSTR; + } + + return ARES_SUCCESS; +} + +static ares_status_t ares_parse_hosts_ipaddr(ares_buf_t *buf, + ares_hosts_entry_t **entry_out) +{ + char addr[INET6_ADDRSTRLEN]; + char *temp; + ares_hosts_entry_t *entry = NULL; + ares_status_t status; + + *entry_out = NULL; + + ares_buf_tag(buf); + ares_buf_consume_nonwhitespace(buf); + status = ares_buf_tag_fetch_string(buf, addr, sizeof(addr)); + if (status != ARES_SUCCESS) { + return status; + } + + /* Validate and normalize the ip address format */ + if (!ares_normalize_ipaddr(addr, addr, sizeof(addr))) { + return ARES_EBADSTR; + } + + entry = ares_malloc_zero(sizeof(*entry)); + if (entry == NULL) { + return ARES_ENOMEM; + } + + entry->ips = ares_llist_create(ares_free); + if (entry->ips == NULL) { + ares_hosts_entry_destroy(entry); + return ARES_ENOMEM; + } + + temp = ares_strdup(addr); + if (temp == NULL) { + ares_hosts_entry_destroy(entry); + return ARES_ENOMEM; + } + + if (ares_llist_insert_first(entry->ips, temp) == NULL) { + ares_free(temp); + ares_hosts_entry_destroy(entry); + return ARES_ENOMEM; + } + + *entry_out = entry; + + return ARES_SUCCESS; +} + +static ares_status_t ares_parse_hosts(const char *filename, + ares_hosts_file_t **out) +{ + ares_buf_t *buf = NULL; + ares_status_t status = ARES_EBADRESP; + ares_hosts_file_t *hf = NULL; + ares_hosts_entry_t *entry = NULL; + + *out = NULL; + + buf = ares_buf_create(); + if (buf == NULL) { + status = ARES_ENOMEM; + goto done; + } + + status = ares_buf_load_file(filename, buf); + if (status != ARES_SUCCESS) { + goto done; + } + + hf = ares_hosts_file_create(filename); + if (hf == NULL) { + status = ARES_ENOMEM; + goto done; + } + + while (ares_buf_len(buf)) { + unsigned char comment = '#'; + + /* -- Start of new line here -- */ + + /* Consume any leading whitespace */ + ares_buf_consume_whitespace(buf, ARES_FALSE); + + if (ares_buf_len(buf) == 0) { + break; + } + + /* See if it is a comment, if so, consume remaining line */ + if (ares_buf_begins_with(buf, &comment, 1)) { + ares_buf_consume_line(buf, ARES_TRUE); + continue; + } + + /* Pull off ip address */ + status = ares_parse_hosts_ipaddr(buf, &entry); + if (status == ARES_ENOMEM) { + goto done; + } + if (status != ARES_SUCCESS) { + /* Bad line, consume and go onto next */ + ares_buf_consume_line(buf, ARES_TRUE); + continue; + } + + /* Parse of the hostnames */ + status = ares_parse_hosts_hostnames(buf, entry); + if (status == ARES_ENOMEM) { + goto done; + } else if (status != ARES_SUCCESS) { + /* Bad line, consume and go onto next */ + ares_hosts_entry_destroy(entry); + entry = NULL; + ares_buf_consume_line(buf, ARES_TRUE); + continue; + } + + /* Append the successful entry to the hosts file */ + status = ares_hosts_file_add(hf, entry); + entry = NULL; /* is always invalidated by this function, even on error */ + if (status != ARES_SUCCESS) { + goto done; + } + + /* Go to next line */ + ares_buf_consume_line(buf, ARES_TRUE); + } + + status = ARES_SUCCESS; + +done: + ares_hosts_entry_destroy(entry); + ares_buf_destroy(buf); + if (status != ARES_SUCCESS) { + ares_hosts_file_destroy(hf); + } else { + *out = hf; + } + return status; +} + +static ares_bool_t ares_hosts_expired(const char *filename, + const ares_hosts_file_t *hf) +{ + time_t mod_ts = 0; + +#ifdef HAVE_STAT + struct stat st; + if (stat(filename, &st) == 0) { + mod_ts = st.st_mtime; + } +#elif defined(_WIN32) + struct _stat st; + if (_stat(filename, &st) == 0) { + mod_ts = st.st_mtime; + } +#else + (void)filename; +#endif + + if (hf == NULL) { + return ARES_TRUE; + } + + /* Expire every 60s if we can't get a time */ + if (mod_ts == 0) { + mod_ts = + time(NULL) - 60; /* LCOV_EXCL_LINE: only on systems without stat() */ + } + + /* If filenames are different, its expired */ + if (!ares_strcaseeq(hf->filename, filename)) { + return ARES_TRUE; + } + + if (hf->ts <= mod_ts) { + return ARES_TRUE; + } + + return ARES_FALSE; +} + +static ares_status_t ares_hosts_path(const ares_channel_t *channel, + ares_bool_t use_env, char **path) +{ + char *path_hosts = NULL; + + *path = NULL; + + if (channel->hosts_path) { + path_hosts = ares_strdup(channel->hosts_path); + if (!path_hosts) { + return ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */ + } + } + + if (use_env) { + if (path_hosts) { + ares_free(path_hosts); + } + + path_hosts = ares_strdup(getenv("CARES_HOSTS")); + if (!path_hosts) { + return ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */ + } + } + + if (!path_hosts) { +#if defined(USE_WINSOCK) + char PATH_HOSTS[MAX_PATH] = ""; + char tmp[MAX_PATH]; + HKEY hkeyHosts; + DWORD dwLength = sizeof(tmp); + if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, WIN_NS_NT_KEY, 0, KEY_READ, + &hkeyHosts) != ERROR_SUCCESS) { + return ARES_ENOTFOUND; + } + RegQueryValueExA(hkeyHosts, DATABASEPATH, NULL, NULL, (LPBYTE)tmp, + &dwLength); + ExpandEnvironmentStringsA(tmp, PATH_HOSTS, MAX_PATH); + RegCloseKey(hkeyHosts); + strcat(PATH_HOSTS, WIN_PATH_HOSTS); +#elif defined(WATT32) + const char *PATH_HOSTS = _w32_GetHostsFile(); + + if (!PATH_HOSTS) { + return ARES_ENOTFOUND; + } +#endif + path_hosts = ares_strdup(PATH_HOSTS); + if (!path_hosts) { + return ARES_ENOMEM; + } + } + + *path = path_hosts; + return ARES_SUCCESS; +} + +static ares_status_t ares_hosts_update(ares_channel_t *channel, + ares_bool_t use_env) +{ + ares_status_t status; + char *filename = NULL; + + status = ares_hosts_path(channel, use_env, &filename); + if (status != ARES_SUCCESS) { + return status; + } + + if (!ares_hosts_expired(filename, channel->hf)) { + ares_free(filename); + return ARES_SUCCESS; + } + + ares_hosts_file_destroy(channel->hf); + channel->hf = NULL; + + status = ares_parse_hosts(filename, &channel->hf); + ares_free(filename); + return status; +} + +ares_status_t ares_hosts_search_ipaddr(ares_channel_t *channel, + ares_bool_t use_env, const char *ipaddr, + const ares_hosts_entry_t **entry) +{ + ares_status_t status; + char addr[INET6_ADDRSTRLEN]; + + *entry = NULL; + + status = ares_hosts_update(channel, use_env); + if (status != ARES_SUCCESS) { + return status; + } + + if (channel->hf == NULL) { + return ARES_ENOTFOUND; /* LCOV_EXCL_LINE: DefensiveCoding */ + } + + if (!ares_normalize_ipaddr(ipaddr, addr, sizeof(addr))) { + return ARES_EBADNAME; + } + + *entry = ares_htable_strvp_get_direct(channel->hf->iphash, addr); + if (*entry == NULL) { + return ARES_ENOTFOUND; + } + + return ARES_SUCCESS; +} + +ares_status_t ares_hosts_search_host(ares_channel_t *channel, + ares_bool_t use_env, const char *host, + const ares_hosts_entry_t **entry) +{ + ares_status_t status; + + *entry = NULL; + + status = ares_hosts_update(channel, use_env); + if (status != ARES_SUCCESS) { + return status; + } + + if (channel->hf == NULL) { + return ARES_ENOTFOUND; /* LCOV_EXCL_LINE: DefensiveCoding */ + } + + *entry = ares_htable_strvp_get_direct(channel->hf->hosthash, host); + if (*entry == NULL) { + return ARES_ENOTFOUND; + } + + return ARES_SUCCESS; +} + +static ares_status_t + ares_hosts_ai_append_cnames(const ares_hosts_entry_t *entry, + struct ares_addrinfo_cname **cnames_out) +{ + struct ares_addrinfo_cname *cname = NULL; + struct ares_addrinfo_cname *cnames = NULL; + const char *primaryhost; + ares_llist_node_t *node; + ares_status_t status; + size_t cnt = 0; + + node = ares_llist_node_first(entry->hosts); + primaryhost = ares_llist_node_val(node); + /* Skip to next node to start with aliases */ + node = ares_llist_node_next(node); + + while (node != NULL) { + const char *host = ares_llist_node_val(node); + + /* Cap at 100 entries. , some people use + * https://github.com/StevenBlack/hosts and we don't need 200k+ aliases */ + cnt++; + if (cnt > 100) { + break; /* LCOV_EXCL_LINE: DefensiveCoding */ + } + + cname = ares_append_addrinfo_cname(&cnames); + if (cname == NULL) { + status = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */ + goto done; /* LCOV_EXCL_LINE: OutOfMemory */ + } + + cname->alias = ares_strdup(host); + if (cname->alias == NULL) { + status = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */ + goto done; /* LCOV_EXCL_LINE: OutOfMemory */ + } + + cname->name = ares_strdup(primaryhost); + if (cname->name == NULL) { + status = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */ + goto done; /* LCOV_EXCL_LINE: OutOfMemory */ + } + + node = ares_llist_node_next(node); + } + + /* No entries, add only primary */ + if (cnames == NULL) { + cname = ares_append_addrinfo_cname(&cnames); + if (cname == NULL) { + status = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */ + goto done; /* LCOV_EXCL_LINE: OutOfMemory */ + } + + cname->name = ares_strdup(primaryhost); + if (cname->name == NULL) { + status = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */ + goto done; /* LCOV_EXCL_LINE: OutOfMemory */ + } + } + status = ARES_SUCCESS; + +done: + if (status != ARES_SUCCESS) { + ares_freeaddrinfo_cnames(cnames); /* LCOV_EXCL_LINE: DefensiveCoding */ + return status; /* LCOV_EXCL_LINE: DefensiveCoding */ + } + + *cnames_out = cnames; + return ARES_SUCCESS; +} + +ares_status_t ares_hosts_entry_to_addrinfo(const ares_hosts_entry_t *entry, + const char *name, int family, + unsigned short port, + ares_bool_t want_cnames, + struct ares_addrinfo *ai) +{ + ares_status_t status; + struct ares_addrinfo_cname *cnames = NULL; + struct ares_addrinfo_node *ainodes = NULL; + ares_llist_node_t *node; + + switch (family) { + case AF_INET: + case AF_INET6: + case AF_UNSPEC: + break; + default: /* LCOV_EXCL_LINE: DefensiveCoding */ + return ARES_EBADFAMILY; /* LCOV_EXCL_LINE: DefensiveCoding */ + } + + if (name != NULL) { + ai->name = ares_strdup(name); + if (ai->name == NULL) { + status = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */ + goto done; /* LCOV_EXCL_LINE: OutOfMemory */ + } + } + + for (node = ares_llist_node_first(entry->ips); node != NULL; + node = ares_llist_node_next(node)) { + struct ares_addr addr; + const void *ptr = NULL; + size_t ptr_len = 0; + const char *ipaddr = ares_llist_node_val(node); + + memset(&addr, 0, sizeof(addr)); + addr.family = family; + ptr = ares_dns_pton(ipaddr, &addr, &ptr_len); + + if (ptr == NULL) { + continue; + } + + status = ares_append_ai_node(addr.family, port, 0, ptr, &ainodes); + if (status != ARES_SUCCESS) { + goto done; /* LCOV_EXCL_LINE: DefensiveCoding */ + } + } + + if (want_cnames) { + status = ares_hosts_ai_append_cnames(entry, &cnames); + if (status != ARES_SUCCESS) { + goto done; /* LCOV_EXCL_LINE: DefensiveCoding */ + } + } + + status = ARES_SUCCESS; + +done: + if (status != ARES_SUCCESS) { + /* LCOV_EXCL_START: defensive coding */ + ares_freeaddrinfo_cnames(cnames); + ares_freeaddrinfo_nodes(ainodes); + ares_free(ai->name); + ai->name = NULL; + return status; + /* LCOV_EXCL_STOP */ + } + ares_addrinfo_cat_cnames(&ai->cnames, cnames); + ares_addrinfo_cat_nodes(&ai->nodes, ainodes); + + return status; +} + +ares_status_t ares_hosts_entry_to_hostent(const ares_hosts_entry_t *entry, + int family, struct hostent **hostent) +{ + ares_status_t status; + struct ares_addrinfo *ai = ares_malloc_zero(sizeof(*ai)); + + *hostent = NULL; + + if (ai == NULL) { + return ARES_ENOMEM; + } + + status = ares_hosts_entry_to_addrinfo(entry, NULL, family, 0, ARES_TRUE, ai); + if (status != ARES_SUCCESS) { + goto done; + } + + status = ares_addrinfo2hostent(ai, family, hostent); + if (status != ARES_SUCCESS) { + goto done; + } + +done: + ares_freeaddrinfo(ai); + if (status != ARES_SUCCESS) { + ares_free_hostent(*hostent); + *hostent = NULL; + } + + return status; +} |