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