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