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