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