Skip to content

Commit

Permalink
IPv6-on-windows: find DNS servers correctly
Browse files Browse the repository at this point in the history
  • Loading branch information
David Stuart authored and bagder committed May 17, 2011
1 parent d60f07d commit 6518b56
Show file tree
Hide file tree
Showing 6 changed files with 197 additions and 62 deletions.
239 changes: 181 additions & 58 deletions ares_init.c
Expand Up @@ -69,6 +69,7 @@
#include "ares_nowarn.h"
#include "ares_platform.h"
#include "ares_private.h"
#include "inet_ntop.h"

#ifdef ANDROID
#include <sys/system_properties.h>
Expand Down Expand Up @@ -595,73 +596,195 @@ static int get_res_interfaces_nt(HKEY hKey, const char *subkey, char **obuf)
return 0;
}

/**
* The desired output for this method is that we set "ret_buf" to
* something like:
*
* 192.168.0.1,dns01.my.domain,fe80::200:f8ff:fe21:67cf
*
* The only ordering requirement is that primary servers are listed
* before secondary. There is no requirement that IPv4 addresses should
* necessarily be before IPv6.
*
* Note that ret_size should ideally be big enough to hold around
* 2-3 IPv4 and 2-3 IPv6 addresses.
*
* Finally, we need to return the total number of DNS servers located.
*/
static int get_iphlpapi_dns_info (char *ret_buf, size_t ret_size)
{
FIXED_INFO *fi, *newfi;
DWORD size = sizeof (*fi);
IP_ADDR_STRING *ipAddr;
int i, count = 0;
int debug = 0;
size_t ip_size = sizeof("255.255.255.255,")-1;
size_t left = ret_size;
char *ret = ret_buf;
HRESULT res;

fi = malloc(size);
if (!fi)
return 0;

res = (*ares_fpGetNetworkParams) (fi, &size);
if ((res != ERROR_BUFFER_OVERFLOW) && (res != ERROR_SUCCESS))
goto quit;

newfi = realloc(fi, size);
if (!newfi)
goto quit;

fi = newfi;
res = (*ares_fpGetNetworkParams) (fi, &size);
if (res != ERROR_SUCCESS)
goto quit;

if (debug)
const size_t ipv4_size = INET_ADDRSTRLEN + 1; /* +1 for ',' at end */
const size_t ipv6_size = INET6_ADDRSTRLEN + 12; /* +12 for "%0123456789," at end */
size_t left = ret_size;
char *ret = ret_buf;
int count = 0;

/* Use the GetAdaptersAddresses method if it's available, otherwise
fall back to GetNetworkParams. */
if (ares_fpGetAdaptersAddresses != ZERO_NULL)
{
printf ("Host Name: %s\n", fi->HostName);
printf ("Domain Name: %s\n", fi->DomainName);
printf ("DNS Servers:\n"
" %s (primary)\n", fi->DnsServerList.IpAddress.String);
const ULONG working_buf_size = 15000;
IP_ADAPTER_ADDRESSES *pFirstEntry = NULL;
IP_ADAPTER_ADDRESSES *pEntry = NULL;
ULONG bufSize = 0;
ULONG result = 0;

/* According to MSDN, the recommended way to do this is to use a temporary
buffer of 15K, to "dramatically reduce the chance that the GetAdaptersAddresses
method returns ERROR_BUFFER_OVERFLOW" */
pFirstEntry = ( IP_ADAPTER_ADDRESSES * ) malloc( working_buf_size );
bufSize = working_buf_size;
if( !pFirstEntry )
return 0;

/* Call the method one time */
result = ( *ares_fpGetAdaptersAddresses )( AF_UNSPEC, 0, 0, pFirstEntry, &bufSize );
if( result == ERROR_BUFFER_OVERFLOW )
{
/* Reallocate, bufSize should now be set to the required size */
pFirstEntry = ( IP_ADAPTER_ADDRESSES * ) realloc( pFirstEntry, bufSize );
if( !pFirstEntry )
return 0;

/* Call the method a second time */
result = ( *ares_fpGetAdaptersAddresses )( AF_UNSPEC, 0, 0, pFirstEntry, &bufSize );
if( result == ERROR_BUFFER_OVERFLOW )
{
/* Reallocate, bufSize should now be set to the required size */
pFirstEntry = ( IP_ADAPTER_ADDRESSES * ) realloc( pFirstEntry, bufSize );
if( !pFirstEntry )
return 0;

/* Call the method a third time. The maximum number of times we're going to do
this is 3. Three shall be the number thou shalt count, and the number of the
counting shall be three. Five is right out. */
result = ( *ares_fpGetAdaptersAddresses )( AF_UNSPEC, 0, 0, pFirstEntry, &bufSize );
}
}

/* Check the current result for failure */
if( result != ERROR_SUCCESS )
{
free( pFirstEntry );
return 0;
}

/* process the results */
for( pEntry = pFirstEntry ; pEntry != NULL ; pEntry = pEntry->Next )
{
IP_ADAPTER_DNS_SERVER_ADDRESS* pDNSAddr = pEntry->FirstDnsServerAddress;
for( ; pDNSAddr != NULL ; pDNSAddr = pDNSAddr->Next )
{
struct sockaddr *pGenericAddr = pDNSAddr->Address.lpSockaddr;
int stringlen = 0;

if( pGenericAddr->sa_family == AF_INET && left > ipv4_size )
{
/* Handle the v4 case */
struct sockaddr_in *pIPv4Addr = ( struct sockaddr_in * ) pGenericAddr;
ares_inet_ntop( AF_INET, &pIPv4Addr->sin_addr, ret, ipv4_size - 1 ); /* -1 for comma */

/* Append a comma to the end, THEN NULL. Should be OK because we
already tested the size at the top of the if statement. */
stringlen = strlen( ret );
ret[ stringlen ] = ',';
ret[ stringlen + 1 ] = '\0';
ret += stringlen + 1;
left -= ret - ret_buf;
++count;
}
else if( pGenericAddr->sa_family == AF_INET6 && left > ipv6_size )
{
/* Handle the v6 case */
struct sockaddr_in6 *pIPv6Addr = ( struct sockaddr_in6 * ) pGenericAddr;
ares_inet_ntop( AF_INET6, &pIPv6Addr->sin6_addr, ret, ipv6_size - 1 ); /* -1 for comma */

/* Append a comma to the end, THEN NULL. Should be OK because we
already tested the size at the top of the if statement. */
stringlen = strlen( ret );
ret[ stringlen ] = ',';
ret[ stringlen + 1 ] = '\0';
ret += stringlen + 1;
left -= ret - ret_buf;
++count;

/* NB on Windows this also returns stuff in the fec0::/10 range,
seems to be hard-coded somehow. Do we need to ignore them? */
}
}
}

if( pFirstEntry )
free( pFirstEntry );
if (ret > ret_buf)
ret[-1] = '\0';
return count;
}
if (strlen(fi->DnsServerList.IpAddress.String) > 0 &&
inet_addr(fi->DnsServerList.IpAddress.String) != INADDR_NONE &&
left > ip_size)
else
{
ret += sprintf (ret, "%s,", fi->DnsServerList.IpAddress.String);
left -= ret - ret_buf;
count++;
}
FIXED_INFO *fi, *newfi;
DWORD size = sizeof (*fi);
IP_ADDR_STRING *ipAddr;
int i;
int debug = 0;
HRESULT res;

fi = malloc(size);
if (!fi)
return 0;

res = (*ares_fpGetNetworkParams) (fi, &size);
if ((res != ERROR_BUFFER_OVERFLOW) && (res != ERROR_SUCCESS))
goto quit;

newfi = realloc(fi, size);
if (!newfi)
goto quit;

fi = newfi;
res = (*ares_fpGetNetworkParams) (fi, &size);
if (res != ERROR_SUCCESS)
goto quit;

for (i = 0, ipAddr = fi->DnsServerList.Next; ipAddr && left > ip_size;
ipAddr = ipAddr->Next, i++)
{
if (inet_addr(ipAddr->IpAddress.String) != INADDR_NONE)
if (debug)
{
ret += sprintf (ret, "%s,", ipAddr->IpAddress.String);
left -= ret - ret_buf;
count++;
printf ("Host Name: %s\n", fi->HostName);
printf ("Domain Name: %s\n", fi->DomainName);
printf ("DNS Servers:\n"
" %s (primary)\n", fi->DnsServerList.IpAddress.String);
}
if (strlen(fi->DnsServerList.IpAddress.String) > 0 &&
inet_addr(fi->DnsServerList.IpAddress.String) != INADDR_NONE &&
left > ipv4_size)
{
ret += sprintf (ret, "%s,", fi->DnsServerList.IpAddress.String);
left -= ret - ret_buf;
++count;
}

for (i = 0, ipAddr = fi->DnsServerList.Next; ipAddr && left > ipv4_size;
ipAddr = ipAddr->Next, i++)
{
if (inet_addr(ipAddr->IpAddress.String) != INADDR_NONE)
{
ret += sprintf (ret, "%s,", ipAddr->IpAddress.String);
left -= ret - ret_buf;
++count;
}
if (debug)
printf (" %s (secondary %d)\n", ipAddr->IpAddress.String, i+1);
}
if (debug)
printf (" %s (secondary %d)\n", ipAddr->IpAddress.String, i+1);
}

quit:
if (fi)
free(fi);

if (debug && left <= ip_size)
printf ("Too many nameservers. Truncating to %d addressess", count);
if (ret > ret_buf)
ret[-1] = '\0';
return count;
if (fi)
free(fi);

if (debug && left <= ipv4_size)
printf ("Too many nameservers. Truncating to %d addressess", count);
if (ret > ret_buf)
ret[-1] = '\0';
return count;
}
}
#endif

