2 * Copyright 2020 Dmitry Timoshkov
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
31 #include "wine/test.h"
33 #define NS_MAXDNAME 1025
35 static void dump_dns_records(DNS_RECORDW
*rec
)
39 if (rec
->wType
== DNS_TYPE_CNAME
)
40 printf(" %s type=CNAME -> %s\n", wine_dbgstr_w(rec
->pName
), wine_dbgstr_w(rec
->Data
.CNAME
.pNameHost
));
41 else if (rec
->wType
== DNS_TYPE_A
)
45 addr
= inet_ntop(AF_INET
, &rec
->Data
.A
.IpAddress
, buf
, sizeof(buf
));
46 printf(" %s type=A -> %s\n", wine_dbgstr_w(rec
->pName
), addr
? addr
: "(unknown IPv4 address)");
48 else if (rec
->wType
== DNS_TYPE_AAAA
)
52 addr
= inet_ntop(AF_INET6
, &rec
->Data
.AAAA
.Ip6Address
, buf
, sizeof(buf
));
53 printf(" %s type=AAAA -> %s\n", wine_dbgstr_w(rec
->pName
), addr
? addr
: "(unknown IPv6 address)");
56 printf(" %s type=%d\n", wine_dbgstr_w(rec
->pName
), rec
->wType
);
61 static void test_DnsQuery(void)
63 WCHAR domain
[MAX_PATH
];
64 WCHAR name
[NS_MAXDNAME
];
66 DNS_RECORDW
*rec
, *ptr
;
70 status
= DnsQuery_W(L
" 192.168.111.11", DNS_TYPE_A
, 0, NULL
, &rec
, NULL
);
71 ok(status
!= ERROR_SUCCESS
, "got %lu.\n", status
);
72 status
= DnsQuery_W(L
"192.168.111.11 ", DNS_TYPE_A
, 0, NULL
, &rec
, NULL
);
73 ok(status
!= ERROR_SUCCESS
, "got %lu.\n", status
);
75 status
= DnsQuery_W(L
"192.168.111.11", DNS_TYPE_A
, 0, NULL
, &rec
, NULL
);
76 ok(!status
, "got %lu.\n", status
);
77 ok(rec
->wType
== DNS_TYPE_A
, "got %#x.\n", rec
->wType
);
78 ok(rec
->wDataLength
== sizeof(rec
->Data
.A
), "got %u.\n", rec
->wDataLength
);
79 ok(rec
->Data
.A
.IpAddress
== 0x0b6fa8c0, "got %#lx.\n", rec
->Data
.A
.IpAddress
);
80 ok(!rec
->pNext
, "got %p.\n", rec
->pNext
);
81 ok(rec
->dwTtl
== 604800, "got %lu.\n", rec
->dwTtl
);
82 ok(!wcscmp(rec
->pName
, L
"192.168.111.11"), "got %s.\n", debugstr_w(rec
->pName
));
83 ok(!rec
->Flags
.S
.Section
, "got %u.\n", rec
->Flags
.S
.Section
);
84 ok(!rec
->Flags
.S
.Delete
, "got %u.\n", rec
->Flags
.S
.Delete
);
85 ok(rec
->Flags
.S
.CharSet
== DnsCharSetUnicode
, "got %u.\n", rec
->Flags
.S
.CharSet
);
86 ok(!rec
->Flags
.S
.Unused
, "got %u.\n", rec
->Flags
.S
.Unused
);
87 ok(rec
->Flags
.S
.Reserved
== 0x20, "got %u.\n", rec
->Flags
.S
.Reserved
);
88 DnsRecordListFree(rec
, DnsFreeRecordList
);
90 status
= DnsQuery_W(L
"2001:db8:3333:4444:5555:6666:7777:8888", DNS_TYPE_AAAA
, 0, NULL
, &rec
, NULL
);
91 ok(!status
, "got %lu.\n", status
);
92 ok(rec
->wType
== DNS_TYPE_AAAA
, "got %#x.\n", rec
->wType
);
93 ok(rec
->wDataLength
== sizeof(rec
->Data
.AAAA
), "got %u.\n", rec
->wDataLength
);
94 ok(rec
->Data
.AAAA
.Ip6Address
.IP6Dword
[0] == 0xb80d0120, "got %#lx.\n", rec
->Data
.AAAA
.Ip6Address
.IP6Dword
[0]);
95 ok(rec
->Data
.AAAA
.Ip6Address
.IP6Dword
[1] == 0x44443333, "got %#lx.\n", rec
->Data
.AAAA
.Ip6Address
.IP6Dword
[1]);
96 ok(rec
->Data
.AAAA
.Ip6Address
.IP6Dword
[2] == 0x66665555, "got %#lx.\n", rec
->Data
.AAAA
.Ip6Address
.IP6Dword
[2]);
97 ok(rec
->Data
.AAAA
.Ip6Address
.IP6Dword
[3] == 0x88887777, "got %#lx.\n", rec
->Data
.AAAA
.Ip6Address
.IP6Dword
[3]);
98 ok(!rec
->pNext
, "got %p.\n", rec
->pNext
);
99 ok(rec
->dwTtl
== 604800, "got %lu.\n", rec
->dwTtl
);
100 ok(!wcscmp(rec
->pName
, L
"2001:db8:3333:4444:5555:6666:7777:8888"), "got %s.\n", debugstr_w(rec
->pName
));
101 ok(!rec
->Flags
.S
.Section
, "got %u.\n", rec
->Flags
.S
.Section
);
102 ok(!rec
->Flags
.S
.Delete
, "got %u.\n", rec
->Flags
.S
.Delete
);
103 ok(rec
->Flags
.S
.CharSet
== DnsCharSetUnicode
, "got %u.\n", rec
->Flags
.S
.CharSet
);
104 ok(!rec
->Flags
.S
.Unused
, "got %u.\n", rec
->Flags
.S
.Unused
);
105 ok(rec
->Flags
.S
.Reserved
== 0x20, "got %u.\n", rec
->Flags
.S
.Reserved
);
106 DnsRecordListFree(rec
, DnsFreeRecordList
);
109 status
= DnsQuery_W(L
"winehq.org", DNS_TYPE_A
, DNS_QUERY_STANDARD
, NULL
, &rec
, NULL
);
110 if (status
== ERROR_TIMEOUT
)
112 skip("query timed out\n");
115 ok(status
== ERROR_SUCCESS
, "got %ld\n", status
);
116 DnsRecordListFree(rec
, DnsFreeRecordList
);
118 /* Show that DNS_TYPE_A returns CNAMEs too */
120 wcscpy(domain
, L
"test.winehq.org"); /* should be a CNAME */
121 status
= DnsQuery_W(domain
, DNS_TYPE_A
, DNS_QUERY_STANDARD
, NULL
, &rec
, NULL
);
122 if (status
== ERROR_TIMEOUT
)
124 skip("query timed out\n");
127 ok(status
== ERROR_SUCCESS
, "got %ld\n", status
);
128 if (status
== ERROR_SUCCESS
&& winetest_debug
> 1)
130 trace("DnsQuery DNS_TYPE_A:\n");
131 dump_dns_records(rec
);
134 ptr
= rec
; /* CNAMEs come first */
135 ok(!wcscmp(domain
, ptr
->pName
), "expected record name %s, got %s\n", wine_dbgstr_w(domain
), wine_dbgstr_w(ptr
->pName
));
136 ok(DNS_TYPE_CNAME
== ptr
->wType
, "expected record type %d, got %d\n", DNS_TYPE_CNAME
, ptr
->wType
);
137 wcscpy(domain
, L
"winehq.org");
138 if (ptr
->wType
== DNS_TYPE_CNAME
)
139 ok(!wcscmp(domain
, ptr
->Data
.CNAME
.pNameHost
), "expected CNAME target %s, got %s\n", wine_dbgstr_w(domain
), wine_dbgstr_w(ptr
->Data
.CNAME
.pNameHost
));
142 ok(!wcscmp(domain
, ptr
->pName
), "expected record name %s, got %s\n",
143 wine_dbgstr_w(domain
), wine_dbgstr_w(ptr
->pName
));
144 ok(DNS_TYPE_A
== ptr
->wType
, "expected record type %d, got %d\n",
145 DNS_TYPE_A
, ptr
->wType
);
150 ok(wcscmp(domain
, ptr
->pName
), "did not expect a record for %s\n",
151 wine_dbgstr_w(ptr
->pName
));
152 ok(ptr
->wType
== DNS_TYPE_A
|| ptr
->wType
== DNS_TYPE_AAAA
|| ptr
->wType
== DNS_TYPE_OPT
,
153 "unexpected record type %d\n", ptr
->wType
);
157 DnsRecordListFree(rec
, DnsFreeRecordList
);
159 /* DNS_TYPE_CNAME does not return a record for its target but it may still
160 * return other related records!
163 wcscpy(domain
, L
"test.winehq.org");
164 status
= DnsQuery_W(L
"test.winehq.org", DNS_TYPE_CNAME
, DNS_QUERY_STANDARD
, NULL
, &rec
, NULL
);
165 if (status
== ERROR_TIMEOUT
)
167 skip("query timed out\n");
170 if (status
== DNS_INFO_NO_RECORDS
)
172 skip("no CNAME records\n");
175 ok(status
== ERROR_SUCCESS
, "got %ld\n", status
);
176 if (status
== ERROR_SUCCESS
&& winetest_debug
> 1)
178 trace("DnsQuery DNS_TYPE_CNAME:\n");
179 dump_dns_records(rec
);
183 ok(!wcscmp(domain
, ptr
->pName
), "expected record name %s, got %s\n", wine_dbgstr_w(domain
), wine_dbgstr_w(ptr
->pName
));
184 ok(DNS_TYPE_CNAME
== ptr
->wType
, "expected record type %d, got %d\n", DNS_TYPE_CNAME
, ptr
->wType
);
185 wcscpy(domain
, L
"winehq.org");
186 if (ptr
->wType
== DNS_TYPE_CNAME
)
187 ok(!wcscmp(domain
, ptr
->Data
.CNAME
.pNameHost
), "expected CNAME target %s, got %s\n", wine_dbgstr_w(domain
), wine_dbgstr_w(ptr
->Data
.CNAME
.pNameHost
));
188 DnsRecordListFree(rec
, DnsFreeRecordList
);
190 status
= DnsQuery_W(L
"", DNS_TYPE_SRV
, DNS_QUERY_STANDARD
, NULL
, &rec
, NULL
);
191 ok(status
== DNS_ERROR_RCODE_NAME_ERROR
|| status
== DNS_INFO_NO_RECORDS
|| status
== ERROR_INVALID_NAME
/* XP */,
192 "got %ld\n", status
);
194 wcscpy(domain
, L
"_ldap._tcp.deadbeef");
195 status
= DnsQuery_W(domain
, DNS_TYPE_SRV
, DNS_QUERY_STANDARD
, NULL
, &rec
, NULL
);
196 ok(status
== DNS_ERROR_RCODE_NAME_ERROR
|| status
== DNS_INFO_NO_RECORDS
|| status
== ERROR_INVALID_NAME
/* XP */,
197 "got %ld\n", status
);
199 wcscpy(domain
, L
"_ldap._tcp.dc._msdcs.");
200 size
= ARRAY_SIZE(domain
) - wcslen(domain
);
201 ret
= GetComputerNameExW(ComputerNameDnsDomain
, domain
+ wcslen(domain
), &size
);
202 ok(ret
, "GetComputerNameEx error %lu\n", GetLastError());
205 skip("computer is not in a domain\n");
209 status
= DnsQuery_W(domain
, DNS_TYPE_SRV
, DNS_QUERY_STANDARD
, NULL
, &rec
, NULL
);
210 trace("DnsQuery_W(%s) => %ld\n", wine_dbgstr_w(domain
), status
);
211 if (status
!= ERROR_SUCCESS
)
213 skip("domain %s doesn't have an SRV entry\n", wine_dbgstr_w(domain
));
217 trace("target %s, port %d\n", wine_dbgstr_w(rec
->Data
.Srv
.pNameTarget
), rec
->Data
.Srv
.wPort
);
219 lstrcpynW(name
, rec
->Data
.Srv
.pNameTarget
, ARRAY_SIZE(name
));
220 DnsRecordListFree(rec
, DnsFreeRecordList
);
223 status
= DnsQuery_W(name
, DNS_TYPE_A
, DNS_QUERY_STANDARD
, NULL
, &rec
, NULL
);
224 ok(status
== ERROR_SUCCESS
|| status
== DNS_ERROR_RCODE_NAME_ERROR
, "DnsQuery_W(%s) => %ld\n",
225 wine_dbgstr_w(name
), status
);
226 if (status
== ERROR_SUCCESS
)
229 WCHAR buf
[IP4_ADDRESS_STRING_LENGTH
];
231 addr
.sin_family
= AF_INET
;
233 addr
.sin_addr
.s_addr
= rec
->Data
.A
.IpAddress
;
235 ret
= WSAAddressToStringW((SOCKADDR
*)&addr
, sizeof(addr
), NULL
, buf
, &size
);
236 ok(!ret
, "WSAAddressToStringW error %lu\n", ret
);
237 trace("WSAAddressToStringW => %s\n", wine_dbgstr_w(buf
));
239 DnsRecordListFree(rec
, DnsFreeRecordList
);
243 status
= DnsQuery_W(name
, DNS_TYPE_AAAA
, DNS_QUERY_STANDARD
, NULL
, &rec
, NULL
);
244 ok(status
== ERROR_SUCCESS
|| status
== DNS_ERROR_RCODE_NAME_ERROR
, "DnsQuery_W(%s) => %ld\n",
245 wine_dbgstr_w(name
), status
);
246 if (status
== ERROR_SUCCESS
)
249 WCHAR buf
[IP6_ADDRESS_STRING_LENGTH
];
251 addr
.sin6_family
= AF_INET6
;
253 addr
.sin6_scope_id
= 0;
254 memcpy(addr
.sin6_addr
.s6_addr
, &rec
->Data
.AAAA
.Ip6Address
, sizeof(rec
->Data
.AAAA
.Ip6Address
));
256 ret
= WSAAddressToStringW((SOCKADDR
*)&addr
, sizeof(addr
), NULL
, buf
, &size
);
257 ok(!ret
, "WSAAddressToStringW error %lu\n", ret
);
258 trace("WSAAddressToStringW => %s\n", wine_dbgstr_w(buf
));
260 DnsRecordListFree(rec
, DnsFreeRecordList
);
264 static IP_ADAPTER_ADDRESSES
*get_adapters(void)
266 ULONG err
, size
= 1024;
267 IP_ADAPTER_ADDRESSES
*ret
= malloc( size
);
270 err
= GetAdaptersAddresses( AF_UNSPEC
, GAA_FLAG_SKIP_ANYCAST
| GAA_FLAG_SKIP_MULTICAST
|
271 GAA_FLAG_SKIP_FRIENDLY_NAME
,
273 if (err
!= ERROR_BUFFER_OVERFLOW
) break;
274 ret
= realloc( ret
, size
);
276 if (err
== ERROR_SUCCESS
) return ret
;
281 static void test_DnsQueryConfig( void )
284 DWORD size
, i
, ipv6_count
;
285 WCHAR name
[MAX_ADAPTER_NAME_LENGTH
+ 1];
286 IP_ADAPTER_ADDRESSES
*adapters
, *ptr
;
287 DNS_ADDR_ARRAY
*ipv4
, *ipv6
, *unspec
;
288 IP4_ARRAY
*ip4_array
;
290 if (!(adapters
= get_adapters())) return;
292 for (ptr
= adapters
; ptr
; ptr
= ptr
->Next
)
294 MultiByteToWideChar( CP_ACP
, 0, ptr
->AdapterName
, -1, name
, ARRAY_SIZE(name
) );
295 if (ptr
->IfType
== IF_TYPE_SOFTWARE_LOOPBACK
) continue;
298 err
= DnsQueryConfig( DnsConfigDnsServersIpv4
, 0, name
, NULL
, NULL
, &size
);
300 ipv4
= malloc( size
);
302 err
= DnsQueryConfig( DnsConfigDnsServersIpv4
, 0, name
, NULL
, ipv4
, &size
);
303 ok( err
== ERROR_MORE_DATA
, "got %ld\n", err
);
305 err
= DnsQueryConfig( DnsConfigDnsServersIpv4
, 0, name
, NULL
, ipv4
, &size
);
306 ok( !err
, "got %ld\n", err
);
308 ok( ipv4
->AddrCount
== ipv4
->MaxCount
, "got %lu vs %lu\n", ipv4
->AddrCount
, ipv4
->MaxCount
);
309 ok( !ipv4
->Tag
, "got %#lx\n", ipv4
->Tag
);
310 ok( !ipv4
->Family
, "got %d\n", ipv4
->Family
);
311 ok( !ipv4
->WordReserved
, "got %#x\n", ipv4
->WordReserved
);
312 ok( !ipv4
->Flags
, "got %#lx\n", ipv4
->Flags
);
313 ok( !ipv4
->MatchFlag
, "got %#lx\n", ipv4
->MatchFlag
);
314 ok( !ipv4
->Reserved1
, "got %#lx\n", ipv4
->Reserved1
);
315 ok( !ipv4
->Reserved2
, "got %#lx\n", ipv4
->Reserved2
);
318 err
= DnsQueryConfig( DnsConfigDnsServerList
, 0, name
, NULL
, NULL
, &size
);
319 ok( !err
, "got %ld\n", err
);
320 ip4_array
= malloc( size
);
321 err
= DnsQueryConfig( DnsConfigDnsServerList
, 0, name
, NULL
, ip4_array
, &size
);
322 ok( !err
, "got %ld\n", err
);
324 ok( ipv4
->AddrCount
== ip4_array
->AddrCount
, "got %lu vs %lu\n", ipv4
->AddrCount
, ip4_array
->AddrCount
);
326 for (i
= 0; i
< ipv4
->AddrCount
; i
++)
328 SOCKADDR_IN
*sa
= (SOCKADDR_IN
*)ipv4
->AddrArray
[i
].MaxSa
;
330 ok( sa
->sin_family
== AF_INET
, "got %d\n", sa
->sin_family
);
331 ok( sa
->sin_addr
.s_addr
== ip4_array
->AddrArray
[i
], "got %#lx vs %#lx\n",
332 sa
->sin_addr
.s_addr
, ip4_array
->AddrArray
[i
] );
333 ok( ipv4
->AddrArray
[i
].Data
.DnsAddrUserDword
[0] == sizeof(*sa
), "got %lu\n",
334 ipv4
->AddrArray
[i
].Data
.DnsAddrUserDword
[0] );
338 err
= DnsQueryConfig( DnsConfigDnsServersIpv6
, 0, name
, NULL
, NULL
, &size
);
339 ok( !err
|| err
== DNS_ERROR_NO_DNS_SERVERS
, "got %ld\n", err
);
344 ipv6
= malloc( size
);
345 err
= DnsQueryConfig( DnsConfigDnsServersIpv6
, 0, name
, NULL
, ipv6
, &size
);
346 ok( !err
, "got %ld\n", err
);
347 ipv6_count
= ipv6
->AddrCount
;
351 err
= DnsQueryConfig( DnsConfigDnsServersUnspec
, 0, name
, NULL
, NULL
, &size
);
352 ok( !err
, "got %ld\n", err
);
353 unspec
= malloc( size
);
354 err
= DnsQueryConfig( DnsConfigDnsServersUnspec
, 0, name
, NULL
, unspec
, &size
);
355 ok( !err
, "got %ld\n", err
);
357 ok( unspec
->AddrCount
== ipv4
->AddrCount
+ ipv6_count
, "got %lu vs %lu + %lu\n",
358 unspec
->AddrCount
, ipv4
->AddrCount
, ipv6_count
);
373 WSAStartup(MAKEWORD(2, 2), &data
);
376 test_DnsQueryConfig();