diff --git a/ares_init.c b/ares_init.c index 971f8cf0..f7b700bf 100644 --- a/ares_init.c +++ b/ares_init.c @@ -832,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() * @@ -839,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)); } /* @@ -1302,6 +1303,146 @@ static int get_DNS_Windows(char **outptr) /* 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) @@ -1325,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 diff --git a/ares_private.h b/ares_private.h index 7359d5eb..5d773869 100644 --- a/ares_private.h +++ b/ares_private.h @@ -54,10 +54,16 @@ #define WIN_NS_9X "System\\CurrentControlSet\\Services\\VxD\\MSTCP" #define WIN_NS_NT_KEY "System\\CurrentControlSet\\Services\\Tcpip\\Parameters" +#define WIN_DNSCLIENT "Software\\Policies\\Microsoft\\System\\DNSClient" #define NAMESERVER "NameServer" #define DHCPNAMESERVER "DhcpNameServer" #define DATABASEPATH "DatabasePath" #define WIN_PATH_HOSTS "\\hosts" +#define SEARCHLIST_KEY "SearchList" +#define PRIMARYDNSSUFFIX_KEY "PrimaryDNSSuffix" +#define INTERFACES_KEY "Interfaces" +#define DOMAIN_KEY "Domain" +#define DHCPDOMAIN_KEY "DhcpDomain" #elif defined(WATT32)