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