Expand Down Expand Up @@ -704,7 +827,7 @@ DhcpNameServer
DWORD data_type;
DWORD bytes;
DWORD result;
char buf[256];
char buf[512];
win_platform platform;

if (channel->nservers > -1) /* don't override ARES_OPT_SERVER */
Expand Down
10 changes: 10 additions & 0 deletions ares_library_init.c
Expand Up @@ -26,6 +26,7 @@
#ifdef USE_WINSOCK
fpGetNetworkParams_t ares_fpGetNetworkParams = ZERO_NULL;
fpSystemFunction036_t ares_fpSystemFunction036 = ZERO_NULL;
fpGetAdaptersAddresses_t ares_fpGetAdaptersAddresses = ZERO_NULL;
#endif

/* library-private global vars with source visibility restricted to this file */
Expand Down Expand Up @@ -56,6 +57,15 @@ static int ares_win32_init(void)
return ARES_EADDRGETNETWORKPARAMS;
}

ares_fpGetAdaptersAddresses = (fpGetAdaptersAddresses_t)
GetProcAddress(hnd_iphlpapi, "GetAdaptersAddresses");
if (!ares_fpGetAdaptersAddresses)
{
/* This can happen on clients before WinXP, I don't
think it should be an error, unless we don't want to
support Windows 2000 anymore */
}

