From bd80dd0903069eacef818c1ebda898320578ec7f Mon Sep 17 00:00:00 2001 From: Huw Davies Date: Tue, 3 Aug 2021 09:20:21 +0100 Subject: [PATCH] dnsapi: Implement support for returning ipv4 servers in a DNS_ADDR_ARRAY. Signed-off-by: Huw Davies Signed-off-by: Alexandre Julliard --- dlls/dnsapi/dnsapi.h | 4 +- dlls/dnsapi/libresolv.c | 31 +++++++++----- dlls/dnsapi/query.c | 50 +++++++++++++++++++++-- dlls/dnsapi/tests/Makefile.in | 2 +- dlls/dnsapi/tests/query.c | 76 +++++++++++++++++++++++++++++++++++ include/windns.h | 7 +++- 6 files changed, 152 insertions(+), 18 deletions(-) diff --git a/dlls/dnsapi/dnsapi.h b/dlls/dnsapi/dnsapi.h index bcd6a6d5bc3..168c37282bd 100644 --- a/dlls/dnsapi/dnsapi.h +++ b/dlls/dnsapi/dnsapi.h @@ -121,13 +121,13 @@ static inline char *strdup_ua( const char *src ) extern const char *type_to_str( unsigned short ) DECLSPEC_HIDDEN; -extern DNS_STATUS CDECL resolv_get_serverlist( IP4_ARRAY *, DWORD * ) DECLSPEC_HIDDEN; +extern DNS_STATUS CDECL resolv_get_serverlist( USHORT, DNS_ADDR_ARRAY *, DWORD * ) DECLSPEC_HIDDEN; extern DNS_STATUS CDECL resolv_query( const char *, WORD, DWORD, DNS_RECORDA ** ) DECLSPEC_HIDDEN; extern DNS_STATUS CDECL resolv_set_serverlist( const IP4_ARRAY * ) DECLSPEC_HIDDEN; struct resolv_funcs { - DNS_STATUS (CDECL *get_serverlist)( IP4_ARRAY *addrs, DWORD *len ); + DNS_STATUS (CDECL *get_serverlist)( USHORT family, DNS_ADDR_ARRAY *addrs, DWORD *len ); DNS_STATUS (CDECL *query)( const char *name, WORD type, DWORD options, DNS_RECORDA **result ); DNS_STATUS (CDECL *set_serverlist)( const IP4_ARRAY *addrs ); }; diff --git a/dlls/dnsapi/libresolv.c b/dlls/dnsapi/libresolv.c index ac52147af01..7e754d73af3 100644 --- a/dlls/dnsapi/libresolv.c +++ b/dlls/dnsapi/libresolv.c @@ -50,6 +50,9 @@ #include "winbase.h" #include "winnls.h" #include "windns.h" +#define USE_WS_PREFIX +#include "ws2def.h" +#include "ws2ipdef.h" #include "wine/debug.h" #include "wine/heap.h" @@ -225,25 +228,33 @@ static DNS_STATUS map_h_errno( int error ) } } -DNS_STATUS CDECL resolv_get_serverlist( IP4_ARRAY *addrs, DWORD *len ) +DNS_STATUS CDECL resolv_get_serverlist( USHORT family, DNS_ADDR_ARRAY *addrs, DWORD *len ) { - unsigned int size; - int i; + DWORD needed, i; init_resolver(); - size = FIELD_OFFSET(IP4_ARRAY, AddrArray[_res.nscount]); - if (!addrs || *len < size) + if (family != WS_AF_INET) return ERROR_NOT_SUPPORTED; + + needed = FIELD_OFFSET(DNS_ADDR_ARRAY, AddrArray[_res.nscount]); + + if (!addrs || *len < needed) { - *len = size; - return ERROR_INSUFFICIENT_BUFFER; + *len = needed; + return !addrs ? ERROR_SUCCESS : ERROR_MORE_DATA; } - addrs->AddrCount = _res.nscount; + *len = needed; + memset( addrs, 0, needed ); + addrs->AddrCount = addrs->MaxCount = _res.nscount; for (i = 0; i < _res.nscount; i++) - addrs->AddrArray[i] = _res.nsaddr_list[i].sin_addr.s_addr; - + { + SOCKADDR_INET *inet = (SOCKADDR_INET *)addrs->AddrArray[i].MaxSa; + inet->Ipv4.sin_family = WS_AF_INET; + inet->Ipv4.sin_addr.WS_s_addr = _res.nsaddr_list[i].sin_addr.s_addr; + addrs->AddrArray[i].Data.DnsAddrUserDword[0] = sizeof(SOCKADDR_IN); + } return ERROR_SUCCESS; } diff --git a/dlls/dnsapi/query.c b/dlls/dnsapi/query.c index a7ae77148be..b836cdaaf48 100644 --- a/dlls/dnsapi/query.c +++ b/dlls/dnsapi/query.c @@ -25,6 +25,7 @@ #include "winnls.h" #include "windns.h" #include "nb30.h" +#include "ws2def.h" #include "wine/debug.h" #include "dnsapi.h" @@ -259,6 +260,46 @@ static DNS_STATUS get_hostname_w( COMPUTER_NAME_FORMAT format, PWSTR buffer, PDW return ERROR_SUCCESS; } +static DNS_STATUS get_dns_server_list( IP4_ARRAY *out, DWORD *len ) +{ + char buf[FIELD_OFFSET(DNS_ADDR_ARRAY, AddrArray[3])]; + DNS_ADDR_ARRAY *servers = (DNS_ADDR_ARRAY *)buf; + DWORD ret, needed, i, num, array_len = sizeof(buf); + + for (;;) + { + ret = resolv_funcs->get_serverlist( AF_INET, servers, &array_len ); + if (ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA) goto err; + num = (array_len - FIELD_OFFSET(DNS_ADDR_ARRAY, AddrArray[0])) / sizeof(DNS_ADDR); + needed = FIELD_OFFSET(IP4_ARRAY, AddrArray[num]); + if (!out || *len < needed) + { + *len = needed; + ret = !out ? ERROR_SUCCESS : ERROR_MORE_DATA; + goto err; + } + if (!ret) break; + + if ((char *)servers != buf) heap_free( servers ); + servers = heap_alloc( array_len ); + if (!servers) + { + ret = ERROR_NOT_ENOUGH_MEMORY; + goto err; + } + } + + out->AddrCount = num; + for (i = 0; i < num; i++) + out->AddrArray[i] = ((SOCKADDR_IN *)servers->AddrArray[i].MaxSa)->sin_addr.s_addr; + *len = needed; + ret = ERROR_SUCCESS; + +err: + if ((char *)servers != buf) heap_free( servers ); + return ret; +} + /****************************************************************************** * DnsQueryConfig [DNSAPI.@] * @@ -276,10 +317,8 @@ DNS_STATUS WINAPI DnsQueryConfig( DNS_CONFIG_TYPE config, DWORD flag, PCWSTR ada switch (config) { case DnsConfigDnsServerList: - { - ret = resolv_funcs->get_serverlist( buffer, len ); - break; - } + return get_dns_server_list( buffer, len ); + case DnsConfigHostName_A: case DnsConfigHostName_UTF8: return get_hostname_a( ComputerNameDnsHostname, buffer, len ); @@ -312,6 +351,9 @@ DNS_STATUS WINAPI DnsQueryConfig( DNS_CONFIG_TYPE config, DWORD flag, PCWSTR ada FIXME( "unimplemented config type %d\n", config ); break; + case DnsConfigDnsServersIpv4: + return resolv_funcs->get_serverlist( AF_INET, buffer, len ); + default: WARN( "unknown config type: %d\n", config ); break; diff --git a/dlls/dnsapi/tests/Makefile.in b/dlls/dnsapi/tests/Makefile.in index f476103b096..3f8c424b4f7 100644 --- a/dlls/dnsapi/tests/Makefile.in +++ b/dlls/dnsapi/tests/Makefile.in @@ -1,5 +1,5 @@ TESTDLL = dnsapi.dll -IMPORTS = dnsapi ws2_32 +IMPORTS = dnsapi ws2_32 iphlpapi C_SRCS = \ cache.c \ diff --git a/dlls/dnsapi/tests/query.c b/dlls/dnsapi/tests/query.c index bee3353e1b9..d11a53b5306 100644 --- a/dlls/dnsapi/tests/query.c +++ b/dlls/dnsapi/tests/query.c @@ -25,6 +25,7 @@ #include "windns.h" #include "winsock2.h" #include "ws2ipdef.h" +#include "iphlpapi.h" #include "wine/test.h" @@ -118,6 +119,80 @@ static void test_DnsQuery(void) } } +static IP_ADAPTER_ADDRESSES *get_adapters(void) +{ + ULONG err, size = 1024; + IP_ADAPTER_ADDRESSES *ret = malloc( size ); + for (;;) + { + err = GetAdaptersAddresses( AF_UNSPEC, GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | + GAA_FLAG_SKIP_FRIENDLY_NAME, + NULL, ret, &size ); + if (err != ERROR_BUFFER_OVERFLOW) break; + ret = realloc( ret, size ); + } + if (err == ERROR_SUCCESS) return ret; + free( ret ); + return NULL; +} + +static void test_DnsQueryConfig( void ) +{ + DWORD err, size, i; + WCHAR name[MAX_ADAPTER_NAME_LENGTH + 1]; + IP_ADAPTER_ADDRESSES *adapters, *ptr; + DNS_ADDR_ARRAY *ipv4; + IP4_ARRAY *ip4_array; + + if (!(adapters = get_adapters())) return; + + for (ptr = adapters; ptr; ptr = ptr->Next) + { + MultiByteToWideChar( CP_ACP, 0, ptr->AdapterName, -1, name, ARRAY_SIZE(name) ); + if (ptr->IfType == IF_TYPE_SOFTWARE_LOOPBACK) continue; + + size = 0; + err = DnsQueryConfig( DnsConfigDnsServersIpv4, 0, name, NULL, NULL, &size ); + if (err) continue; + ipv4 = malloc( size ); + err = DnsQueryConfig( DnsConfigDnsServersIpv4, 0, name, NULL, ipv4, &size ); + ok( !err, "got %d\n", err ); + + ok( ipv4->AddrCount == ipv4->MaxCount, "got %d vs %d\n", ipv4->AddrCount, ipv4->MaxCount ); + ok( !ipv4->Tag, "got %08x\n", ipv4->Tag ); + ok( !ipv4->Family, "got %d\n", ipv4->Family ); + ok( !ipv4->WordReserved, "got %04x\n", ipv4->WordReserved ); + ok( !ipv4->Flags, "got %08x\n", ipv4->Flags ); + ok( !ipv4->MatchFlag, "got %08x\n", ipv4->MatchFlag ); + ok( !ipv4->Reserved1, "got %08x\n", ipv4->Reserved1 ); + ok( !ipv4->Reserved2, "got %08x\n", ipv4->Reserved2 ); + + size = 0; + err = DnsQueryConfig( DnsConfigDnsServerList, 0, name, NULL, NULL, &size ); + ok( !err, "got %d\n", err ); + ip4_array = malloc( size ); + err = DnsQueryConfig( DnsConfigDnsServerList, 0, name, NULL, ip4_array, &size ); + ok( !err, "got %d\n", err ); + + ok( ipv4->AddrCount == ip4_array->AddrCount, "got %d vs %d\n", ipv4->AddrCount, ip4_array->AddrCount ); + + for (i = 0; i < ipv4->AddrCount; i++) + { + SOCKADDR_IN *sa = (SOCKADDR_IN *)ipv4->AddrArray[i].MaxSa; + + ok( sa->sin_family == AF_INET, "got %d\n", sa->sin_family ); + ok( sa->sin_addr.s_addr == ip4_array->AddrArray[i], "got %08x vs %08x\n", + sa->sin_addr.s_addr, ip4_array->AddrArray[i] ); + ok( ipv4->AddrArray[i].Data.DnsAddrUserDword[0] == sizeof(*sa), "got %d\n", + ipv4->AddrArray[i].Data.DnsAddrUserDword[0] ); + } + free( ip4_array ); + free( ipv4 ); + } + + free( adapters ); +} + START_TEST(query) { WSADATA data; @@ -125,4 +200,5 @@ START_TEST(query) WSAStartup(MAKEWORD(2, 2), &data); test_DnsQuery(); + test_DnsQueryConfig(); } diff --git a/include/windns.h b/include/windns.h index 26d37165ce1..f1f2184281c 100644 --- a/include/windns.h +++ b/include/windns.h @@ -152,7 +152,12 @@ typedef enum _DNS_CONFIG_TYPE DnsConfigHostName_UTF8, DnsConfigFullHostName_W, DnsConfigFullHostName_A, - DnsConfigFullHostName_UTF8 + DnsConfigFullHostName_UTF8, + + /* These are undocumented and return a DNS_ADDR_ARRAY */ + DnsConfigDnsServersUnspec = 4144, + DnsConfigDnsServersIpv4, + DnsConfigDnsServersIpv6 } DNS_CONFIG_TYPE; typedef enum _DnsSection