diff --git a/CMakeLists.txt b/CMakeLists.txt index f216b338..25578930 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -830,6 +830,7 @@ set(HDR_PRIVATE evrpc-internal.h evsignal-internal.h evthread-internal.h + evdns-internal.h ht-internal.h http-internal.h iocp-internal.h diff --git a/Makefile.am b/Makefile.am index e9a1590d..efb9d972 100644 --- a/Makefile.am +++ b/Makefile.am @@ -333,6 +333,7 @@ noinst_HEADERS += \ evrpc-internal.h \ evsignal-internal.h \ evthread-internal.h \ + evdns-internal.h \ ht-internal.h \ http-internal.h \ iocp-internal.h \ diff --git a/evdns-internal.h b/evdns-internal.h new file mode 100644 index 00000000..56e88848 --- /dev/null +++ b/evdns-internal.h @@ -0,0 +1,22 @@ +#ifndef EVDNS_INTERNAL_H_INCLUDED_ +#define EVDNS_INTERNAL_H_INCLUDED_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "event2/event_struct.h" + +#ifdef _WIN32 + +EVENT2_EXPORT_SYMBOL +int load_nameservers_with_getadaptersaddresses(struct evdns_base *base); + +#endif + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/evdns.c b/evdns.c index 28318612..a8630738 100644 --- a/evdns.c +++ b/evdns.c @@ -104,6 +104,7 @@ #include "ipv6-internal.h" #include "util-internal.h" #include "evthread-internal.h" +#include "evdns-internal.h" #ifdef _WIN32 #include #include @@ -4621,21 +4622,20 @@ evdns_nameserver_ip_add_line(struct evdns_base *base, const char *ips) { return 0; } -typedef DWORD(WINAPI *GetNetworkParams_fn_t)(FIXED_INFO *, DWORD*); +typedef DWORD(WINAPI *GetAdaptersAddresses_fn_t)(ULONG, ULONG, PVOID, PIP_ADAPTER_ADDRESSES, PULONG); -/* Use the windows GetNetworkParams interface in iphlpapi.dll to */ +/* Use the windows GetAdaptersAddresses interface in iphlpapi.dll to */ /* figure out what our nameservers are. */ static int -load_nameservers_with_getnetworkparams(struct evdns_base *base) +load_nameservers_with_getadaptersaddresses_unlocked(struct evdns_base *base) { - /* Based on MSDN examples and inspection of c-ares code. */ - FIXED_INFO *fixed; + PIP_ADAPTER_ADDRESSES addresses = NULL; HMODULE handle = 0; - ULONG size = sizeof(FIXED_INFO); + ULONG size = sizeof(IP_ADAPTER_ADDRESSES); void *buf = NULL; - int status = 0, r, added_any; - IP_ADDR_STRING *ns; - GetNetworkParams_fn_t fn; + int status = 0, r, added_any = 0; + GetAdaptersAddresses_fn_t fn; + IP_ADAPTER_DNS_SERVER_ADDRESS *dnsserver = NULL; ASSERT_LOCKED(base); if (!(handle = evutil_load_windows_system_library_( @@ -4644,7 +4644,7 @@ load_nameservers_with_getnetworkparams(struct evdns_base *base) status = -1; goto done; } - if (!(fn = (GetNetworkParams_fn_t) GetProcAddress(handle, "GetNetworkParams"))) { + if (!(fn = (GetAdaptersAddresses_fn_t) GetProcAddress(handle, "GetAdaptersAddresses"))) { log(EVDNS_LOG_WARN, "Could not get address of function."); status = -1; goto done; @@ -4652,40 +4652,51 @@ load_nameservers_with_getnetworkparams(struct evdns_base *base) buf = mm_malloc(size); if (!buf) { status = 4; goto done; } - fixed = buf; - r = fn(fixed, &size); - if (r != ERROR_SUCCESS && r != ERROR_BUFFER_OVERFLOW) { + addresses = buf; + r = fn(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, NULL, addresses, &size); + if (r != NO_ERROR && r != ERROR_BUFFER_OVERFLOW) { status = -1; goto done; } - if (r != ERROR_SUCCESS) { + if (r != NO_ERROR) { mm_free(buf); buf = mm_malloc(size); if (!buf) { status = 4; goto done; } - fixed = buf; - r = fn(fixed, &size); - if (r != ERROR_SUCCESS) { + addresses = buf; + r = fn(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, NULL, addresses, &size); + if (r != NO_ERROR) { log(EVDNS_LOG_DEBUG, "fn() failed."); status = -1; goto done; } } - EVUTIL_ASSERT(fixed); - added_any = 0; - ns = &(fixed->DnsServerList); - while (ns) { - r = evdns_nameserver_ip_add_line(base, ns->IpAddress.String); - if (r) { - log(EVDNS_LOG_DEBUG,"Could not add nameserver %s to list,error: %d", - (ns->IpAddress.String),(int)GetLastError()); - status = r; - } else { - ++added_any; - log(EVDNS_LOG_DEBUG,"Successfully added %s as nameserver",ns->IpAddress.String); - } + while (addresses) { + dnsserver = addresses->FirstDnsServerAddress; + while (dnsserver && (addresses->OperStatus == IfOperStatusUp)) { + char ip[INET6_ADDRSTRLEN] = {0}; + if (AF_INET == dnsserver->Address.lpSockaddr->sa_family) { + inet_ntop(AF_INET, &((SOCKADDR_IN *)dnsserver->Address.lpSockaddr)->sin_addr, ip, sizeof(ip)); + } else if (AF_INET6 == dnsserver->Address.lpSockaddr->sa_family) { + inet_ntop(AF_INET6, &((SOCKADDR_IN6 *)dnsserver->Address.lpSockaddr)->sin6_addr, ip, sizeof(ip)); + } - ns = ns->Next; + dnsserver = dnsserver->Next; + if (strncmp(ip, "fec0:", 5) == 0) { /* remove ipv6 reserved address */ + continue; + } + + r = evdns_base_nameserver_ip_add(base, ip); + if (r) { + log(EVDNS_LOG_DEBUG, "Could not add nameserver %s to list, error: %d", ip, r); + status = r; + } else { + ++added_any; + log(EVDNS_LOG_DEBUG, "Successfully added %s as nameserver", ip); + } + } + + addresses = addresses->Next; } if (!added_any) { @@ -4704,6 +4715,16 @@ load_nameservers_with_getnetworkparams(struct evdns_base *base) return status; } +int +load_nameservers_with_getadaptersaddresses(struct evdns_base *base) +{ + int r; + EVDNS_LOCK(base); + r = load_nameservers_with_getadaptersaddresses_unlocked(base); + EVDNS_UNLOCK(base); + return r; +} + static int config_nameserver_from_reg_key(struct evdns_base *base, HKEY key, const TCHAR *subkey) { @@ -4803,7 +4824,7 @@ evdns_base_config_windows_nameservers(struct evdns_base *base) if (fname) mm_free(fname); - if (load_nameservers_with_getnetworkparams(base) == 0) { + if (load_nameservers_with_getadaptersaddresses_unlocked(base) == 0) { EVDNS_UNLOCK(base); return 0; } diff --git a/test/regress_dns.c b/test/regress_dns.c index e03f0d42..7c1846ab 100644 --- a/test/regress_dns.c +++ b/test/regress_dns.c @@ -78,6 +78,7 @@ #include #include "log-internal.h" #include "evthread-internal.h" +#include "evdns-internal.h" #include "regress.h" #include "regress_testutils.h" #include "regress_thread.h" @@ -1200,6 +1201,47 @@ end: evdns_base_free(dns, 0); } +#ifdef _WIN32 +static void +windows_dns_initialize_ipv6_nameservers_test(void *arg) +{ + struct basic_test_data *data = arg; + struct event_base *base = data->base; + struct evdns_base *dns = NULL; + struct sockaddr_storage ss; + int i = 0, count = 0, ipv6_count = 0, size = 0; + int sockfd = 0; + + dns = evdns_base_new(base, 0); + tt_assert(dns); + + tt_int_op(load_nameservers_with_getadaptersaddresses(dns), ==, 0); + count = evdns_base_count_nameservers(dns); + tt_int_op(count, >, 0); + + sockfd = socket(AF_INET6, SOCK_DGRAM, 0); + if (sockfd < 0) { + event_warn("socket(AF_INET6) failed. Skipping test."); + tt_skip(); + } + evutil_closesocket(sockfd); + + for (i = 0; i < count; ++i) { + size = evdns_base_get_nameserver_addr(dns, i, (struct sockaddr *)&ss, sizeof(ss)); + tt_int_op(size, >, 0); + if (ss.ss_family == AF_INET6) { + ipv6_count++; + } + } + /* CI environment does not have IPv6 addresses, so we cannot assert on this one */ + TT_BLATHER(("Found %i IPv6 addresses", ipv6_count)); + +end: + if (dns) + evdns_base_free(dns, 0); +} +#endif + static const char *dns_resolvconf_with_one_nameserver = "nameserver 127.0.0.53\n"; @@ -3273,6 +3315,12 @@ struct testcase_t dns_testcases[] = { { "initialize_nameservers", dns_initialize_nameservers_test, TT_FORK|TT_NEED_BASE, &basic_setup, NULL }, + +#ifdef _WIN32 + { "windows_initialize_ipv6_nameservers", windows_dns_initialize_ipv6_nameservers_test, + TT_FORK|TT_NEED_BASE, &basic_setup, NULL }, +#endif + #ifndef _WIN32 {"initialize_with_one_inactive_nameserver", dns_initialize_inactive_one_nameserver_test, TT_FORK | TT_NEED_BASE, &basic_setup, NULL},