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