xref: /aosp_15_r20/external/cronet/net/dns/dns_config_service_win_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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