Windows platform supports reading IPv6 addresses for DNS server. (#1701)

When using libevent on the Windows platform in an IPv6 environment, I found that libevent could not read the DNS server address for IPv6 addresses during DNS initialization, resulting in constant DNS resolution failures. Then, on MSDN, I discovered that the GetNetworkParams interface does not support obtaining IPv6 addresses, and they provided another interface, GetAdaptersAddresses, to obtain both IPv4 and IPv6 addresses. Therefore, I replaced the GetNetworkParams interface with the GetAdaptersAddresses interface. Please review whether this modification can be merged into the master branch.

Reference MSDN documentation:
https://learn.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-getnetworkparams
https://learn.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-getadaptersaddresses

Co-authored-by: alphacheng <alphacheng@tencent.com>
Co-authored-by: Azat Khuzhin <azat@libevent.org>
Co-authored-by: Azat Khuzhin <a3at.mail@gmail.com>
This commit is contained in:
CXD 2024-11-03 01:26:59 +08:00 committed by GitHub
parent ffe913b9f9
commit d6dbd7f818
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 125 additions and 32 deletions

View File

@ -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

View File

@ -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 \

22
evdns-internal.h Normal file
View File

@ -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

85
evdns.c
View File

@ -104,6 +104,7 @@
#include "ipv6-internal.h"
#include "util-internal.h"
#include "evthread-internal.h"
#include "evdns-internal.h"
#ifdef _WIN32
#include <ctype.h>
#include <winsock2.h>
@ -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;
}

View File

@ -78,6 +78,7 @@
#include <event2/thread.h>
#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},