Alien-cares
view release on metacpan or search on metacpan
libcares/ares_init.c view on Meta::CPAN
#endif
}
/* 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[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;
/* 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. */
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()
*
* 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 seperated 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 int get_DNS_AdaptersAddresses(char **outptr)
{
IP_ADAPTER_DNS_SERVER_ADDRESS *ipaDNSAddr;
IP_ADAPTER_ADDRESSES *ipaa, *newipaa, *ipaaEntry;
ULONG ReqBufsz = IPAA_INITIAL_BUF_SZ;
ULONG Bufsz = IPAA_INITIAL_BUF_SZ;
ULONG AddrFlags = 0;
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;
*outptr = NULL;
/* Verify run-time availability of GetAdaptersAddresses() */
if (ares_fpGetAdaptersAddresses == ZERO_NULL)
return 0;
ipaa = ares_malloc(Bufsz);
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);
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 = (*ares_fpGetAdaptersAddresses) (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)
{
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;
}
/* 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 = (ULONG)-1;
}
/* Record insertion index to make qsort stable */
addresses[addressesIndex].orig_idx = addressesIndex;
if (! ares_inet_ntop(AF_INET, &namesrvr.sa4->sin_addr,
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) {
libcares/ares_init.c view on Meta::CPAN
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 0;
}
return 1;
}
/*
* get_DNS_Windows()
*
* Locates DNS info from Windows employing most suitable methods available at
* run-time no matter which Windows version it is. When located, this returns
* a pointer in *outptr to a newly allocated memory area holding a string with
* a space or comma seperated list of DNS IP addresses, null-terminated.
*
* 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 95 and newer.
*/
static int get_DNS_Windows(char **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 GetNetworkParams(). IPv4 only. */
if (get_DNS_NetworkParams(outptr))
return 1;
/* Fall-back to registry information */
return get_DNS_Registry(outptr);
}
/*
* 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;
*outptr = NULL;
if (ares__getplatform() != WIN_NT)
return 0;
/* 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;
}
( run in 0.532 second using v1.01-cache-2.11-cpan-e1769b4cff6 )