xref: /aosp_15_r20/external/cronet/net/dns/dns_config_service_win.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 <sysinfoapi.h>
8 
9 #include <memory>
10 #include <optional>
11 #include <set>
12 #include <string>
13 #include <string_view>
14 
15 #include "base/compiler_specific.h"
16 #include "base/files/file_path.h"
17 #include "base/files/file_path_watcher.h"
18 #include "base/functional/bind.h"
19 #include "base/functional/callback.h"
20 #include "base/location.h"
21 #include "base/logging.h"
22 #include "base/memory/free_deleter.h"
23 #include "base/memory/raw_ptr.h"
24 #include "base/memory/weak_ptr.h"
25 #include "base/metrics/histogram_functions.h"
26 #include "base/ranges/algorithm.h"
27 #include "base/sequence_checker.h"
28 #include "base/strings/strcat.h"
29 #include "base/strings/string_number_conversions.h"
30 #include "base/strings/string_piece.h"
31 #include "base/strings/string_split.h"
32 #include "base/strings/string_util.h"
33 #include "base/strings/utf_string_conversions.h"
34 #include "base/synchronization/lock.h"
35 #include "base/task/single_thread_task_runner.h"
36 #include "base/threading/scoped_blocking_call.h"
37 #include "base/types/expected.h"
38 #include "base/win/registry.h"
39 #include "base/win/scoped_handle.h"
40 #include "base/win/windows_types.h"
41 #include "net/base/ip_address.h"
42 #include "net/base/network_change_notifier.h"
43 #include "net/dns/dns_hosts.h"
44 #include "net/dns/public/dns_protocol.h"
45 #include "net/dns/serial_worker.h"
46 #include "url/url_canon.h"
47 
48 namespace net {
49 
50 namespace internal {
51 
52 namespace {
53 
54 // Registry key paths.
55 const wchar_t kTcpipPath[] =
56     L"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters";
57 const wchar_t kTcpip6Path[] =
58     L"SYSTEM\\CurrentControlSet\\Services\\Tcpip6\\Parameters";
59 const wchar_t kDnscachePath[] =
60     L"SYSTEM\\CurrentControlSet\\Services\\Dnscache\\Parameters";
61 const wchar_t kPolicyPath[] =
62     L"SOFTWARE\\Policies\\Microsoft\\Windows NT\\DNSClient";
63 
64 // These values are persisted to logs. Entries should not be renumbered and
65 // numeric values should never be reused.
66 enum class DnsWindowsCompatibility {
67   kCompatible = 0,
68   kIncompatibleResolutionPolicy = 1,
69   kIncompatibleProxy = 1 << 1,
70   kIncompatibleVpn = 1 << 2,
71   kIncompatibleAdapterSpecificNameserver = 1 << 3,
72 
73   KAllIncompatibleFlags = (1 << 4) - 1,
74   kMaxValue = KAllIncompatibleFlags
75 };
76 
operator |(DnsWindowsCompatibility a,DnsWindowsCompatibility b)77 inline constexpr DnsWindowsCompatibility operator|(DnsWindowsCompatibility a,
78                                                    DnsWindowsCompatibility b) {
79   return static_cast<DnsWindowsCompatibility>(static_cast<int>(a) |
80                                               static_cast<int>(b));
81 }
82 
operator |=(DnsWindowsCompatibility & a,DnsWindowsCompatibility b)83 inline DnsWindowsCompatibility& operator|=(DnsWindowsCompatibility& a,
84                                            DnsWindowsCompatibility b) {
85   return a = a | b;
86 }
87 
88 // Wrapper for GetAdaptersAddresses to get unicast addresses.
89 // Returns nullptr if failed.
90 std::unique_ptr<IP_ADAPTER_ADDRESSES, base::FreeDeleter>
ReadAdapterUnicastAddresses()91 ReadAdapterUnicastAddresses() {
92   base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
93                                                 base::BlockingType::MAY_BLOCK);
94 
95   std::unique_ptr<IP_ADAPTER_ADDRESSES, base::FreeDeleter> out;
96   ULONG len = 15000;  // As recommended by MSDN for GetAdaptersAddresses.
97   UINT rv = ERROR_BUFFER_OVERFLOW;
98   // Try up to three times.
99   for (unsigned tries = 0; (tries < 3) && (rv == ERROR_BUFFER_OVERFLOW);
100        tries++) {
101     out.reset(static_cast<PIP_ADAPTER_ADDRESSES>(malloc(len)));
102     memset(out.get(), 0, len);
103     rv = GetAdaptersAddresses(AF_UNSPEC,
104                               GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_DNS_SERVER |
105                               GAA_FLAG_SKIP_MULTICAST |
106                               GAA_FLAG_SKIP_FRIENDLY_NAME,
107                               nullptr, out.get(), &len);
108   }
109   if (rv != NO_ERROR)
110     out.reset();
111   return out;
112 }
113 
114 // Default address of "localhost" and local computer name can be overridden
115 // by the HOSTS file, but if it's not there, then we need to fill it in.
AddLocalhostEntriesTo(DnsHosts & in_out_hosts)116 bool AddLocalhostEntriesTo(DnsHosts& in_out_hosts) {
117   IPAddress loopback_ipv4 = IPAddress::IPv4Localhost();
118   IPAddress loopback_ipv6 = IPAddress::IPv6Localhost();
119 
120   // This does not override any pre-existing entries from the HOSTS file.
121   in_out_hosts.emplace(DnsHostsKey("localhost", ADDRESS_FAMILY_IPV4),
122                        loopback_ipv4);
123   in_out_hosts.emplace(DnsHostsKey("localhost", ADDRESS_FAMILY_IPV6),
124                        loopback_ipv6);
125 
126   wchar_t buffer[MAX_PATH];
127   DWORD size = MAX_PATH;
128   if (!GetComputerNameExW(ComputerNameDnsHostname, buffer, &size))
129     return false;
130   std::string localname = ParseDomainASCII(buffer);
131   if (localname.empty())
132     return false;
133   localname = base::ToLowerASCII(localname);
134 
135   bool have_ipv4 =
136       in_out_hosts.count(DnsHostsKey(localname, ADDRESS_FAMILY_IPV4)) > 0;
137   bool have_ipv6 =
138       in_out_hosts.count(DnsHostsKey(localname, ADDRESS_FAMILY_IPV6)) > 0;
139 
140   if (have_ipv4 && have_ipv6)
141     return true;
142 
143   std::unique_ptr<IP_ADAPTER_ADDRESSES, base::FreeDeleter> addresses =
144       ReadAdapterUnicastAddresses();
145   if (!addresses.get())
146     return false;
147 
148   // The order of adapters is the network binding order, so stick to the
149   // first good adapter for each family.
150   for (const IP_ADAPTER_ADDRESSES* adapter = addresses.get();
151        adapter != nullptr && (!have_ipv4 || !have_ipv6);
152        adapter = adapter->Next) {
153     if (adapter->OperStatus != IfOperStatusUp)
154       continue;
155     if (adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK)
156       continue;
157 
158     for (const IP_ADAPTER_UNICAST_ADDRESS* address =
159              adapter->FirstUnicastAddress;
160          address != nullptr; address = address->Next) {
161       IPEndPoint ipe;
162       if (!ipe.FromSockAddr(address->Address.lpSockaddr,
163                             address->Address.iSockaddrLength)) {
164         return false;
165       }
166       if (!have_ipv4 && (ipe.GetFamily() == ADDRESS_FAMILY_IPV4)) {
167         have_ipv4 = true;
168         in_out_hosts[DnsHostsKey(localname, ADDRESS_FAMILY_IPV4)] =
169             ipe.address();
170       } else if (!have_ipv6 && (ipe.GetFamily() == ADDRESS_FAMILY_IPV6)) {
171         have_ipv6 = true;
172         in_out_hosts[DnsHostsKey(localname, ADDRESS_FAMILY_IPV6)] =
173             ipe.address();
174       }
175     }
176   }
177   return true;
178 }
179 
180 // Watches a single registry key for changes.
181 class RegistryWatcher {
182  public:
183   typedef base::RepeatingCallback<void(bool succeeded)> CallbackType;
RegistryWatcher()184   RegistryWatcher() {}
185 
186   RegistryWatcher(const RegistryWatcher&) = delete;
187   RegistryWatcher& operator=(const RegistryWatcher&) = delete;
188 
~RegistryWatcher()189   ~RegistryWatcher() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); }
190 
Watch(const wchar_t key[],const CallbackType & callback)191   bool Watch(const wchar_t key[], const CallbackType& callback) {
192     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
193     DCHECK(!callback.is_null());
194     DCHECK(callback_.is_null());
195     callback_ = callback;
196     if (key_.Open(HKEY_LOCAL_MACHINE, key, KEY_NOTIFY) != ERROR_SUCCESS)
197       return false;
198 
199     return key_.StartWatching(base::BindOnce(&RegistryWatcher::OnObjectSignaled,
200                                              base::Unretained(this)));
201   }
202 
OnObjectSignaled()203   void OnObjectSignaled() {
204     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
205     DCHECK(!callback_.is_null());
206     if (key_.StartWatching(base::BindOnce(&RegistryWatcher::OnObjectSignaled,
207                                           base::Unretained(this)))) {
208       callback_.Run(true);
209     } else {
210       key_.Close();
211       callback_.Run(false);
212     }
213   }
214 
215  private:
216   CallbackType callback_;
217   base::win::RegKey key_;
218 
219   SEQUENCE_CHECKER(sequence_checker_);
220 };
221 
222 // Returns the path to the HOSTS file.
GetHostsPath()223 base::FilePath GetHostsPath() {
224   wchar_t buffer[MAX_PATH];
225   UINT rc = GetSystemDirectory(buffer, MAX_PATH);
226   DCHECK(0 < rc && rc < MAX_PATH);
227   return base::FilePath(buffer).Append(
228       FILE_PATH_LITERAL("drivers\\etc\\hosts"));
229 }
230 
ConfigureSuffixSearch(const WinDnsSystemSettings & settings,DnsConfig & in_out_config)231 void ConfigureSuffixSearch(const WinDnsSystemSettings& settings,
232                            DnsConfig& in_out_config) {
233   // SearchList takes precedence, so check it first.
234   if (settings.policy_search_list.has_value()) {
235     std::vector<std::string> search =
236         ParseSearchList(settings.policy_search_list.value());
237     if (!search.empty()) {
238       in_out_config.search = std::move(search);
239       return;
240     }
241     // Even if invalid, the policy disables the user-specified setting below.
242   } else if (settings.tcpip_search_list.has_value()) {
243     std::vector<std::string> search =
244         ParseSearchList(settings.tcpip_search_list.value());
245     if (!search.empty()) {
246       in_out_config.search = std::move(search);
247       return;
248     }
249   }
250 
251   // In absence of explicit search list, suffix search is:
252   // [primary suffix, connection-specific suffix, devolution of primary suffix].
253   // Primary suffix can be set by policy (primary_dns_suffix) or
254   // user setting (tcpip_domain).
255   //
256   // The policy (primary_dns_suffix) can be edited via Group Policy Editor
257   // (gpedit.msc) at Local Computer Policy => Computer Configuration
258   // => Administrative Template => Network => DNS Client => Primary DNS Suffix.
259   //
260   // The user setting (tcpip_domain) can be configurred at Computer Name in
261   // System Settings
262   std::string primary_suffix;
263   if (settings.primary_dns_suffix.has_value())
264     primary_suffix = ParseDomainASCII(settings.primary_dns_suffix.value());
265   if (primary_suffix.empty() && settings.tcpip_domain.has_value())
266     primary_suffix = ParseDomainASCII(settings.tcpip_domain.value());
267   if (primary_suffix.empty())
268     return;  // No primary suffix, hence no devolution.
269   // Primary suffix goes in front.
270   in_out_config.search.insert(in_out_config.search.begin(), primary_suffix);
271 
272   // Devolution is determined by precedence: policy > dnscache > tcpip.
273   // |enabled|: UseDomainNameDevolution and |level|: DomainNameDevolutionLevel
274   // are overridden independently.
275   WinDnsSystemSettings::DevolutionSetting devolution =
276       settings.policy_devolution;
277 
278   if (!devolution.enabled.has_value())
279     devolution.enabled = settings.dnscache_devolution.enabled;
280   if (!devolution.enabled.has_value())
281     devolution.enabled = settings.tcpip_devolution.enabled;
282   if (devolution.enabled.has_value() && (devolution.enabled.value() == 0))
283     return;  // Devolution disabled.
284 
285   // By default devolution is enabled.
286 
287   if (!devolution.level.has_value())
288     devolution.level = settings.dnscache_devolution.level;
289   if (!devolution.level.has_value())
290     devolution.level = settings.tcpip_devolution.level;
291 
292   // After the recent update, Windows will try to determine a safe default
293   // value by comparing the forest root domain (FRD) to the primary suffix.
294   // See http://support.microsoft.com/kb/957579 for details.
295   // For now, if the level is not set, we disable devolution, assuming that
296   // we will fallback to the system getaddrinfo anyway. This might cause
297   // performance loss for resolutions which depend on the system default
298   // devolution setting.
299   //
300   // If the level is explicitly set below 2, devolution is disabled.
301   if (!devolution.level.has_value() || devolution.level.value() < 2)
302     return;  // Devolution disabled.
303 
304   // Devolve the primary suffix. This naive logic matches the observed
305   // behavior (see also ParseSearchList). If a suffix is not valid, it will be
306   // discarded when the fully-qualified name is converted to DNS format.
307 
308   unsigned num_dots = base::ranges::count(primary_suffix, '.');
309 
310   for (size_t offset = 0; num_dots >= devolution.level.value(); --num_dots) {
311     offset = primary_suffix.find('.', offset + 1);
312     in_out_config.search.push_back(primary_suffix.substr(offset + 1));
313   }
314 }
315 
GetNameServers(const IP_ADAPTER_ADDRESSES * adapter)316 std::optional<std::vector<IPEndPoint>> GetNameServers(
317     const IP_ADAPTER_ADDRESSES* adapter) {
318   std::vector<IPEndPoint> nameservers;
319   for (const IP_ADAPTER_DNS_SERVER_ADDRESS* address =
320            adapter->FirstDnsServerAddress;
321        address != nullptr; address = address->Next) {
322     IPEndPoint ipe;
323     if (ipe.FromSockAddr(address->Address.lpSockaddr,
324                          address->Address.iSockaddrLength)) {
325       if (WinDnsSystemSettings::IsStatelessDiscoveryAddress(ipe.address()))
326         continue;
327       // Override unset port.
328       if (!ipe.port())
329         ipe = IPEndPoint(ipe.address(), dns_protocol::kDefaultPort);
330       nameservers.push_back(ipe);
331     } else {
332       return std::nullopt;
333     }
334   }
335   return nameservers;
336 }
337 
CheckAndRecordCompatibility(bool have_name_resolution_policy,bool have_proxy,bool uses_vpn,bool has_adapter_specific_nameservers)338 bool CheckAndRecordCompatibility(bool have_name_resolution_policy,
339                                  bool have_proxy,
340                                  bool uses_vpn,
341                                  bool has_adapter_specific_nameservers) {
342   DnsWindowsCompatibility compatibility = DnsWindowsCompatibility::kCompatible;
343   if (have_name_resolution_policy)
344     compatibility |= DnsWindowsCompatibility::kIncompatibleResolutionPolicy;
345   if (have_proxy)
346     compatibility |= DnsWindowsCompatibility::kIncompatibleProxy;
347   if (uses_vpn)
348     compatibility |= DnsWindowsCompatibility::kIncompatibleVpn;
349   if (has_adapter_specific_nameservers) {
350     compatibility |=
351         DnsWindowsCompatibility::kIncompatibleAdapterSpecificNameserver;
352   }
353   base::UmaHistogramEnumeration("Net.DNS.DnsConfig.Windows.Compatibility",
354                                 compatibility);
355   return compatibility == DnsWindowsCompatibility::kCompatible;
356 }
357 
358 }  // namespace
359 
ParseDomainASCII(std::wstring_view widestr)360 std::string ParseDomainASCII(std::wstring_view widestr) {
361   if (widestr.empty())
362     return "";
363 
364   // Check if already ASCII.
365   if (base::IsStringASCII(base::AsStringPiece16(widestr))) {
366     return std::string(widestr.begin(), widestr.end());
367   }
368 
369   // Otherwise try to convert it from IDN to punycode.
370   const int kInitialBufferSize = 256;
371   url::RawCanonOutputT<char16_t, kInitialBufferSize> punycode;
372   if (!url::IDNToASCII(base::AsStringPiece16(widestr), &punycode)) {
373     return "";
374   }
375 
376   // |punycode_output| should now be ASCII; convert it to a std::string.
377   // (We could use UTF16ToASCII() instead, but that requires an extra string
378   // copy. Since ASCII is a subset of UTF8 the following is equivalent).
379   std::string converted;
380   bool success =
381       base::UTF16ToUTF8(punycode.data(), punycode.length(), &converted);
382   DCHECK(success);
383   DCHECK(base::IsStringASCII(converted));
384   return converted;
385 }
386 
ParseSearchList(std::wstring_view value)387 std::vector<std::string> ParseSearchList(std::wstring_view value) {
388   if (value.empty())
389     return {};
390 
391   std::vector<std::string> output;
392 
393   // If the list includes an empty hostname (",," or ", ,"), it is terminated.
394   // Although nslookup and network connection property tab ignore such
395   // fragments ("a,b,,c" becomes ["a", "b", "c"]), our reference is getaddrinfo
396   // (which sees ["a", "b"]). WMI queries also return a matching search list.
397   for (std::wstring_view t : base::SplitStringPiece(
398            value, L",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
399     // Convert non-ASCII to punycode, although getaddrinfo does not properly
400     // handle such suffixes.
401     std::string parsed = ParseDomainASCII(t);
402     if (parsed.empty())
403       break;
404     output.push_back(std::move(parsed));
405   }
406   return output;
407 }
408 
409 base::expected<DnsConfig, ReadWinSystemDnsSettingsError>
ConvertSettingsToDnsConfig(const base::expected<WinDnsSystemSettings,ReadWinSystemDnsSettingsError> & settings_or_error)410 ConvertSettingsToDnsConfig(
411     const base::expected<WinDnsSystemSettings, ReadWinSystemDnsSettingsError>&
412         settings_or_error) {
413   if (!settings_or_error.has_value()) {
414     return base::unexpected(settings_or_error.error());
415   }
416   const WinDnsSystemSettings& settings = *settings_or_error;
417   bool uses_vpn = false;
418   bool has_adapter_specific_nameservers = false;
419 
420   DnsConfig dns_config;
421 
422   std::set<IPEndPoint> previous_nameservers_set;
423 
424   // Use GetAdapterAddresses to get effective DNS server order and
425   // connection-specific DNS suffix. Ignore disconnected and loopback adapters.
426   // The order of adapters is the network binding order, so stick to the
427   // first good adapter.
428   for (const IP_ADAPTER_ADDRESSES* adapter = settings.addresses.get();
429        adapter != nullptr; adapter = adapter->Next) {
430     // Check each adapter for a VPN interface. Even if a single such interface
431     // is present, treat this as an unhandled configuration.
432     if (adapter->IfType == IF_TYPE_PPP) {
433       uses_vpn = true;
434     }
435 
436     std::optional<std::vector<IPEndPoint>> nameservers =
437         GetNameServers(adapter);
438     if (!nameservers) {
439       return base::unexpected(
440           ReadWinSystemDnsSettingsError::kGetNameServersFailed);
441     }
442 
443     if (!nameservers->empty() && (adapter->OperStatus == IfOperStatusUp)) {
444       // Check if the |adapter| has adapter specific nameservers.
445       std::set<IPEndPoint> nameservers_set(nameservers->begin(),
446                                            nameservers->end());
447       if (!previous_nameservers_set.empty() &&
448           (previous_nameservers_set != nameservers_set)) {
449         has_adapter_specific_nameservers = true;
450       }
451       previous_nameservers_set = std::move(nameservers_set);
452     }
453 
454     // Skip disconnected and loopback adapters. If a good configuration was
455     // previously found, skip processing another adapter.
456     if (adapter->OperStatus != IfOperStatusUp ||
457         adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK ||
458         !dns_config.nameservers.empty())
459       continue;
460 
461     dns_config.nameservers = std::move(*nameservers);
462 
463     // IP_ADAPTER_ADDRESSES in Vista+ has a search list at |FirstDnsSuffix|,
464     // but it came up empty in all trials.
465     // |DnsSuffix| stores the effective connection-specific suffix, which is
466     // obtained via DHCP (regkey: Tcpip\Parameters\Interfaces\{XXX}\DhcpDomain)
467     // or specified by the user (regkey: Tcpip\Parameters\Domain).
468     std::string dns_suffix = ParseDomainASCII(adapter->DnsSuffix);
469     if (!dns_suffix.empty())
470       dns_config.search.push_back(std::move(dns_suffix));
471   }
472 
473   if (dns_config.nameservers.empty()) {
474     return base::unexpected(ReadWinSystemDnsSettingsError::kNoNameServerFound);
475   }
476 
477   // Windows always tries a multi-label name "as is" before using suffixes.
478   dns_config.ndots = 1;
479 
480   if (!settings.append_to_multi_label_name.has_value()) {
481     dns_config.append_to_multi_label_name = false;
482   } else {
483     dns_config.append_to_multi_label_name =
484         (settings.append_to_multi_label_name.value() != 0);
485   }
486 
487   if (settings.have_name_resolution_policy) {
488     // TODO(szym): only set this to true if NRPT has DirectAccess rules.
489     dns_config.use_local_ipv6 = true;
490   }
491 
492   if (!CheckAndRecordCompatibility(settings.have_name_resolution_policy,
493                                    settings.have_proxy, uses_vpn,
494                                    has_adapter_specific_nameservers)) {
495     dns_config.unhandled_options = true;
496   }
497 
498   ConfigureSuffixSearch(settings, dns_config);
499   return dns_config;
500 }
501 
502 // Watches registry and HOSTS file for changes. Must live on a sequence which
503 // allows IO.
504 class DnsConfigServiceWin::Watcher
505     : public NetworkChangeNotifier::IPAddressObserver,
506       public DnsConfigService::Watcher {
507  public:
Watcher(DnsConfigServiceWin & service)508   explicit Watcher(DnsConfigServiceWin& service)
509       : DnsConfigService::Watcher(service) {}
510 
511   Watcher(const Watcher&) = delete;
512   Watcher& operator=(const Watcher&) = delete;
513 
~Watcher()514   ~Watcher() override { NetworkChangeNotifier::RemoveIPAddressObserver(this); }
515 
Watch()516   bool Watch() override {
517     CheckOnCorrectSequence();
518 
519     RegistryWatcher::CallbackType callback =
520         base::BindRepeating(&Watcher::OnConfigChanged, base::Unretained(this));
521 
522     bool success = true;
523 
524     // The Tcpip key must be present.
525     if (!tcpip_watcher_.Watch(kTcpipPath, callback)) {
526       LOG(ERROR) << "DNS registry watch failed to start.";
527       success = false;
528     }
529 
530     // Watch for IPv6 nameservers.
531     tcpip6_watcher_.Watch(kTcpip6Path, callback);
532 
533     // DNS suffix search list and devolution can be configured via group
534     // policy which sets this registry key. If the key is missing, the policy
535     // does not apply, and the DNS client uses Tcpip and Dnscache settings.
536     // If a policy is installed, DnsConfigService will need to be restarted.
537     // BUG=99509
538 
539     dnscache_watcher_.Watch(kDnscachePath, callback);
540     policy_watcher_.Watch(kPolicyPath, callback);
541 
542     if (!hosts_watcher_.Watch(
543             GetHostsPath(), base::FilePathWatcher::Type::kNonRecursive,
544             base::BindRepeating(&Watcher::OnHostsFilePathWatcherChange,
545                                 base::Unretained(this)))) {
546       LOG(ERROR) << "DNS hosts watch failed to start.";
547       success = false;
548     } else {
549       // Also need to observe changes to local non-loopback IP for DnsHosts.
550       NetworkChangeNotifier::AddIPAddressObserver(this);
551     }
552     return success;
553   }
554 
555  private:
OnHostsFilePathWatcherChange(const base::FilePath & path,bool error)556   void OnHostsFilePathWatcherChange(const base::FilePath& path, bool error) {
557     if (error)
558       NetworkChangeNotifier::RemoveIPAddressObserver(this);
559     OnHostsChanged(!error);
560   }
561 
562   // NetworkChangeNotifier::IPAddressObserver:
OnIPAddressChanged()563   void OnIPAddressChanged() override {
564     // Need to update non-loopback IP of local host.
565     OnHostsChanged(true);
566   }
567 
568   RegistryWatcher tcpip_watcher_;
569   RegistryWatcher tcpip6_watcher_;
570   RegistryWatcher dnscache_watcher_;
571   RegistryWatcher policy_watcher_;
572   base::FilePathWatcher hosts_watcher_;
573 };
574 
575 // Reads config from registry and IpHelper. All work performed in ThreadPool.
576 class DnsConfigServiceWin::ConfigReader : public SerialWorker {
577  public:
ConfigReader(DnsConfigServiceWin & service)578   explicit ConfigReader(DnsConfigServiceWin& service)
579       : SerialWorker(/*max_number_of_retries=*/3), service_(&service) {}
~ConfigReader()580   ~ConfigReader() override {}
581 
582   // SerialWorker::
CreateWorkItem()583   std::unique_ptr<SerialWorker::WorkItem> CreateWorkItem() override {
584     return std::make_unique<WorkItem>();
585   }
586 
OnWorkFinished(std::unique_ptr<SerialWorker::WorkItem> serial_worker_work_item)587   bool OnWorkFinished(std::unique_ptr<SerialWorker::WorkItem>
588                           serial_worker_work_item) override {
589     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
590     DCHECK(serial_worker_work_item);
591     DCHECK(!IsCancelled());
592 
593     WorkItem* work_item = static_cast<WorkItem*>(serial_worker_work_item.get());
594     base::UmaHistogramEnumeration(
595         base::StrCat({"Net.DNS.DnsConfig.Windows.ReadSystemSettings",
596                       base::NumberToString(GetFailureCount())}),
597         work_item->dns_config_or_error_.has_value()
598             ? ReadWinSystemDnsSettingsError::kOk
599             : work_item->dns_config_or_error_.error());
600 
601     if (work_item->dns_config_or_error_.has_value()) {
602       service_->OnConfigRead(
603           std::move(work_item->dns_config_or_error_).value());
604       return true;
605     } else {
606       LOG(WARNING) << "Failed to read DnsConfig.";
607       return false;
608     }
609   }
610 
611  private:
612   class WorkItem : public SerialWorker::WorkItem {
613    public:
614     ~WorkItem() override = default;
615 
DoWork()616     void DoWork() override {
617       dns_config_or_error_ =
618           ConvertSettingsToDnsConfig(ReadWinSystemDnsSettings());
619     }
620 
621    private:
622     friend DnsConfigServiceWin::ConfigReader;
623     base::expected<DnsConfig, ReadWinSystemDnsSettingsError>
624         dns_config_or_error_;
625   };
626 
627   raw_ptr<DnsConfigServiceWin> service_;
628   // Written in DoWork(), read in OnWorkFinished(). No locking required.
629 };
630 
631 // Extension of DnsConfigService::HostsReader that fills in localhost and local
632 // computer name if necessary.
633 class DnsConfigServiceWin::HostsReader : public DnsConfigService::HostsReader {
634  public:
HostsReader(DnsConfigServiceWin & service)635   explicit HostsReader(DnsConfigServiceWin& service)
636       : DnsConfigService::HostsReader(GetHostsPath().value(), service) {}
637 
638   ~HostsReader() override = default;
639 
640   HostsReader(const HostsReader&) = delete;
641   HostsReader& operator=(const HostsReader&) = delete;
642 
643   // SerialWorker:
CreateWorkItem()644   std::unique_ptr<SerialWorker::WorkItem> CreateWorkItem() override {
645     return std::make_unique<WorkItem>(GetHostsPath());
646   }
647 
648  private:
649   class WorkItem : public DnsConfigService::HostsReader::WorkItem {
650    public:
WorkItem(base::FilePath hosts_file_path)651     explicit WorkItem(base::FilePath hosts_file_path)
652         : DnsConfigService::HostsReader::WorkItem(
653               std::make_unique<DnsHostsFileParser>(
654                   std::move(hosts_file_path))) {}
655 
656     ~WorkItem() override = default;
657 
AddAdditionalHostsTo(DnsHosts & in_out_dns_hosts)658     bool AddAdditionalHostsTo(DnsHosts& in_out_dns_hosts) override {
659       base::ScopedBlockingCall scoped_blocking_call(
660           FROM_HERE, base::BlockingType::MAY_BLOCK);
661       return AddLocalhostEntriesTo(in_out_dns_hosts);
662     }
663   };
664 };
665 
DnsConfigServiceWin()666 DnsConfigServiceWin::DnsConfigServiceWin()
667     : DnsConfigService(GetHostsPath().value(),
668                        std::nullopt /* config_change_delay */) {
669   // Allow constructing on one sequence and living on another.
670   DETACH_FROM_SEQUENCE(sequence_checker_);
671 }
672 
~DnsConfigServiceWin()673 DnsConfigServiceWin::~DnsConfigServiceWin() {
674   if (config_reader_)
675     config_reader_->Cancel();
676   if (hosts_reader_)
677     hosts_reader_->Cancel();
678 }
679 
ReadConfigNow()680 void DnsConfigServiceWin::ReadConfigNow() {
681   if (!config_reader_)
682     config_reader_ = std::make_unique<ConfigReader>(*this);
683   config_reader_->WorkNow();
684 }
685 
ReadHostsNow()686 void DnsConfigServiceWin::ReadHostsNow() {
687   if (!hosts_reader_)
688     hosts_reader_ = std::make_unique<HostsReader>(*this);
689   hosts_reader_->WorkNow();
690 }
691 
StartWatching()692 bool DnsConfigServiceWin::StartWatching() {
693   DCHECK(!watcher_);
694   // TODO(szym): re-start watcher if that makes sense. http://crbug.com/116139
695   watcher_ = std::make_unique<Watcher>(*this);
696   return watcher_->Watch();
697 }
698 
699 }  // namespace internal
700 
701 // static
CreateSystemService()702 std::unique_ptr<DnsConfigService> DnsConfigService::CreateSystemService() {
703   return std::make_unique<internal::DnsConfigServiceWin>();
704 }
705 
706 }  // namespace net
707