summaryrefslogtreecommitdiff
path: root/ares_init.c
diff options
context:
space:
mode:
authorYang Tse <yangsita@gmail.com>2012-04-21 14:12:47 +0200
committerYang Tse <yangsita@gmail.com>2012-04-21 14:17:44 +0200
commit7ce8fe781af579e5e108ad4cd15d14b903418d9e (patch)
tree145c08e261eb50a07f4f9f35018512253c554c16 /ares_init.c
parent05346c25cbc2f6845917c64bf6df5b3f455ee7fb (diff)
downloadc-ares-7ce8fe781af579e5e108ad4cd15d14b903418d9e.tar.gz
c-ares-7ce8fe781af579e5e108ad4cd15d14b903418d9e.tar.bz2
c-ares-7ce8fe781af579e5e108ad4cd15d14b903418d9e.zip
ares_init.c: Further refactoring of Windows system's DNS fetching code
Diffstat (limited to 'ares_init.c')
-rw-r--r--ares_init.c550
1 files changed, 369 insertions, 181 deletions
diff --git a/ares_init.c b/ares_init.c
index 863b3b0..823bef5 100644
--- a/ares_init.c
+++ b/ares_init.c
@@ -546,74 +546,331 @@ static int init_by_environment(ares_channel channel)
#ifdef WIN32
/*
- * Warning: returns a dynamically allocated buffer, the user MUST
- * use free() if the function returns 1
+ * 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 int get_res_nt(HKEY hKey, const char *subkey, char **obuf)
+static int get_REG_SZ(HKEY hKey, const char *leafKeyName, char **outptr)
{
- /* Test for the size we need */
DWORD size = 0;
- int result;
+ int res;
+
+ *outptr = NULL;
- result = RegQueryValueEx(hKey, subkey, 0, NULL, NULL, &size);
- if ((result != ERROR_SUCCESS && result != ERROR_MORE_DATA) || !size)
+ /* Find out size of string stored in registry */
+ res = RegQueryValueEx(hKey, leafKeyName, 0, NULL, NULL, &size);
+ if ((res != ERROR_SUCCESS && res != ERROR_MORE_DATA) || !size)
return 0;
- *obuf = malloc(size+1);
- if (!*obuf)
+
+ /* Allocate buffer of indicated size plus one given that string
+ might have been stored without null termination */
+ *outptr = malloc(size+1);
+ if (!*outptr)
return 0;
- if (RegQueryValueEx(hKey, subkey, 0, NULL,
- (LPBYTE)*obuf, &size) != ERROR_SUCCESS)
+ /* Get the value for real */
+ res = RegQueryValueEx(hKey, leafKeyName, 0, NULL,
+ (unsigned char *)*outptr, &size);
+ if ((res != ERROR_SUCCESS) || (size == 1))
{
- free(*obuf);
+ free(*outptr);
+ *outptr = NULL;
return 0;
}
- if (size == 1)
+
+ /* Null terminate buffer allways */
+ *(*outptr + size) = '\0';
+
+ return 1;
+}
+
+/*
+ * get_REG_SZ_9X()
+ *
+ * Functionally identical to get_REG_SZ()
+ *
+ * Supported on Windows 95, 98 and ME.
+ */
+static int get_REG_SZ_9X(HKEY hKey, const char *leafKeyName, char **outptr)
+{
+ DWORD dataType = 0;
+ DWORD size = 0;
+ int res;
+
+ *outptr = NULL;
+
+ /* Find out size of string stored in registry */
+ res = RegQueryValueEx(hKey, leafKeyName, 0, &dataType, NULL, &size);
+ if ((res != ERROR_SUCCESS && res != ERROR_MORE_DATA) || !size)
+ return 0;
+
+ /* Allocate buffer of indicated size plus one given that string
+ might have been stored without null termination */
+ *outptr = malloc(size+1);
+ if (!*outptr)
+ return 0;
+
+ /* Get the value for real */
+ res = RegQueryValueEx(hKey, leafKeyName, 0, &dataType,
+ (unsigned char *)*outptr, &size);
+ if ((res != ERROR_SUCCESS) || (size == 1))
{
- free(*obuf);
+ free(*outptr);
+ *outptr = NULL;
return 0;
}
+
+ /* Null terminate buffer allways */
+ *(*outptr + size) = '\0';
+
return 1;
}
-static int get_res_interfaces_nt(HKEY hKey, const char *subkey, char **obuf)
+/*
+ * get_enum_REG_SZ()
+ *
+ * Given a 'hKeyParent' handle to an open registry key and a 'leafKeyName'
+ * pointer to the name of the registry leaf key to be queried, parent key
+ * is enumerated searching in child keys for given leaf key name and its
+ * associated string value. When located, this returns 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 int get_enum_REG_SZ(HKEY hKeyParent, const char *leafKeyName,
+ char **outptr)
{
- char enumbuf[39]; /* GUIDs are 38 chars + 1 for NULL */
- DWORD enum_size = 39;
- int idx = 0;
- HKEY hVal;
+ char enumKeyName[256];
+ DWORD enumKeyNameBuffSize;
+ DWORD enumKeyIdx = 0;
+ HKEY hKeyEnum;
+ int gotString;
+ int res;
- while (RegEnumKeyEx(hKey, idx++, enumbuf, &enum_size, 0,
- NULL, NULL, NULL) != ERROR_NO_MORE_ITEMS)
- {
- int rc;
+ *outptr = NULL;
- enum_size = 39;
- if (RegOpenKeyEx(hKey, enumbuf, 0, KEY_QUERY_VALUE, &hVal) !=
- ERROR_SUCCESS)
+ for(;;)
+ {
+ enumKeyNameBuffSize = sizeof(enumKeyName);
+ res = RegEnumKeyEx(hKeyParent, enumKeyIdx++, enumKeyName,
+ &enumKeyNameBuffSize, 0, NULL, NULL, NULL);
+ if (res != ERROR_SUCCESS)
+ break;
+ res = RegOpenKeyEx(hKeyParent, enumKeyName, 0, KEY_QUERY_VALUE,
+ &hKeyEnum);
+ if (res != ERROR_SUCCESS)
continue;
- rc = get_res_nt(hVal, subkey, obuf);
- RegCloseKey(hVal);
- if (rc)
- return 1;
- }
- return 0;
+ gotString = get_REG_SZ(hKeyEnum, leafKeyName, outptr);
+ RegCloseKey(hKeyEnum);
+ if (gotString)
+ break;
+ }
+
+ if (!*outptr)
+ return 0;
+
+ return 1;
}
-/* get_iphlpapi_dns_classic() is supported on W98 and newer */
-static int get_iphlpapi_dns_classic(char *ret_buf, size_t ret_size)
+/*
+ * get_DNS_Registry_9X()
+ *
+ * Functionally identical to get_DNS_Registry()
+ *
+ * Implementation supports Windows 95, 98 and ME.
+ */
+static int get_DNS_Registry_9X(char **outptr)
+{
+ HKEY hKey_VxD_MStcp;
+ int gotString;
+ int res;
+
+ *outptr = NULL;
+
+ res = RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_NS_9X, 0, KEY_READ,
+ &hKey_VxD_MStcp);
+ if (res != ERROR_SUCCESS)
+ return 0;
+
+ gotString = get_REG_SZ_9X(hKey_VxD_MStcp, NAMESERVER, outptr);
+ RegCloseKey(hKey_VxD_MStcp);
+
+ if (!gotString || !*outptr)
+ return 0;
+
+ return 1;
+}
+
+/*
+ * get_DNS_Registry_NT()
+ *
+ * Functionally identical to get_DNS_Registry()
+ *
+ * Refs: Microsoft Knowledge Base articles KB120642 and KB314053.
+ *
+ * Implementation supports Windows NT 3.5 and newer.
+ */
+static int get_DNS_Registry_NT(char **outptr)
+{
+ HKEY hKey_Interfaces = NULL;
+ HKEY hKey_Tcpip_Parameters;
+ int gotString;
+ int res;
+
+ *outptr = NULL;
+
+ res = RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_NS_NT_KEY, 0, KEY_READ,
+ &hKey_Tcpip_Parameters);
+ if (res != ERROR_SUCCESS)
+ return 0;
+
+ /*
+ ** Global DNS settings override adapter specific parameters when both
+ ** are set. Additionally static DNS settings override DHCP-configured
+ ** parameters when both are set.
+ */
+
+ /* Global DNS static parameters */
+ gotString = get_REG_SZ(hKey_Tcpip_Parameters, NAMESERVER, outptr);
+ if (gotString)
+ goto done;
+
+ /* Global DNS DHCP-configured parameters */
+ gotString = get_REG_SZ(hKey_Tcpip_Parameters, DHCPNAMESERVER, outptr);
+ if (gotString)
+ goto done;
+
+ /* Try adapter specific parameters */
+ res = RegOpenKeyEx(hKey_Tcpip_Parameters, "Interfaces", 0,
+ KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS,
+ &hKey_Interfaces);
+ if (res != ERROR_SUCCESS)
+ {
+ hKey_Interfaces = NULL;
+ goto done;
+ }
+
+ /* Adapter specific DNS static parameters */
+ gotString = get_enum_REG_SZ(hKey_Interfaces, NAMESERVER, outptr);
+ if (gotString)
+ goto done;
+
+ /* Adapter specific DNS DHCP-configured parameters */
+ gotString = get_enum_REG_SZ(hKey_Interfaces, DHCPNAMESERVER, outptr);
+
+done:
+ if (hKey_Interfaces)
+ RegCloseKey(hKey_Interfaces);
+
+ RegCloseKey(hKey_Tcpip_Parameters);
+
+ if (!gotString || !*outptr)
+ return 0;
+
+ return 1;
+}
+
+/*
+ * get_DNS_Registry()
+ *
+ * Locates DNS info in the registry. 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.
+ */
+static int get_DNS_Registry(char **outptr)
+{
+ win_platform platform;
+ int gotString = 0;
+
+ *outptr = NULL;
+
+ platform = ares__getplatform();
+
+ if (platform == WIN_NT)
+ gotString = get_DNS_Registry_NT(outptr);
+ else if (platform == WIN_9X)
+ gotString = get_DNS_Registry_9X(outptr);
+
+ if (!gotString)
+ return 0;
+
+ return 1;
+}
+
+/*
+ * commajoin()
+ *
+ * RTF code.
+ */
+static void commajoin(char **dst, const char *src)
+{
+ char *tmp;
+
+ if (*dst)
+ {
+ tmp = malloc(strlen(*dst) + strlen(src) + 2);
+ if (!tmp)
+ return;
+ sprintf(tmp, "%s,%s", *dst, src);
+ free(*dst);
+ *dst = tmp;
+ }
+ else
+ {
+ *dst = malloc(strlen(src) + 1);
+ if (!*dst)
+ return;
+ strcpy(*dst, src);
+ }
+}
+
+/*
+ * get_DNS_NetworkParams()
+ *
+ * Locates DNS info using GetNetworkParams() 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 98 and newer.
+ *
+ * Note: Ancient PSDK required in order to build a W98 target.
+ */
+static int get_DNS_NetworkParams(char **outptr)
{
FIXED_INFO *fi, *newfi;
struct ares_addr namesrvr;
char *txtaddr;
IP_ADDR_STRING *ipAddr;
- size_t txtlen;
- HRESULT res;
+ int res;
DWORD size = sizeof (*fi);
- char *endptr = ret_buf;
- int count = 0;
- *endptr = '\0';
+ *outptr = NULL;
+
+ /* Verify run-time availability of GetNetworkParams() */
+ if (ares_fpGetNetworkParams == ZERO_NULL)
+ return 0;
fi = malloc(size);
if (!fi)
@@ -621,16 +878,16 @@ static int get_iphlpapi_dns_classic(char *ret_buf, size_t ret_size)
res = (*ares_fpGetNetworkParams) (fi, &size);
if ((res != ERROR_BUFFER_OVERFLOW) && (res != ERROR_SUCCESS))
- goto quit;
+ goto done;
newfi = realloc(fi, size);
if (!newfi)
- goto quit;
+ goto done;
fi = newfi;
res = (*ares_fpGetNetworkParams) (fi, &size);
if (res != ERROR_SUCCESS)
- goto quit;
+ goto done;
for (ipAddr = &fi->DnsServerList; ipAddr; ipAddr = ipAddr->Next)
{
@@ -652,39 +909,47 @@ static int get_iphlpapi_dns_classic(char *ret_buf, size_t ret_size)
else
continue;
- txtlen = strlen(txtaddr);
- if (ret_size >= strlen(ret_buf) + txtlen + 2)
- {
- sprintf(endptr, "%s,", txtaddr);
- endptr += txtlen + 1;
- count++;
- }
+ commajoin(outptr, txtaddr);
+
+ if (!*outptr)
+ break;
}
-quit:
+done:
if (fi)
free(fi);
- if (endptr != ret_buf)
- *(endptr - 1) = '\0';
+ if (!*outptr)
+ return 0;
- return count;
+ return 1;
}
+/*
+ * 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_iphlpapi_dns_info(char *ret_buf, size_t ret_size)
+static int get_DNS_AdaptersAddresses(char **outptr)
{
IP_ADAPTER_DNS_SERVER_ADDRESS *ipaDNSAddr;
IP_ADAPTER_ADDRESSES *ipaa, *newipaa, *ipaaEntry;
- ULONG res;
ULONG ReqBufsz = IPAA_INITIAL_BUF_SZ;
ULONG Bufsz = IPAA_INITIAL_BUF_SZ;
ULONG AddrFlags = 0;
- char *endptr = ret_buf;
int trying = IPAA_MAX_TRIES;
- int count = 0;
+ int res;
union {
struct sockaddr *sa;
@@ -693,13 +958,12 @@ static int get_iphlpapi_dns_info(char *ret_buf, size_t ret_size)
} namesrvr;
char txtaddr[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
- size_t txtlen;
- /* Unless GetAdaptersAddresses is available, use GetNetworkParams */
- if (ares_fpGetAdaptersAddresses == ZERO_NULL)
- return get_iphlpapi_dns_classic(ret_buf, ret_size);
+ *outptr = NULL;
- *endptr = '\0';
+ /* Verify run-time availability of GetAdaptersAddresses() */
+ if (ares_fpGetAdaptersAddresses == ZERO_NULL)
+ return 0;
ipaa = malloc(Bufsz);
if (!ipaa)
@@ -709,7 +973,7 @@ static int get_iphlpapi_dns_info(char *ret_buf, size_t ret_size)
res = (*ares_fpGetAdaptersAddresses) (AF_UNSPEC, AddrFlags, NULL,
ipaa, &ReqBufsz);
if ((res != ERROR_BUFFER_OVERFLOW) && (res != ERROR_SUCCESS))
- goto quit;
+ goto done;
while ((res == ERROR_BUFFER_OVERFLOW) && (--trying))
{
@@ -717,7 +981,7 @@ static int get_iphlpapi_dns_info(char *ret_buf, size_t ret_size)
{
newipaa = realloc(ipaa, ReqBufsz);
if (!newipaa)
- goto quit;
+ goto done;
Bufsz = ReqBufsz;
ipaa = newipaa;
}
@@ -727,7 +991,7 @@ static int get_iphlpapi_dns_info(char *ret_buf, size_t ret_size)
break;
}
if (res != ERROR_SUCCESS)
- goto quit;
+ goto done;
for (ipaaEntry = ipaa; ipaaEntry; ipaaEntry = ipaaEntry->Next)
{
@@ -758,24 +1022,49 @@ static int get_iphlpapi_dns_info(char *ret_buf, size_t ret_size)
else
continue;
- txtlen = strlen(txtaddr);
- if (ret_size >= strlen(ret_buf) + txtlen + 2)
- {
- sprintf(endptr, "%s,", txtaddr);
- endptr += txtlen + 1;
- count++;
- }
+ commajoin(outptr, txtaddr);
+
+ if (!*outptr)
+ goto done;
}
}
-quit:
+done:
if (ipaa)
free(ipaa);
- if (endptr != ret_buf)
- *(endptr - 1) = '\0';
+ 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() */
+ if (get_DNS_AdaptersAddresses(outptr))
+ return 1;
- return count;
+ /* Try using IP helper API GetNetworkParams() */
+ if (get_DNS_NetworkParams(outptr))
+ return 1;
+
+ /* Fall-back to registry information */
+ return get_DNS_Registry(outptr);
}
#endif
@@ -790,111 +1079,13 @@ static int init_by_resolv_conf(ares_channel channel)
#ifdef WIN32
- /*
- NameServer info via IPHLPAPI (IP helper API):
- GetNetworkParams() should be the trusted source for this.
- Available in Win-98/2000 and later. If that fail, fall-back to
- registry information.
-
- NameServer Registry:
-
- On Windows 9X, the DNS server can be found in:
-HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\VxD\MSTCP\NameServer
-
- On Windows NT/2000/XP/2003:
-HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\NameServer
- or
-HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\DhcpNameServer
- or
-HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\{AdapterID}\
-NameServer
- or
-HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\{AdapterID}\
-DhcpNameServer
- */
-
- HKEY mykey;
- HKEY subkey;
- DWORD data_type;
- DWORD bytes;
- DWORD result;
- char buf[512];
- win_platform platform;
-
if (channel->nservers > -1) /* don't override ARES_OPT_SERVER */
return ARES_SUCCESS;
- if (get_iphlpapi_dns_info(buf,sizeof(buf)) > 0)
+ if (get_DNS_Windows(&line))
{
- status = config_nameserver(&servers, &nservers, buf);
- if (status == ARES_SUCCESS)
- goto okay;
- }
-
- platform = ares__getplatform();
-
- if (platform == WIN_NT)
- {
- if (RegOpenKeyEx(
- HKEY_LOCAL_MACHINE, WIN_NS_NT_KEY, 0,
- KEY_READ, &mykey
- ) == ERROR_SUCCESS)
- {
- RegOpenKeyEx(mykey, "Interfaces", 0,
- KEY_QUERY_VALUE|KEY_ENUMERATE_SUB_KEYS, &subkey);
- if (get_res_nt(mykey, NAMESERVER, &line))
- {
- status = config_nameserver(&servers, &nservers, line);
- free(line);
- }
- else if (get_res_nt(mykey, DHCPNAMESERVER, &line))
- {
- status = config_nameserver(&servers, &nservers, line);
- free(line);
- }
- /* Try the interfaces */
- else if (get_res_interfaces_nt(subkey, NAMESERVER, &line))
- {
- status = config_nameserver(&servers, &nservers, line);
- free(line);
- }
- else if (get_res_interfaces_nt(subkey, DHCPNAMESERVER, &line))
- {
- status = config_nameserver(&servers, &nservers, line);
- free(line);
- }
- RegCloseKey(subkey);
- RegCloseKey(mykey);
- }
- }
- else if (platform == WIN_9X)
- {
- if (RegOpenKeyEx(
- HKEY_LOCAL_MACHINE, WIN_NS_9X, 0,
- KEY_READ, &mykey
- ) == ERROR_SUCCESS)
- {
- if ((result = RegQueryValueEx(
- mykey, NAMESERVER, NULL, &data_type,
- NULL, &bytes
- )
- ) == ERROR_SUCCESS ||
- result == ERROR_MORE_DATA)
- {
- if (bytes)
- {
- line = malloc(bytes+1);
- if (RegQueryValueEx(mykey, NAMESERVER, NULL, &data_type,
- (unsigned char *)line, &bytes) ==
- ERROR_SUCCESS)
- {
- status = config_nameserver(&servers, &nservers, line);
- }
- free(line);
- }
- }
- }
- RegCloseKey(mykey);
+ status = config_nameserver(&servers, &nservers, line);
+ free(line);
}
if (status == ARES_SUCCESS)
@@ -1128,9 +1319,6 @@ DhcpNameServer
}
/* If we got any name server entries, fill them in. */
-#ifdef WIN32
-okay:
-#endif
if (servers)
{
channel->servers = servers;