/*
* When advapi32.dll is unavailable or advapi32.dll has no SystemFunction036,
* also known as RtlGenRandom, which is the case for Windows versions prior
Expand Down
2 changes: 2 additions & 0 deletions ares_library_init.h
Expand Up @@ -26,12 +26,14 @@

typedef DWORD (WINAPI *fpGetNetworkParams_t) (FIXED_INFO*, DWORD*);
typedef BOOLEAN (APIENTRY *fpSystemFunction036_t) (void*, ULONG);
typedef ULONG (WINAPI *fpGetAdaptersAddresses_t) ( ULONG, ULONG, void*, IP_ADAPTER_ADDRESSES*, ULONG* );

/* Forward-declaration of variables defined in ares_library_init.c */
/* that are global and unique instances for whole c-ares library. */

extern fpGetNetworkParams_t ares_fpGetNetworkParams;
extern fpSystemFunction036_t ares_fpSystemFunction036;
extern fpGetAdaptersAddresses_t ares_fpGetAdaptersAddresses;

#endif /* USE_WINSOCK */

Expand Down
2 changes: 1 addition & 1 deletion ares_private.h
Expand Up @@ -203,7 +203,7 @@ struct query {
void *arg;

/* Query status */
int try; /* Number of times we tried this query already. */
int try_count; /* Number of times we tried this query already. */
int server; /* Server this query has last been sent to. */
struct query_server_info *server_info; /* per-server state */
int using_tcp;
Expand Down
4 changes: 2 additions & 2 deletions ares_process.c
Expand Up @@ -684,7 +684,7 @@ static void next_server(ares_channel channel, struct query *query,
* servers to try. In total, we need to do channel->nservers * channel->tries
* attempts. Use query->try to remember how many times we already attempted
* this query. Use modular arithmetic to find the next server to try. */
while (++(query->try) < (channel->nservers * channel->tries))
while (++(query->try_count) < (channel->nservers * channel->tries))
{
struct server_state *server;

Expand Down Expand Up @@ -789,7 +789,7 @@ void ares__send_query(ares_channel channel, struct query *query,
return;
}
}
timeplus = channel->timeout << (query->try / channel->nservers);
timeplus = channel->timeout << (query->try_count / channel->nservers);
timeplus = (timeplus * (9 + (rand () & 7))) / 16;
query->timeout = *now;
ares__timeadd(&query->timeout,
Expand Down
2 changes: 1 addition & 1 deletion ares_send.c
Expand Up @@ -96,7 +96,7 @@ void ares_send(ares_channel channel, const unsigned char *qbuf, int qlen,
query->arg = arg;

/* Initialize query status. */
query->try = 0;
query->try_count = 0;

/* Choose the server to send the query to. If rotation is enabled, keep track
* of the next server we want to use. */
Expand Down

0 comments on commit 6518b56

Please sign in to comment.