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