%PDF- %PDF-
Direktori : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/cares/src/lib/ |
Current File : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/cares/src/lib/ares_sysconfig.c |
/* MIT License * * Copyright (c) 1998 Massachusetts Institute of Technology * Copyright (c) 2007 Daniel Stenberg * * 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_setup.h" #ifdef HAVE_SYS_PARAM_H # include <sys/param.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 "ares_nameser.h" #if defined(ANDROID) || defined(__ANDROID__) # include <sys/system_properties.h> # include "ares_android.h" /* From the Bionic sources */ # define DNS_PROP_NAME_PREFIX "net.dns" # define MAX_DNS_PROPERTIES 8 #endif #if defined(CARES_USE_LIBRESOLV) # include <resolv.h> #endif #if defined(USE_WINSOCK) # if defined(HAVE_IPHLPAPI_H) # include <iphlpapi.h> # endif # if defined(HAVE_NETIOAPI_H) # include <netioapi.h> # endif #endif #include "ares.h" #include "ares_inet_net_pton.h" #include "ares_platform.h" #include "ares_private.h" #ifdef WATT32 # undef WIN32 /* Redefined in MingW/MSVC headers */ #endif #ifdef WIN32 /* * get_REG_SZ() * * Given a 'hKey' handle to an open registry key and a 'leafKeyName' pointer * to the name of the registry leaf key to be queried, fetch it's string * value and return a pointer in *outptr to a newly allocated memory area * holding it as a null-terminated string. * * Returns 0 and nullifies *outptr upon inability to return a string value. * * Returns 1 and sets *outptr when returning a dynamically allocated string. * * Supported on Windows NT 3.5 and newer. */ static ares_bool_t get_REG_SZ(HKEY hKey, const char *leafKeyName, char **outptr) { DWORD size = 0; int res; *outptr = NULL; /* Find out size of string stored in registry */ res = RegQueryValueExA(hKey, leafKeyName, 0, NULL, NULL, &size); if ((res != ERROR_SUCCESS && res != ERROR_MORE_DATA) || !size) { return ARES_FALSE; } /* Allocate buffer of indicated size plus one given that string might have been stored without null termination */ *outptr = ares_malloc(size + 1); if (!*outptr) { return ARES_FALSE; } /* Get the value for real */ res = RegQueryValueExA(hKey, leafKeyName, 0, NULL, (unsigned char *)*outptr, &size); if ((res != ERROR_SUCCESS) || (size == 1)) { ares_free(*outptr); *outptr = NULL; return ARES_FALSE; } /* Null terminate buffer always */ *(*outptr + size) = '\0'; return ARES_TRUE; } static void commanjoin(char **dst, const char * const src, const size_t len) { char *newbuf; size_t newsize; /* 1 for terminating 0 and 2 for , and terminating 0 */ newsize = len + (*dst ? (ares_strlen(*dst) + 2) : 1); newbuf = ares_realloc(*dst, newsize); if (!newbuf) { return; } if (*dst == NULL) { *newbuf = '\0'; } *dst = newbuf; if (ares_strlen(*dst) != 0) { strcat(*dst, ","); } strncat(*dst, src, len); } /* * commajoin() * * RTF code. */ static void commajoin(char **dst, const char *src) { commanjoin(dst, src, ares_strlen(src)); } /* A structure to hold the string form of IPv4 and IPv6 addresses so we can * sort them by a metric. */ typedef struct { /* The metric we sort them by. */ ULONG metric; /* Original index of the item, used as a secondary sort parameter to make * qsort() stable if the metrics are equal */ size_t orig_idx; /* Room enough for the string form of any IPv4 or IPv6 address that * ares_inet_ntop() will create. Based on the existing c-ares practice. */ char text[INET6_ADDRSTRLEN + 8 + 64]; /* [%s]:NNNNN%iface */ } Address; /* Sort Address values \a left and \a right by metric, returning the usual * indicators for qsort(). */ static int compareAddresses(const void *arg1, const void *arg2) { const Address * const left = arg1; const Address * const right = arg2; /* Lower metric the more preferred */ if (left->metric < right->metric) { return -1; } if (left->metric > right->metric) { return 1; } /* If metrics are equal, lower original index more preferred */ if (left->orig_idx < right->orig_idx) { return -1; } if (left->orig_idx > right->orig_idx) { return 1; } return 0; } /* There can be multiple routes to "the Internet". And there can be different * DNS servers associated with each of the interfaces that offer those routes. * We have to assume that any DNS server can serve any request. But, some DNS * servers may only respond if requested over their associated interface. But * we also want to use "the preferred route to the Internet" whenever possible * (and not use DNS servers on a non-preferred route even by forcing request * to go out on the associated non-preferred interface). i.e. We want to use * the DNS servers associated with the same interface that we would use to * make a general request to anything else. * * But, Windows won't sort the DNS servers by the metrics associated with the * routes and interfaces _even_ though it obviously sends IP packets based on * those same routes and metrics. So, we must do it ourselves. * * So, we sort the DNS servers by the same metric values used to determine how * an outgoing IP packet will go, thus effectively using the DNS servers * associated with the interface that the DNS requests themselves will * travel. This gives us optimal routing and avoids issues where DNS servers * won't respond to requests that don't arrive via some specific subnetwork * (and thus some specific interface). * * This function computes the metric we use to sort. On the interface * identified by \a luid, it determines the best route to \a dest and combines * that route's metric with \a interfaceMetric to compute a metric for the * destination address on that interface. This metric can be used as a weight * to sort the DNS server addresses associated with each interface (lower is * better). * * Note that by restricting the route search to the specific interface with * which the DNS servers are associated, this function asks the question "What * is the metric for sending IP packets to this DNS server?" which allows us * to sort the DNS servers correctly. */ static ULONG getBestRouteMetric(IF_LUID * const luid, /* Can't be const :( */ const SOCKADDR_INET * const dest, const ULONG interfaceMetric) { /* On this interface, get the best route to that destination. */ # if defined(__WATCOMC__) /* OpenWatcom's builtin Windows SDK does not have a definition for * MIB_IPFORWARD_ROW2, and also does not allow the usage of SOCKADDR_INET * as a variable. Let's work around this by returning the worst possible * metric, but only when using the OpenWatcom compiler. * It may be worth investigating using a different version of the Windows * SDK with OpenWatcom in the future, though this may be fixed in OpenWatcom * 2.0. */ return (ULONG)-1; # else MIB_IPFORWARD_ROW2 row; SOCKADDR_INET ignored; if (GetBestRoute2(/* The interface to use. The index is ignored since we are * passing a LUID. */ luid, 0, /* No specific source address. */ NULL, /* Our destination address. */ dest, /* No options. */ 0, /* The route row. */ &row, /* The best source address, which we don't need. */ &ignored) != NO_ERROR /* If the metric is "unused" (-1) or too large for us to add the two * metrics, use the worst possible, thus sorting this last. */ || row.Metric == (ULONG)-1 || row.Metric > ((ULONG)-1) - interfaceMetric) { /* Return the worst possible metric. */ return (ULONG)-1; } /* Return the metric value from that row, plus the interface metric. * * See * http://msdn.microsoft.com/en-us/library/windows/desktop/aa814494(v=vs.85).aspx * which describes the combination as a "sum". */ return row.Metric + interfaceMetric; # endif /* __WATCOMC__ */ } /* * get_DNS_Windows() * * Locates DNS info using GetAdaptersAddresses() function from the Internet * Protocol Helper (IP Helper) API. When located, this returns a pointer * in *outptr to a newly allocated memory area holding a null-terminated * string with a space or comma separated list of DNS IP addresses. * * Returns 0 and nullifies *outptr upon inability to return DNSes string. * * Returns 1 and sets *outptr when returning a dynamically allocated string. * * Implementation supports Windows XP and newer. */ # define IPAA_INITIAL_BUF_SZ 15 * 1024 # define IPAA_MAX_TRIES 3 static ares_bool_t get_DNS_Windows(char **outptr) { IP_ADAPTER_DNS_SERVER_ADDRESS *ipaDNSAddr; IP_ADAPTER_ADDRESSES *ipaa; IP_ADAPTER_ADDRESSES *newipaa; IP_ADAPTER_ADDRESSES *ipaaEntry; ULONG ReqBufsz = IPAA_INITIAL_BUF_SZ; ULONG Bufsz = IPAA_INITIAL_BUF_SZ; ULONG AddrFlags = 0; int trying = IPAA_MAX_TRIES; ULONG res; /* The capacity of addresses, in elements. */ size_t addressesSize; /* The number of elements in addresses. */ size_t addressesIndex = 0; /* The addresses we will sort. */ Address *addresses; union { struct sockaddr *sa; struct sockaddr_in *sa4; struct sockaddr_in6 *sa6; } namesrvr; *outptr = NULL; ipaa = ares_malloc(Bufsz); if (!ipaa) { return ARES_FALSE; } /* Start with enough room for a few DNS server addresses and we'll grow it * as we encounter more. */ addressesSize = 4; addresses = (Address *)ares_malloc(sizeof(Address) * addressesSize); if (addresses == NULL) { /* We need room for at least some addresses to function. */ ares_free(ipaa); return ARES_FALSE; } /* Usually this call succeeds with initial buffer size */ res = GetAdaptersAddresses(AF_UNSPEC, AddrFlags, NULL, ipaa, &ReqBufsz); if ((res != ERROR_BUFFER_OVERFLOW) && (res != ERROR_SUCCESS)) { goto done; } while ((res == ERROR_BUFFER_OVERFLOW) && (--trying)) { if (Bufsz < ReqBufsz) { newipaa = ares_realloc(ipaa, ReqBufsz); if (!newipaa) { goto done; } Bufsz = ReqBufsz; ipaa = newipaa; } res = GetAdaptersAddresses(AF_UNSPEC, AddrFlags, NULL, ipaa, &ReqBufsz); if (res == ERROR_SUCCESS) { break; } } if (res != ERROR_SUCCESS) { goto done; } for (ipaaEntry = ipaa; ipaaEntry; ipaaEntry = ipaaEntry->Next) { if (ipaaEntry->OperStatus != IfOperStatusUp) { continue; } /* For each interface, find any associated DNS servers as IPv4 or IPv6 * addresses. For each found address, find the best route to that DNS * server address _on_ _that_ _interface_ (at this moment in time) and * compute the resulting total metric, just as Windows routing will do. * Then, sort all the addresses found by the metric. */ for (ipaDNSAddr = ipaaEntry->FirstDnsServerAddress; ipaDNSAddr; ipaDNSAddr = ipaDNSAddr->Next) { char ipaddr[INET6_ADDRSTRLEN] = ""; namesrvr.sa = ipaDNSAddr->Address.lpSockaddr; if (namesrvr.sa->sa_family == AF_INET) { if ((namesrvr.sa4->sin_addr.S_un.S_addr == INADDR_ANY) || (namesrvr.sa4->sin_addr.S_un.S_addr == INADDR_NONE)) { continue; } /* Allocate room for another address, if necessary, else skip. */ if (addressesIndex == addressesSize) { const size_t newSize = addressesSize + 4; Address * const newMem = (Address *)ares_realloc(addresses, sizeof(Address) * newSize); if (newMem == NULL) { continue; } addresses = newMem; addressesSize = newSize; } addresses[addressesIndex].metric = getBestRouteMetric( &ipaaEntry->Luid, (SOCKADDR_INET *)((void *)(namesrvr.sa)), ipaaEntry->Ipv4Metric); /* Record insertion index to make qsort stable */ addresses[addressesIndex].orig_idx = addressesIndex; if (!ares_inet_ntop(AF_INET, &namesrvr.sa4->sin_addr, ipaddr, sizeof(ipaddr))) { continue; } snprintf(addresses[addressesIndex].text, sizeof(addresses[addressesIndex].text), "[%s]:%u", ipaddr, ntohs(namesrvr.sa4->sin_port)); ++addressesIndex; } else if (namesrvr.sa->sa_family == AF_INET6) { unsigned int ll_scope = 0; struct ares_addr addr; if (memcmp(&namesrvr.sa6->sin6_addr, &ares_in6addr_any, sizeof(namesrvr.sa6->sin6_addr)) == 0) { continue; } /* Allocate room for another address, if necessary, else skip. */ if (addressesIndex == addressesSize) { const size_t newSize = addressesSize + 4; Address * const newMem = (Address *)ares_realloc(addresses, sizeof(Address) * newSize); if (newMem == NULL) { continue; } addresses = newMem; addressesSize = newSize; } /* See if its link-local */ memset(&addr, 0, sizeof(addr)); addr.family = AF_INET6; memcpy(&addr.addr.addr6, &namesrvr.sa6->sin6_addr, 16); if (ares__addr_is_linklocal(&addr)) { ll_scope = ipaaEntry->Ipv6IfIndex; } addresses[addressesIndex].metric = getBestRouteMetric( &ipaaEntry->Luid, (SOCKADDR_INET *)((void *)(namesrvr.sa)), ipaaEntry->Ipv6Metric); /* Record insertion index to make qsort stable */ addresses[addressesIndex].orig_idx = addressesIndex; if (!ares_inet_ntop(AF_INET6, &namesrvr.sa6->sin6_addr, ipaddr, sizeof(ipaddr))) { continue; } if (ll_scope) { snprintf(addresses[addressesIndex].text, sizeof(addresses[addressesIndex].text), "[%s]:%u%%%u", ipaddr, ntohs(namesrvr.sa6->sin6_port), ll_scope); } else { snprintf(addresses[addressesIndex].text, sizeof(addresses[addressesIndex].text), "[%s]:%u", ipaddr, ntohs(namesrvr.sa6->sin6_port)); } ++addressesIndex; } else { /* Skip non-IPv4/IPv6 addresses completely. */ continue; } } } /* Sort all of the textual addresses by their metric (and original index if * metrics are equal). */ qsort(addresses, addressesIndex, sizeof(*addresses), compareAddresses); /* Join them all into a single string, removing duplicates. */ { size_t i; for (i = 0; i < addressesIndex; ++i) { size_t j; /* Look for this address text appearing previously in the results. */ for (j = 0; j < i; ++j) { if (strcmp(addresses[j].text, addresses[i].text) == 0) { break; } } /* Iff we didn't emit this address already, emit it now. */ if (j == i) { /* Add that to outptr (if we can). */ commajoin(outptr, addresses[i].text); } } } done: ares_free(addresses); if (ipaa) { ares_free(ipaa); } if (!*outptr) { return ARES_FALSE; } return ARES_TRUE; } /* * get_SuffixList_Windows() * * Reads the "DNS Suffix Search List" from registry and writes the list items * whitespace separated to outptr. If the Search List is empty, the * "Primary Dns Suffix" is written to outptr. * * Returns 0 and nullifies *outptr upon inability to return the suffix list. * * Returns 1 and sets *outptr when returning a dynamically allocated string. * * Implementation supports Windows Server 2003 and newer */ static ares_bool_t get_SuffixList_Windows(char **outptr) { HKEY hKey; HKEY hKeyEnum; char keyName[256]; DWORD keyNameBuffSize; DWORD keyIdx = 0; char *p = NULL; *outptr = NULL; if (ares__getplatform() != WIN_NT) { return ARES_FALSE; } /* 1. Global DNS Suffix Search List */ if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, WIN_NS_NT_KEY, 0, KEY_READ, &hKey) == ERROR_SUCCESS) { get_REG_SZ(hKey, SEARCHLIST_KEY, outptr); if (get_REG_SZ(hKey, DOMAIN_KEY, &p)) { commajoin(outptr, p); ares_free(p); p = NULL; } RegCloseKey(hKey); } if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, WIN_NT_DNSCLIENT, 0, KEY_READ, &hKey) == ERROR_SUCCESS) { if (get_REG_SZ(hKey, SEARCHLIST_KEY, &p)) { commajoin(outptr, p); ares_free(p); p = NULL; } RegCloseKey(hKey); } /* 2. Connection Specific Search List composed of: * a. Primary DNS Suffix */ if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, WIN_DNSCLIENT, 0, KEY_READ, &hKey) == ERROR_SUCCESS) { if (get_REG_SZ(hKey, PRIMARYDNSSUFFIX_KEY, &p)) { commajoin(outptr, p); ares_free(p); p = NULL; } RegCloseKey(hKey); } /* b. Interface SearchList, Domain, DhcpDomain */ if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, WIN_NS_NT_KEY "\\" INTERFACES_KEY, 0, KEY_READ, &hKey) == ERROR_SUCCESS) { for (;;) { keyNameBuffSize = sizeof(keyName); if (RegEnumKeyExA(hKey, keyIdx++, keyName, &keyNameBuffSize, 0, NULL, NULL, NULL) != ERROR_SUCCESS) { break; } if (RegOpenKeyExA(hKey, keyName, 0, KEY_QUERY_VALUE, &hKeyEnum) != ERROR_SUCCESS) { continue; } /* p can be comma separated (SearchList) */ if (get_REG_SZ(hKeyEnum, SEARCHLIST_KEY, &p)) { commajoin(outptr, p); ares_free(p); p = NULL; } if (get_REG_SZ(hKeyEnum, DOMAIN_KEY, &p)) { commajoin(outptr, p); ares_free(p); p = NULL; } if (get_REG_SZ(hKeyEnum, DHCPDOMAIN_KEY, &p)) { commajoin(outptr, p); ares_free(p); p = NULL; } RegCloseKey(hKeyEnum); } RegCloseKey(hKey); } return *outptr != NULL ? ARES_TRUE : ARES_FALSE; } static ares_status_t ares__init_sysconfig_windows(ares_sysconfig_t *sysconfig) { char *line = NULL; ares_status_t status = ARES_SUCCESS; if (get_DNS_Windows(&line)) { status = ares__sconfig_append_fromstr(&sysconfig->sconfig, line, ARES_TRUE); ares_free(line); if (status != ARES_SUCCESS) { goto done; } } if (get_SuffixList_Windows(&line)) { sysconfig->domains = ares__strsplit(line, ", ", &sysconfig->ndomains); ares_free(line); if (sysconfig->domains == NULL) { status = ARES_EFILE; } if (status != ARES_SUCCESS) { goto done; } } done: return status; } #endif #if defined(__MVS__) static ares_status_t ares__init_sysconfig_mvs(ares_sysconfig_t *sysconfig) { struct __res_state *res = 0; size_t count4; size_t count6; int i; __STATEEXTIPV6 *v6; arse__llist_t *sconfig = NULL; ares_status_t status; if (0 == res) { int rc = res_init(); while (rc == -1 && h_errno == TRY_AGAIN) { rc = res_init(); } if (rc == -1) { return ARES_ENOMEM; } res = __res(); } v6 = res->__res_extIPv6; if (res->nscount > 0) { count4 = (size_t)res->nscount; } if (v6 && v6->__stat_nscount > 0) { count6 = (size_t)v6->__stat_nscount; } else { count6 = 0; } for (i = 0; i < count4; i++) { struct sockaddr_in *addr_in = &(res->nsaddr_list[i]); struct ares_addr addr; addr.addr.addr4.s_addr = addr_in->sin_addr.s_addr; addr.family = AF_INET; status = ares__sconfig_append(&sysconfig->sconfig, &addr, htons(addr_in->sin_port), htons(addr_in->sin_port), NULL); if (status != ARES_SUCCESS) { return status; } } for (i = 0; i < count6; i++) { struct sockaddr_in6 *addr_in = &(v6->__stat_nsaddr_list[i]); struct ares_addr addr; addr.family = AF_INET6; memcpy(&(addr.addr.addr6), &(addr_in->sin6_addr), sizeof(addr_in->sin6_addr)); status = ares__sconfig_append(&sysconfig->sconfig, &addr, htons(addr_in->sin_port), htons(addr_in->sin_port), NULL); if (status != ARES_SUCCESS) { return status; } } return ARES_SUCCESS; } #endif #if defined(__riscos__) static ares_status_t ares__init_sysconfig_riscos(ares_sysconfig_t *sysconfig) { char *line; ares_status_t status = ARES_SUCCESS; /* Under RISC OS, name servers are listed in the system variable Inet$Resolvers, space separated. */ line = getenv("Inet$Resolvers"); if (line) { char *resolvers = ares_strdup(line); char *pos; char *space; if (!resolvers) { return ARES_ENOMEM; } pos = resolvers; do { space = strchr(pos, ' '); if (space) { *space = '\0'; } status = ares__sconfig_append_fromstr(&sysconfig->sconfig, pos, ARES_TRUE); if (status != ARES_SUCCESS) { break; } pos = space + 1; } while (space); ares_free(resolvers); } return status; } #endif #if defined(WATT32) static ares_status_t ares__init_sysconfig_watt32(ares_sysconfig_t *sysconfig) { size_t i; ares_status_t status; sock_init(); for (i = 0; def_nameservers[i]; i++) { struct ares_addr addr; addr.family = AF_INET; addr.addr.addr4.s_addr = htonl(def_nameservers[i]); status = ares__sconfig_append(&sysconfig->sconfig, &addr, 0, 0, NULL); if (status != ARES_SUCCESS) { return status; } } return ARES_SUCCESS; } #endif #if defined(ANDROID) || defined(__ANDROID__) static ares_status_t ares__init_sysconfig_android(ares_sysconfig_t *sysconfig) { size_t i; char **dns_servers; char *domains; size_t num_servers; ares_status_t status = ARES_EFILE; /* Use the Android connectivity manager to get a list * of DNS servers. As of Android 8 (Oreo) net.dns# * system properties are no longer available. Google claims this * improves privacy. Apps now need the ACCESS_NETWORK_STATE * permission and must use the ConnectivityManager which * is Java only. */ dns_servers = ares_get_android_server_list(MAX_DNS_PROPERTIES, &num_servers); if (dns_servers != NULL) { for (i = 0; i < num_servers; i++) { status = ares__sconfig_append_fromstr(&sysconfig->sconfig, dns_servers[i], ARES_TRUE); if (status != ARES_SUCCESS) { return status; } } for (i = 0; i < num_servers; i++) { ares_free(dns_servers[i]); } ares_free(dns_servers); } domains = ares_get_android_search_domains_list(); sysconfig->domains = ares__strsplit(domains, ", ", &sysconfig->ndomains); ares_free(domains); # ifdef HAVE___SYSTEM_PROPERTY_GET /* Old way using the system property still in place as * a fallback. Older android versions can still use this. * it's possible for older apps not not have added the new * permission and we want to try to avoid breaking those. * * We'll only run this if we don't have any dns servers * because this will get the same ones (if it works). */ if (sysconfig->sconfig == NULL) { char propname[PROP_NAME_MAX]; char propvalue[PROP_VALUE_MAX] = ""; for (i = 1; i <= MAX_DNS_PROPERTIES; i++) { snprintf(propname, sizeof(propname), "%s%u", DNS_PROP_NAME_PREFIX, i); if (__system_property_get(propname, propvalue) < 1) { break; } status = ares__sconfig_append_fromstr(&sysconfig->sconfig, propvalue, ARES_TRUE); if (status != ARES_SUCCESS) { return status; } } } # endif /* HAVE___SYSTEM_PROPERTY_GET */ return status; } #endif #if defined(CARES_USE_LIBRESOLV) static ares_status_t ares__init_sysconfig_libresolv(ares_sysconfig_t *sysconfig) { struct __res_state res; ares_status_t status = ARES_SUCCESS; union res_sockaddr_union addr[MAXNS]; int nscount; size_t i; size_t entries = 0; ares__buf_t *ipbuf = NULL; memset(&res, 0, sizeof(res)); if (res_ninit(&res) != 0 || !(res.options & RES_INIT)) { return ARES_EFILE; } nscount = res_getservers(&res, addr, MAXNS); for (i = 0; i < (size_t)nscount; ++i) { char ipaddr[INET6_ADDRSTRLEN] = ""; char *ipstr = NULL; unsigned short port = 0; unsigned int ll_scope = 0; sa_family_t family = addr[i].sin.sin_family; if (family == AF_INET) { ares_inet_ntop(family, &addr[i].sin.sin_addr, ipaddr, sizeof(ipaddr)); port = ntohs(addr[i].sin.sin_port); } else if (family == AF_INET6) { ares_inet_ntop(family, &addr[i].sin6.sin6_addr, ipaddr, sizeof(ipaddr)); port = ntohs(addr[i].sin6.sin6_port); ll_scope = addr[i].sin6.sin6_scope_id; } else { continue; } /* [ip]:port%iface */ ipbuf = ares__buf_create(); if (ipbuf == NULL) { status = ARES_ENOMEM; goto done; } status = ares__buf_append_str(ipbuf, "["); if (status != ARES_SUCCESS) { goto done; } status = ares__buf_append_str(ipbuf, ipaddr); if (status != ARES_SUCCESS) { goto done; } status = ares__buf_append_str(ipbuf, "]"); if (status != ARES_SUCCESS) { goto done; } if (port) { status = ares__buf_append_str(ipbuf, ":"); if (status != ARES_SUCCESS) { goto done; } status = ares__buf_append_num_dec(ipbuf, port, 0); if (status != ARES_SUCCESS) { goto done; } } if (ll_scope) { status = ares__buf_append_str(ipbuf, "%"); if (status != ARES_SUCCESS) { goto done; } status = ares__buf_append_num_dec(ipbuf, ll_scope, 0); if (status != ARES_SUCCESS) { goto done; } } ipstr = ares__buf_finish_str(ipbuf, NULL); ipbuf = NULL; if (ipstr == NULL) { status = ARES_ENOMEM; goto done; } status = ares__sconfig_append_fromstr(&sysconfig->sconfig, ipstr, ARES_TRUE); ares_free(ipstr); if (status != ARES_SUCCESS) { goto done; } } while ((entries < MAXDNSRCH) && res.dnsrch[entries]) { entries++; } if (entries) { sysconfig->domains = ares_malloc_zero(entries * sizeof(char *)); if (sysconfig->domains == NULL) { status = ARES_ENOMEM; goto done; } else { sysconfig->ndomains = entries; for (i = 0; i < sysconfig->ndomains; i++) { sysconfig->domains[i] = ares_strdup(res.dnsrch[i]); if (sysconfig->domains[i] == NULL) { status = ARES_ENOMEM; goto done; } } } } if (res.ndots > 0) { sysconfig->ndots = (size_t)res.ndots; } if (res.retry > 0) { sysconfig->tries = (size_t)res.retry; } if (res.options & RES_ROTATE) { sysconfig->rotate = ARES_TRUE; } if (res.retrans > 0) { if (res.retrans > 0) { sysconfig->timeout_ms = (unsigned int)res.retrans * 1000; } # ifdef __APPLE__ if (res.retry >= 0) { sysconfig->timeout_ms /= ((unsigned int)res.retry + 1) * (unsigned int)(res.nscount > 0 ? res.nscount : 1); } # endif } done: ares__buf_destroy(ipbuf); res_ndestroy(&res); return status; } #endif static void ares_sysconfig_free(ares_sysconfig_t *sysconfig) { ares__llist_destroy(sysconfig->sconfig); ares__strsplit_free(sysconfig->domains, sysconfig->ndomains); ares_free(sysconfig->sortlist); ares_free(sysconfig->lookups); memset(sysconfig, 0, sizeof(*sysconfig)); } static ares_status_t ares_sysconfig_apply(ares_channel_t *channel, const ares_sysconfig_t *sysconfig) { ares_status_t status; if (sysconfig->sconfig && !(channel->optmask & ARES_OPT_SERVERS)) { status = ares__servers_update(channel, sysconfig->sconfig, ARES_FALSE); if (status != ARES_SUCCESS) { return status; } } if (sysconfig->domains && !(channel->optmask & ARES_OPT_DOMAINS)) { /* Make sure we duplicate first then replace so even if there is * ARES_ENOMEM, the channel stays in a good state */ char **temp = ares__strsplit_duplicate(sysconfig->domains, sysconfig->ndomains); if (temp == NULL) { return ARES_ENOMEM; } ares__strsplit_free(channel->domains, channel->ndomains); channel->domains = temp; channel->ndomains = sysconfig->ndomains; } if (sysconfig->lookups && !(channel->optmask & ARES_OPT_LOOKUPS)) { char *temp = ares_strdup(sysconfig->lookups); if (temp == NULL) { return ARES_ENOMEM; } ares_free(channel->lookups); channel->lookups = temp; } if (sysconfig->sortlist && !(channel->optmask & ARES_OPT_SORTLIST)) { struct apattern *temp = ares_malloc(sizeof(*channel->sortlist) * sysconfig->nsortlist); if (temp == NULL) { return ARES_ENOMEM; } memcpy(temp, sysconfig->sortlist, sizeof(*channel->sortlist) * sysconfig->nsortlist); ares_free(channel->sortlist); channel->sortlist = temp; channel->nsort = sysconfig->nsortlist; } if (sysconfig->ndots && !(channel->optmask & ARES_OPT_NDOTS)) { channel->ndots = sysconfig->ndots; } if (sysconfig->tries && !(channel->optmask & ARES_OPT_TRIES)) { channel->tries = sysconfig->tries; } if (sysconfig->timeout_ms && !(channel->optmask & ARES_OPT_TIMEOUTMS)) { channel->timeout = sysconfig->timeout_ms; } if (!(channel->optmask & (ARES_OPT_ROTATE | ARES_OPT_NOROTATE))) { channel->rotate = sysconfig->rotate; } return ARES_SUCCESS; } ares_status_t ares__init_by_sysconfig(ares_channel_t *channel) { ares_status_t status; ares_sysconfig_t sysconfig; memset(&sysconfig, 0, sizeof(sysconfig)); #ifdef _WIN32 status = ares__init_sysconfig_windows(&sysconfig); #elif defined(__MVS__) status = ares__init_sysconfig_mvs(&sysconfig); #elif defined(__riscos__) status = ares__init_sysconfig_riscos(&sysconfig); #elif defined(WATT32) status = ares__init_sysconfig_watt32(&sysconfig); #elif defined(ANDROID) || defined(__ANDROID__) status = ares__init_sysconfig_android(&sysconfig); #elif defined(CARES_USE_LIBRESOLV) status = ares__init_sysconfig_libresolv(&sysconfig); #else status = ares__init_sysconfig_files(channel, &sysconfig); #endif if (status != ARES_SUCCESS) { goto done; } /* Environment is supposed to override sysconfig */ status = ares__init_by_environment(&sysconfig); if (status != ARES_SUCCESS) { goto done; } status = ares_sysconfig_apply(channel, &sysconfig); if (status != ARES_SUCCESS) { goto done; } done: ares_sysconfig_free(&sysconfig); return status; }