1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "net/dns/dns_config_service_win.h"
6
7 #include <optional>
8 #include <string>
9 #include <vector>
10
11 #include "base/check.h"
12 #include "base/memory/free_deleter.h"
13 #include "base/test/gmock_expected_support.h"
14 #include "net/base/ip_address.h"
15 #include "net/base/ip_endpoint.h"
16 #include "net/dns/public/dns_protocol.h"
17 #include "net/dns/public/win_dns_system_settings.h"
18 #include "testing/gmock/include/gmock/gmock.h"
19 #include "testing/gtest/include/gtest/gtest.h"
20
21 namespace net {
22
23 namespace {
24
TEST(DnsConfigServiceWinTest,ParseSearchList)25 TEST(DnsConfigServiceWinTest, ParseSearchList) {
26 const struct TestCase {
27 const wchar_t* input;
28 std::vector<std::string> expected;
29 } cases[] = {
30 {L"chromium.org", {"chromium.org"}},
31 {L"chromium.org,org", {"chromium.org", "org"}},
32 // Empty suffixes terminate the list
33 {L"crbug.com,com,,org", {"crbug.com", "com"}},
34 // IDN are converted to punycode
35 {L"\u017c\xf3\u0142ta.pi\u0119\u015b\u0107.pl,pl",
36 {"xn--ta-4ja03asj.xn--pi-wla5e0q.pl", "pl"}},
37 // Empty search list is invalid
38 {L"", {}},
39 {L",,", {}},
40 };
41
42 for (const auto& t : cases) {
43 EXPECT_EQ(internal::ParseSearchList(t.input), t.expected);
44 }
45 }
46
47 struct AdapterInfo {
48 IFTYPE if_type;
49 IF_OPER_STATUS oper_status;
50 const WCHAR* dns_suffix;
51 std::string dns_server_addresses[4]; // Empty string indicates end.
52 uint16_t ports[4];
53 };
54
CreateAdapterAddresses(const AdapterInfo * infos)55 std::unique_ptr<IP_ADAPTER_ADDRESSES, base::FreeDeleter> CreateAdapterAddresses(
56 const AdapterInfo* infos) {
57 size_t num_adapters = 0;
58 size_t num_addresses = 0;
59 for (size_t i = 0; infos[i].if_type; ++i) {
60 ++num_adapters;
61 for (size_t j = 0; !infos[i].dns_server_addresses[j].empty(); ++j) {
62 ++num_addresses;
63 }
64 }
65
66 size_t heap_size = num_adapters * sizeof(IP_ADAPTER_ADDRESSES) +
67 num_addresses * (sizeof(IP_ADAPTER_DNS_SERVER_ADDRESS) +
68 sizeof(struct sockaddr_storage));
69 std::unique_ptr<IP_ADAPTER_ADDRESSES, base::FreeDeleter> heap(
70 static_cast<IP_ADAPTER_ADDRESSES*>(malloc(heap_size)));
71 CHECK(heap.get());
72 memset(heap.get(), 0, heap_size);
73
74 IP_ADAPTER_ADDRESSES* adapters = heap.get();
75 IP_ADAPTER_DNS_SERVER_ADDRESS* addresses =
76 reinterpret_cast<IP_ADAPTER_DNS_SERVER_ADDRESS*>(adapters + num_adapters);
77 struct sockaddr_storage* storage =
78 reinterpret_cast<struct sockaddr_storage*>(addresses + num_addresses);
79
80 for (size_t i = 0; i < num_adapters; ++i) {
81 const AdapterInfo& info = infos[i];
82 IP_ADAPTER_ADDRESSES* adapter = adapters + i;
83 if (i + 1 < num_adapters)
84 adapter->Next = adapter + 1;
85 adapter->IfType = info.if_type;
86 adapter->OperStatus = info.oper_status;
87 adapter->DnsSuffix = const_cast<PWCHAR>(info.dns_suffix);
88 IP_ADAPTER_DNS_SERVER_ADDRESS* address = nullptr;
89 for (size_t j = 0; !info.dns_server_addresses[j].empty(); ++j) {
90 --num_addresses;
91 if (j == 0) {
92 address = adapter->FirstDnsServerAddress = addresses + num_addresses;
93 } else {
94 // Note that |address| is moving backwards.
95 address = address->Next = address - 1;
96 }
97 IPAddress ip;
98 CHECK(ip.AssignFromIPLiteral(info.dns_server_addresses[j]));
99 IPEndPoint ipe = IPEndPoint(ip, info.ports[j]);
100 address->Address.lpSockaddr =
101 reinterpret_cast<LPSOCKADDR>(storage + num_addresses);
102 socklen_t length = sizeof(struct sockaddr_storage);
103 CHECK(ipe.ToSockAddr(address->Address.lpSockaddr, &length));
104 address->Address.iSockaddrLength = static_cast<int>(length);
105 }
106 }
107
108 return heap;
109 }
110
TEST(DnsConfigServiceWinTest,ConvertAdapterAddresses)111 TEST(DnsConfigServiceWinTest, ConvertAdapterAddresses) {
112 // Check nameservers and connection-specific suffix.
113 const struct TestCase {
114 AdapterInfo input_adapters[4]; // |if_type| == 0 indicates end.
115 std::string expected_nameservers[4]; // Empty string indicates end.
116 std::string expected_suffix;
117 uint16_t expected_ports[4];
118 } cases[] = {
119 { // Ignore loopback and inactive adapters.
120 {
121 { IF_TYPE_SOFTWARE_LOOPBACK, IfOperStatusUp, L"funnyloop",
122 { "2.0.0.2" } },
123 { IF_TYPE_FASTETHER, IfOperStatusDormant, L"example.com",
124 { "1.0.0.1" } },
125 { IF_TYPE_USB, IfOperStatusUp, L"chromium.org",
126 { "10.0.0.10", "2001:FFFF::1111" } },
127 { 0 },
128 },
129 { "10.0.0.10", "2001:FFFF::1111" },
130 "chromium.org",
131 },
132 { // Respect configured ports.
133 {
134 { IF_TYPE_USB, IfOperStatusUp, L"chromium.org",
135 { "10.0.0.10", "2001:FFFF::1111" }, { 1024, 24 } },
136 { 0 },
137 },
138 { "10.0.0.10", "2001:FFFF::1111" },
139 "chromium.org",
140 { 1024, 24 },
141 },
142 { // Use the preferred adapter (first in binding order) and filter
143 // stateless DNS discovery addresses.
144 {
145 { IF_TYPE_SOFTWARE_LOOPBACK, IfOperStatusUp, L"funnyloop",
146 { "2.0.0.2" } },
147 { IF_TYPE_FASTETHER, IfOperStatusUp, L"example.com",
148 { "1.0.0.1", "fec0:0:0:ffff::2", "8.8.8.8" } },
149 { IF_TYPE_USB, IfOperStatusUp, L"chromium.org",
150 { "10.0.0.10", "2001:FFFF::1111" } },
151 { 0 },
152 },
153 { "1.0.0.1", "8.8.8.8" },
154 "example.com",
155 },
156 { // No usable adapters.
157 {
158 { IF_TYPE_SOFTWARE_LOOPBACK, IfOperStatusUp, L"localhost",
159 { "2.0.0.2" } },
160 { IF_TYPE_FASTETHER, IfOperStatusDormant, L"example.com",
161 { "1.0.0.1" } },
162 { IF_TYPE_USB, IfOperStatusUp, L"chromium.org" },
163 { 0 },
164 },
165 },
166 };
167
168 for (const auto& t : cases) {
169 WinDnsSystemSettings settings;
170 settings.addresses = CreateAdapterAddresses(t.input_adapters);
171 // Default settings for the rest.
172 std::vector<IPEndPoint> expected_nameservers;
173 for (size_t j = 0; !t.expected_nameservers[j].empty(); ++j) {
174 IPAddress ip;
175 ASSERT_TRUE(ip.AssignFromIPLiteral(t.expected_nameservers[j]));
176 uint16_t port = t.expected_ports[j];
177 if (!port)
178 port = dns_protocol::kDefaultPort;
179 expected_nameservers.push_back(IPEndPoint(ip, port));
180 }
181
182 base::expected<DnsConfig, ReadWinSystemDnsSettingsError> config_or_error =
183 internal::ConvertSettingsToDnsConfig(std::move(settings));
184 bool expected_success = !expected_nameservers.empty();
185 EXPECT_EQ(expected_success, config_or_error.has_value());
186 if (config_or_error.has_value()) {
187 EXPECT_EQ(expected_nameservers, config_or_error->nameservers);
188 EXPECT_THAT(config_or_error->search,
189 testing::ElementsAre(t.expected_suffix));
190 }
191 }
192 }
193
TEST(DnsConfigServiceWinTest,ConvertSuffixSearch)194 TEST(DnsConfigServiceWinTest, ConvertSuffixSearch) {
195 AdapterInfo infos[2] = {
196 { IF_TYPE_USB, IfOperStatusUp, L"connection.suffix", { "1.0.0.1" } },
197 { 0 },
198 };
199
200 const struct TestCase {
201 struct {
202 std::optional<std::wstring> policy_search_list;
203 std::optional<std::wstring> tcpip_search_list;
204 std::optional<std::wstring> tcpip_domain;
205 std::optional<std::wstring> primary_dns_suffix;
206 WinDnsSystemSettings::DevolutionSetting policy_devolution;
207 WinDnsSystemSettings::DevolutionSetting dnscache_devolution;
208 WinDnsSystemSettings::DevolutionSetting tcpip_devolution;
209 } input_settings;
210 std::vector<std::string> expected_search;
211 } cases[] = {
212 {
213 // Policy SearchList override.
214 {
215 L"policy.searchlist.a,policy.searchlist.b",
216 L"tcpip.searchlist.a,tcpip.searchlist.b",
217 L"tcpip.domain",
218 L"primary.dns.suffix",
219 },
220 {"policy.searchlist.a", "policy.searchlist.b"},
221 },
222 {
223 // User-specified SearchList override.
224 {
225 std::nullopt,
226 L"tcpip.searchlist.a,tcpip.searchlist.b",
227 L"tcpip.domain",
228 L"primary.dns.suffix",
229 },
230 {"tcpip.searchlist.a", "tcpip.searchlist.b"},
231 },
232 {
233 // Void SearchList. Using tcpip.domain
234 {
235 L",bad.searchlist,parsed.as.empty",
236 L"tcpip.searchlist,good.but.overridden",
237 L"tcpip.domain",
238 std::nullopt,
239 },
240 {"tcpip.domain", "connection.suffix"},
241 },
242 {
243 // Void SearchList. Using primary.dns.suffix
244 {
245 L",bad.searchlist,parsed.as.empty",
246 L"tcpip.searchlist,good.but.overridden",
247 L"tcpip.domain",
248 L"primary.dns.suffix",
249 },
250 {"primary.dns.suffix", "connection.suffix"},
251 },
252 {
253 // Void SearchList. Using tcpip.domain when primary.dns.suffix is
254 // empty
255 {
256 L",bad.searchlist,parsed.as.empty",
257 L"tcpip.searchlist,good.but.overridden",
258 L"tcpip.domain",
259 L"",
260 },
261 {"tcpip.domain", "connection.suffix"},
262 },
263 {
264 // Void SearchList. Using tcpip.domain when primary.dns.suffix is NULL
265 {
266 L",bad.searchlist,parsed.as.empty",
267 L"tcpip.searchlist,good.but.overridden",
268 L"tcpip.domain",
269 L"",
270 },
271 {"tcpip.domain", "connection.suffix"},
272 },
273 {
274 // No primary suffix. Devolution does not matter.
275 {
276 std::nullopt,
277 std::nullopt,
278 L"",
279 L"",
280 {1, 2},
281 },
282 {"connection.suffix"},
283 },
284 {
285 // Devolution enabled by policy, level by dnscache.
286 {
287 std::nullopt,
288 std::nullopt,
289 L"a.b.c.d.e",
290 std::nullopt,
291 {1, std::nullopt}, // policy_devolution: enabled, level
292 {0, 3}, // dnscache_devolution
293 {0, 1}, // tcpip_devolution
294 },
295 {"a.b.c.d.e", "connection.suffix", "b.c.d.e", "c.d.e"},
296 },
297 {
298 // Devolution enabled by dnscache, level by policy.
299 {
300 std::nullopt,
301 std::nullopt,
302 L"a.b.c.d.e",
303 L"f.g.i.l.j",
304 {std::nullopt, 4},
305 {1, std::nullopt},
306 {0, 3},
307 },
308 {"f.g.i.l.j", "connection.suffix", "g.i.l.j"},
309 },
310 {
311 // Devolution enabled by default.
312 {
313 std::nullopt,
314 std::nullopt,
315 L"a.b.c.d.e",
316 std::nullopt,
317 {std::nullopt, std::nullopt},
318 {std::nullopt, 3},
319 {std::nullopt, 1},
320 },
321 {"a.b.c.d.e", "connection.suffix", "b.c.d.e", "c.d.e"},
322 },
323 {
324 // Devolution enabled at level = 2, but nothing to devolve.
325 {
326 std::nullopt,
327 std::nullopt,
328 L"a.b",
329 std::nullopt,
330 {std::nullopt, std::nullopt},
331 {std::nullopt, 2},
332 {std::nullopt, 2},
333 },
334 {"a.b", "connection.suffix"},
335 },
336 {
337 // Devolution disabled when no explicit level.
338 {
339 std::nullopt,
340 std::nullopt,
341 L"a.b.c.d.e",
342 std::nullopt,
343 {1, std::nullopt},
344 {1, std::nullopt},
345 {1, std::nullopt},
346 },
347 {"a.b.c.d.e", "connection.suffix"},
348 },
349 {
350 // Devolution disabled by policy level.
351 {
352 std::nullopt,
353 std::nullopt,
354 L"a.b.c.d.e",
355 std::nullopt,
356 {std::nullopt, 1},
357 {1, 3},
358 {1, 4},
359 },
360 {"a.b.c.d.e", "connection.suffix"},
361 },
362 {
363 // Devolution disabled by user setting.
364 {
365 std::nullopt,
366 std::nullopt,
367 L"a.b.c.d.e",
368 std::nullopt,
369 {std::nullopt, 3},
370 {std::nullopt, 3},
371 {0, 3},
372 },
373 {"a.b.c.d.e", "connection.suffix"},
374 },
375 };
376
377 for (auto& t : cases) {
378 WinDnsSystemSettings settings;
379 settings.addresses = CreateAdapterAddresses(infos);
380 settings.policy_search_list = t.input_settings.policy_search_list;
381 settings.tcpip_search_list = t.input_settings.tcpip_search_list;
382 settings.tcpip_domain = t.input_settings.tcpip_domain;
383 settings.primary_dns_suffix = t.input_settings.primary_dns_suffix;
384 settings.policy_devolution = t.input_settings.policy_devolution;
385 settings.dnscache_devolution = t.input_settings.dnscache_devolution;
386 settings.tcpip_devolution = t.input_settings.tcpip_devolution;
387
388 ASSERT_OK_AND_ASSIGN(
389 DnsConfig dns_config,
390 internal::ConvertSettingsToDnsConfig(std::move(settings)));
391 EXPECT_THAT(dns_config,
392 testing::Field(&DnsConfig::search,
393 testing::ElementsAreArray(t.expected_search)));
394 }
395 }
396
TEST(DnsConfigServiceWinTest,AppendToMultiLabelName)397 TEST(DnsConfigServiceWinTest, AppendToMultiLabelName) {
398 AdapterInfo infos[2] = {
399 { IF_TYPE_USB, IfOperStatusUp, L"connection.suffix", { "1.0.0.1" } },
400 { 0 },
401 };
402
403 const struct TestCase {
404 std::optional<DWORD> input;
405 bool expected_output;
406 } cases[] = {
407 {0, false},
408 {1, true},
409 {std::nullopt, false},
410 };
411
412 for (const auto& t : cases) {
413 WinDnsSystemSettings settings;
414 settings.addresses = CreateAdapterAddresses(infos);
415 settings.append_to_multi_label_name = t.input;
416 ASSERT_OK_AND_ASSIGN(
417 DnsConfig dns_config,
418 internal::ConvertSettingsToDnsConfig(std::move(settings)));
419 EXPECT_THAT(dns_config,
420 testing::Field(&DnsConfig::append_to_multi_label_name,
421 testing::Eq(t.expected_output)));
422 }
423 }
424
425 // Setting have_name_resolution_policy_table should set `unhandled_options`.
TEST(DnsConfigServiceWinTest,HaveNRPT)426 TEST(DnsConfigServiceWinTest, HaveNRPT) {
427 AdapterInfo infos[2] = {
428 { IF_TYPE_USB, IfOperStatusUp, L"connection.suffix", { "1.0.0.1" } },
429 { 0 },
430 };
431
432 const struct TestCase {
433 bool have_nrpt;
434 bool unhandled_options;
435 } cases[] = {
436 {false, false},
437 {true, true},
438 };
439
440 for (const auto& t : cases) {
441 WinDnsSystemSettings settings;
442 settings.addresses = CreateAdapterAddresses(infos);
443 settings.have_name_resolution_policy = t.have_nrpt;
444 ASSERT_OK_AND_ASSIGN(
445 DnsConfig dns_config,
446 internal::ConvertSettingsToDnsConfig(std::move(settings)));
447 EXPECT_EQ(t.unhandled_options, dns_config.unhandled_options);
448 EXPECT_EQ(t.have_nrpt, dns_config.use_local_ipv6);
449 }
450 }
451
452 // Setting have_proxy should set `unhandled_options`.
TEST(DnsConfigServiceWinTest,HaveProxy)453 TEST(DnsConfigServiceWinTest, HaveProxy) {
454 AdapterInfo infos[2] = {
455 {IF_TYPE_USB, IfOperStatusUp, L"connection.suffix", {"1.0.0.1"}},
456 {0},
457 };
458
459 const struct TestCase {
460 bool have_proxy;
461 bool unhandled_options;
462 } cases[] = {
463 {false, false},
464 {true, true},
465 };
466
467 for (const auto& t : cases) {
468 WinDnsSystemSettings settings;
469 settings.addresses = CreateAdapterAddresses(infos);
470 settings.have_proxy = t.have_proxy;
471 ASSERT_OK_AND_ASSIGN(
472 DnsConfig dns_config,
473 internal::ConvertSettingsToDnsConfig(std::move(settings)));
474 EXPECT_THAT(dns_config, testing::Field(&DnsConfig::unhandled_options,
475 testing::Eq(t.unhandled_options)));
476 }
477 }
478
479 // Setting uses_vpn should set `unhandled_options`.
TEST(DnsConfigServiceWinTest,UsesVpn)480 TEST(DnsConfigServiceWinTest, UsesVpn) {
481 AdapterInfo infos[3] = {
482 {IF_TYPE_USB, IfOperStatusUp, L"connection.suffix", {"1.0.0.1"}},
483 {IF_TYPE_PPP, IfOperStatusUp, L"connection.suffix", {"1.0.0.1"}},
484 {0},
485 };
486
487 WinDnsSystemSettings settings;
488 settings.addresses = CreateAdapterAddresses(infos);
489 ASSERT_OK_AND_ASSIGN(
490 DnsConfig dns_config,
491 internal::ConvertSettingsToDnsConfig(std::move(settings)));
492 EXPECT_THAT(dns_config,
493 testing::Field(&DnsConfig::unhandled_options, testing::IsTrue()));
494 }
495
496 // Setting adapter specific nameservers should set `unhandled_options`.
TEST(DnsConfigServiceWinTest,AdapterSpecificNameservers)497 TEST(DnsConfigServiceWinTest, AdapterSpecificNameservers) {
498 AdapterInfo infos[3] = {
499 {IF_TYPE_FASTETHER,
500 IfOperStatusUp,
501 L"example.com",
502 {"1.0.0.1", "fec0:0:0:ffff::2", "8.8.8.8"}},
503 {IF_TYPE_USB,
504 IfOperStatusUp,
505 L"chromium.org",
506 {"10.0.0.10", "2001:FFFF::1111"}},
507 {0},
508 };
509
510 WinDnsSystemSettings settings;
511 settings.addresses = CreateAdapterAddresses(infos);
512 ASSERT_OK_AND_ASSIGN(
513 DnsConfig dns_config,
514 internal::ConvertSettingsToDnsConfig(std::move(settings)));
515 EXPECT_THAT(dns_config,
516 testing::Field(&DnsConfig::unhandled_options, testing::IsTrue()));
517 }
518
519 // Setting adapter specific nameservers for non operational adapter should not
520 // set `unhandled_options`.
TEST(DnsConfigServiceWinTest,AdapterSpecificNameserversForNo)521 TEST(DnsConfigServiceWinTest, AdapterSpecificNameserversForNo) {
522 AdapterInfo infos[3] = {
523 {IF_TYPE_FASTETHER,
524 IfOperStatusUp,
525 L"example.com",
526 {"1.0.0.1", "fec0:0:0:ffff::2", "8.8.8.8"}},
527 {IF_TYPE_USB,
528 IfOperStatusDown,
529 L"chromium.org",
530 {"10.0.0.10", "2001:FFFF::1111"}},
531 {0},
532 };
533
534 WinDnsSystemSettings settings;
535 settings.addresses = CreateAdapterAddresses(infos);
536 ASSERT_OK_AND_ASSIGN(
537 DnsConfig dns_config,
538 internal::ConvertSettingsToDnsConfig(std::move(settings)));
539 EXPECT_THAT(dns_config, testing::Field(&DnsConfig::unhandled_options,
540 testing::IsFalse()));
541 }
542
543 } // namespace
544
545 } // namespace net
546