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