summaryrefslogtreecommitdiff
path: root/deps/cares/src/ares_init.c
diff options
context:
space:
mode:
Diffstat (limited to 'deps/cares/src/ares_init.c')
-rw-r--r--deps/cares/src/ares_init.c446
1 files changed, 407 insertions, 39 deletions
diff --git a/deps/cares/src/ares_init.c b/deps/cares/src/ares_init.c
index f557947aa1..dbc67ed441 100644
--- a/deps/cares/src/ares_init.c
+++ b/deps/cares/src/ares_init.c
@@ -84,7 +84,7 @@ static int config_sortlist(struct apattern **sortlist, int *nsort,
const char *str);
static int sortlist_alloc(struct apattern **sortlist, int *nsort,
struct apattern *pat);
-static int ip_addr(const char *s, ssize_t len, struct in_addr *addr);
+static int ip_addr(const char *s, ares_ssize_t len, struct in_addr *addr);
static void natural_mask(struct apattern *pat);
#if !defined(WIN32) && !defined(WATT32) && \
!defined(ANDROID) && !defined(__ANDROID__) && !defined(CARES_USE_LIBRESOLV)
@@ -166,6 +166,8 @@ int ares_init_options(ares_channel *channelptr, struct ares_options *options,
channel->sock_create_cb_data = NULL;
channel->sock_config_cb = NULL;
channel->sock_config_cb_data = NULL;
+ channel->sock_funcs = NULL;
+ channel->sock_func_cb_data = NULL;
channel->last_server = 0;
channel->last_timeout_processed = (time_t)now.tv_sec;
@@ -292,6 +294,8 @@ int ares_dup(ares_channel *dest, ares_channel src)
(*dest)->sock_create_cb_data = src->sock_create_cb_data;
(*dest)->sock_config_cb = src->sock_config_cb;
(*dest)->sock_config_cb_data = src->sock_config_cb_data;
+ (*dest)->sock_funcs = src->sock_funcs;
+ (*dest)->sock_func_cb_data = src->sock_func_cb_data;
strncpy((*dest)->local_dev_name, src->local_dev_name,
sizeof(src->local_dev_name));
@@ -828,6 +832,24 @@ static int get_DNS_Registry(char **outptr)
return 1;
}
+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 ? (strlen(*dst) + 2) : 1);
+ newbuf = ares_realloc(*dst, newsize);
+ if (!newbuf)
+ return;
+ if (*dst == NULL)
+ *newbuf = '\0';
+ *dst = newbuf;
+ if (strlen(*dst) != 0)
+ strcat(*dst, ",");
+ strncat(*dst, src, len);
+}
+
/*
* commajoin()
*
@@ -835,24 +857,7 @@ static int get_DNS_Registry(char **outptr)
*/
static void commajoin(char **dst, const char *src)
{
- char *tmp;
-
- if (*dst)
- {
- tmp = ares_malloc(strlen(*dst) + strlen(src) + 2);
- if (!tmp)
- return;
- sprintf(tmp, "%s,%s", *dst, src);
- ares_free(*dst);
- *dst = tmp;
- }
- else
- {
- *dst = ares_malloc(strlen(src) + 1);
- if (!*dst)
- return;
- strcpy(*dst, src);
- }
+ commanjoin(dst, src, strlen(src));
}
/*
@@ -939,6 +944,116 @@ done:
return 1;
}
+static BOOL ares_IsWindowsVistaOrGreater(void)
+{
+ OSVERSIONINFO vinfo;
+ memset(&vinfo, 0, sizeof(vinfo));
+ vinfo.dwOSVersionInfoSize = sizeof(vinfo);
+ if (!GetVersionEx(&vinfo) || vinfo.dwMajorVersion < 6)
+ return FALSE;
+ return TRUE;
+}
+
+/* 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;
+
+ /* 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[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
+} 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;
+ if(left->metric < right->metric) return -1;
+ if(left->metric > right->metric) 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. */
+ MIB_IPFORWARD_ROW2 row;
+ SOCKADDR_INET ignored;
+ if(!ares_fpGetBestRoute2 ||
+ ares_fpGetBestRoute2(/* 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;
+}
+
/*
* get_DNS_AdaptersAddresses()
*
@@ -965,14 +1080,19 @@ static int get_DNS_AdaptersAddresses(char **outptr)
int trying = IPAA_MAX_TRIES;
int 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;
- char txtaddr[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
-
*outptr = NULL;
/* Verify run-time availability of GetAdaptersAddresses() */
@@ -983,6 +1103,17 @@ static int get_DNS_AdaptersAddresses(char **outptr)
if (!ipaa)
return 0;
+ /* 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 0;
+ }
+
/* Usually this call suceeds with initial buffer size */
res = (*ares_fpGetAdaptersAddresses) (AF_UNSPEC, AddrFlags, NULL,
ipaa, &ReqBufsz);
@@ -1012,6 +1143,12 @@ static int get_DNS_AdaptersAddresses(char **outptr)
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)
@@ -1023,35 +1160,117 @@ static int get_DNS_AdaptersAddresses(char **outptr)
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;
+ }
+
+ /* Vista required for Luid or Ipv4Metric */
+ if (ares_IsWindowsVistaOrGreater())
+ {
+ /* Save the address as the next element in addresses. */
+ addresses[addressesIndex].metric =
+ getBestRouteMetric(&ipaaEntry->Luid,
+ (SOCKADDR_INET*)(namesrvr.sa),
+ ipaaEntry->Ipv4Metric);
+ }
+ else
+ {
+ addresses[addressesIndex].metric = -1;
+ }
+
if (! ares_inet_ntop(AF_INET, &namesrvr.sa4->sin_addr,
- txtaddr, sizeof(txtaddr)))
+ addresses[addressesIndex].text,
+ sizeof(addresses[0].text))) {
continue;
+ }
+ ++addressesIndex;
}
else if (namesrvr.sa->sa_family == AF_INET6)
{
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;
+ }
+
+ /* Vista required for Luid or Ipv4Metric */
+ if (ares_IsWindowsVistaOrGreater())
+ {
+ /* Save the address as the next element in addresses. */
+ addresses[addressesIndex].metric =
+ getBestRouteMetric(&ipaaEntry->Luid,
+ (SOCKADDR_INET*)(namesrvr.sa),
+ ipaaEntry->Ipv6Metric);
+ }
+ else
+ {
+ addresses[addressesIndex].metric = -1;
+ }
+
if (! ares_inet_ntop(AF_INET6, &namesrvr.sa6->sin6_addr,
- txtaddr, sizeof(txtaddr)))
+ addresses[addressesIndex].text,
+ sizeof(addresses[0].text))) {
continue;
+ }
+ ++addressesIndex;
}
- else
+ else {
+ /* Skip non-IPv4/IPv6 addresses completely. */
continue;
+ }
+ }
+ }
- commajoin(outptr, txtaddr);
+ /* Sort all of the textual addresses by their metric. */
+ qsort(addresses, addressesIndex, sizeof(*addresses), compareAddresses);
- if (!*outptr)
- goto done;
+ /* 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)
+ if (!*outptr) {
return 0;
+ }
return 1;
}
@@ -1072,23 +1291,158 @@ done:
*/
static int get_DNS_Windows(char **outptr)
{
- /*
- Use GetNetworkParams First in case of
- multiple adapter is enabled on this machine.
- GetAdaptersAddresses will retrive dummy dns servers.
- That will slowing DNS lookup.
- */
- /* Try using IP helper API GetNetworkParams() */
- if (get_DNS_NetworkParams(outptr))
+ /* Try using IP helper API GetAdaptersAddresses(). IPv4 + IPv6, also sorts
+ * DNS servers by interface route metrics to try to use the best DNS server. */
+ if (get_DNS_AdaptersAddresses(outptr))
return 1;
- /* Try using IP helper API GetAdaptersAddresses() */
- if (get_DNS_AdaptersAddresses(outptr))
+ /* Try using IP helper API GetNetworkParams(). IPv4 only. */
+ if (get_DNS_NetworkParams(outptr))
return 1;
/* Fall-back to registry information */
return get_DNS_Registry(outptr);
}
+
+static void replace_comma_by_space(char* str)
+{
+ /* replace ',' by ' ' to coincide with resolv.conf search parameter */
+ char *p;
+ for (p = str; *p != '\0'; p++)
+ {
+ if (*p == ',')
+ *p = ' ';
+ }
+}
+
+/* Search if 'suffix' is containted in the 'searchlist'. Returns true if yes,
+ * otherwise false. 'searchlist' is a comma separated list of domain suffixes,
+ * 'suffix' is one domain suffix, 'len' is the length of 'suffix'.
+ * The search ignores case. E.g.:
+ * contains_suffix("abc.def,ghi.jkl", "ghi.JKL") returns true */
+static bool contains_suffix(const char* const searchlist,
+ const char* const suffix, const size_t len)
+{
+ const char* beg = searchlist;
+ const char* end;
+ if (!*suffix)
+ return true;
+ for (;;)
+ {
+ while (*beg && (ISSPACE(*beg) || (*beg == ',')))
+ ++beg;
+ if (!*beg)
+ return false;
+ end = beg;
+ while (*end && !ISSPACE(*end) && (*end != ','))
+ ++end;
+ if (len == (end - beg) && !strnicmp(beg, suffix, len))
+ return true;
+ beg = end;
+ }
+}
+
+/* advances list to the next suffix within a comma separated search list.
+ * len is the length of the next suffix. */
+static size_t next_suffix(const char** list, const size_t advance)
+{
+ const char* beg = *list + advance;
+ const char* end;
+ while (*beg && (ISSPACE(*beg) || (*beg == ',')))
+ ++beg;
+ end = beg;
+ while (*end && !ISSPACE(*end) && (*end != ','))
+ ++end;
+ *list = beg;
+ return end - beg;
+}
+
+/*
+ * 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 int get_SuffixList_Windows(char **outptr)
+{
+ HKEY hKey, hKeyEnum;
+ char keyName[256];
+ DWORD keyNameBuffSize;
+ DWORD keyIdx = 0;
+ char *p = NULL;
+ char *pp;
+ size_t len = 0;
+
+ *outptr = NULL;
+
+ if (ares__getplatform() != WIN_NT)
+ return 0;
+
+ /* 1. Global DNS Suffix Search List */
+ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_NS_NT_KEY, 0,
+ KEY_READ, &hKey) == ERROR_SUCCESS)
+ {
+ if (get_REG_SZ(hKey, SEARCHLIST_KEY, outptr))
+ replace_comma_by_space(*outptr);
+ RegCloseKey(hKey);
+ if (*outptr)
+ return 1;
+ }
+
+ /* 2. Connection Specific Search List composed of:
+ * a. Primary DNS Suffix */
+ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_DNSCLIENT, 0,
+ KEY_READ, &hKey) == ERROR_SUCCESS)
+ {
+ get_REG_SZ(hKey, PRIMARYDNSSUFFIX_KEY, outptr);
+ RegCloseKey(hKey);
+ }
+ if (!*outptr)
+ return 0;
+
+ /* b. Interface SearchList, Domain, DhcpDomain */
+ if (!RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_NS_NT_KEY "\\" INTERFACES_KEY, 0,
+ KEY_READ, &hKey) == ERROR_SUCCESS)
+ return 0;
+ for(;;)
+ {
+ keyNameBuffSize = sizeof(keyName);
+ if (RegEnumKeyEx(hKey, keyIdx++, keyName, &keyNameBuffSize,
+ 0, NULL, NULL, NULL)
+ != ERROR_SUCCESS)
+ break;
+ if (RegOpenKeyEx(hKey, keyName, 0, KEY_QUERY_VALUE, &hKeyEnum)
+ != ERROR_SUCCESS)
+ continue;
+ if (get_REG_SZ(hKeyEnum, SEARCHLIST_KEY, &p) ||
+ get_REG_SZ(hKeyEnum, DOMAIN_KEY, &p) ||
+ get_REG_SZ(hKeyEnum, DHCPDOMAIN_KEY, &p))
+ {
+ /* p can be comma separated (SearchList) */
+ pp = p;
+ while (len = next_suffix(&pp, len))
+ {
+ if (!contains_suffix(*outptr, pp, len))
+ commanjoin(outptr, pp, len);
+ }
+ ares_free(p);
+ p = NULL;
+ }
+ RegCloseKey(hKeyEnum);
+ }
+ RegCloseKey(hKey);
+ if (*outptr)
+ replace_comma_by_space(*outptr);
+ return *outptr != NULL;
+}
+
#endif
static int init_by_resolv_conf(ares_channel channel)
@@ -1112,6 +1466,12 @@ static int init_by_resolv_conf(ares_channel channel)
ares_free(line);
}
+ if (channel->ndomains == -1 && get_SuffixList_Windows(&line))
+ {
+ status = set_search(channel, line);
+ ares_free(line);
+ }
+
if (status == ARES_SUCCESS)
status = ARES_EOF;
else
@@ -1950,7 +2310,7 @@ static char *try_config(char *s, const char *opt, char scc)
}
#endif /* !WIN32 & !WATT32 & !ANDROID & !__ANDROID__ */
-static int ip_addr(const char *ipbuf, ssize_t len, struct in_addr *addr)
+static int ip_addr(const char *ipbuf, ares_ssize_t len, struct in_addr *addr)
{
/* Four octets and three periods yields at most 15 characters. */
@@ -2103,6 +2463,14 @@ void ares_set_socket_configure_callback(ares_channel channel,
channel->sock_config_cb_data = data;
}
+void ares_set_socket_functions(ares_channel channel,
+ const struct ares_socket_functions * funcs,
+ void *data)
+{
+ channel->sock_funcs = funcs;
+ channel->sock_func_cb_data = data;
+}
+
int ares_set_sortlist(ares_channel channel, const char *sortstr)
{
int nsort = 0;