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