Remove IsHighDPIEnabled, move EnableHighDPISupport to only place it's used
[chromium-blink-merge.git] / net / dns / dns_config_service_win.cc
blobddb8a4c18138a12d1d029223e7bf2947a5191eae
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "net/dns/dns_config_service_win.h"
7 #include <algorithm>
8 #include <string>
10 #include "base/bind.h"
11 #include "base/callback.h"
12 #include "base/compiler_specific.h"
13 #include "base/files/file_path.h"
14 #include "base/files/file_path_watcher.h"
15 #include "base/logging.h"
16 #include "base/memory/scoped_ptr.h"
17 #include "base/metrics/histogram.h"
18 #include "base/profiler/scoped_profile.h"
19 #include "base/strings/string_split.h"
20 #include "base/strings/string_util.h"
21 #include "base/strings/utf_string_conversions.h"
22 #include "base/synchronization/lock.h"
23 #include "base/threading/non_thread_safe.h"
24 #include "base/threading/thread_restrictions.h"
25 #include "base/time/time.h"
26 #include "base/win/registry.h"
27 #include "base/win/scoped_handle.h"
28 #include "base/win/windows_version.h"
29 #include "net/base/net_util.h"
30 #include "net/base/network_change_notifier.h"
31 #include "net/dns/dns_hosts.h"
32 #include "net/dns/dns_protocol.h"
33 #include "net/dns/serial_worker.h"
34 #include "url/url_canon.h"
36 #pragma comment(lib, "iphlpapi.lib")
38 namespace net {
40 namespace internal {
42 namespace {
44 // Interval between retries to parse config. Used only until parsing succeeds.
45 const int kRetryIntervalSeconds = 5;
47 // Registry key paths.
48 const wchar_t* const kTcpipPath =
49 L"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters";
50 const wchar_t* const kTcpip6Path =
51 L"SYSTEM\\CurrentControlSet\\Services\\Tcpip6\\Parameters";
52 const wchar_t* const kDnscachePath =
53 L"SYSTEM\\CurrentControlSet\\Services\\Dnscache\\Parameters";
54 const wchar_t* const kPolicyPath =
55 L"SOFTWARE\\Policies\\Microsoft\\Windows NT\\DNSClient";
56 const wchar_t* const kPrimaryDnsSuffixPath =
57 L"SOFTWARE\\Policies\\Microsoft\\System\\DNSClient";
58 const wchar_t* const kNRPTPath =
59 L"SOFTWARE\\Policies\\Microsoft\\Windows NT\\DNSClient\\DnsPolicyConfig";
61 enum HostsParseWinResult {
62 HOSTS_PARSE_WIN_OK = 0,
63 HOSTS_PARSE_WIN_UNREADABLE_HOSTS_FILE,
64 HOSTS_PARSE_WIN_COMPUTER_NAME_FAILED,
65 HOSTS_PARSE_WIN_IPHELPER_FAILED,
66 HOSTS_PARSE_WIN_BAD_ADDRESS,
67 HOSTS_PARSE_WIN_MAX // Bounding values for enumeration.
70 // Convenience for reading values using RegKey.
71 class RegistryReader : public base::NonThreadSafe {
72 public:
73 explicit RegistryReader(const wchar_t* key) {
74 // Ignoring the result. |key_.Valid()| will catch failures.
75 key_.Open(HKEY_LOCAL_MACHINE, key, KEY_QUERY_VALUE);
78 bool ReadString(const wchar_t* name,
79 DnsSystemSettings::RegString* out) const {
80 DCHECK(CalledOnValidThread());
81 out->set = false;
82 if (!key_.Valid()) {
83 // Assume that if the |key_| is invalid then the key is missing.
84 return true;
86 LONG result = key_.ReadValue(name, &out->value);
87 if (result == ERROR_SUCCESS) {
88 out->set = true;
89 return true;
91 return (result == ERROR_FILE_NOT_FOUND);
94 bool ReadDword(const wchar_t* name,
95 DnsSystemSettings::RegDword* out) const {
96 DCHECK(CalledOnValidThread());
97 out->set = false;
98 if (!key_.Valid()) {
99 // Assume that if the |key_| is invalid then the key is missing.
100 return true;
102 LONG result = key_.ReadValueDW(name, &out->value);
103 if (result == ERROR_SUCCESS) {
104 out->set = true;
105 return true;
107 return (result == ERROR_FILE_NOT_FOUND);
110 private:
111 base::win::RegKey key_;
113 DISALLOW_COPY_AND_ASSIGN(RegistryReader);
116 // Wrapper for GetAdaptersAddresses. Returns NULL if failed.
117 scoped_ptr<IP_ADAPTER_ADDRESSES, base::FreeDeleter> ReadIpHelper(ULONG flags) {
118 base::ThreadRestrictions::AssertIOAllowed();
120 scoped_ptr<IP_ADAPTER_ADDRESSES, base::FreeDeleter> out;
121 ULONG len = 15000; // As recommended by MSDN for GetAdaptersAddresses.
122 UINT rv = ERROR_BUFFER_OVERFLOW;
123 // Try up to three times.
124 for (unsigned tries = 0; (tries < 3) && (rv == ERROR_BUFFER_OVERFLOW);
125 tries++) {
126 out.reset(static_cast<PIP_ADAPTER_ADDRESSES>(malloc(len)));
127 memset(out.get(), 0, len);
128 rv = GetAdaptersAddresses(AF_UNSPEC, flags, NULL, out.get(), &len);
130 if (rv != NO_ERROR)
131 out.reset();
132 return out.Pass();
135 // Converts a base::string16 domain name to ASCII, possibly using punycode.
136 // Returns true if the conversion succeeds and output is not empty. In case of
137 // failure, |domain| might become dirty.
138 bool ParseDomainASCII(const base::string16& widestr, std::string* domain) {
139 DCHECK(domain);
140 if (widestr.empty())
141 return false;
143 // Check if already ASCII.
144 if (base::IsStringASCII(widestr)) {
145 *domain = base::UTF16ToASCII(widestr);
146 return true;
149 // Otherwise try to convert it from IDN to punycode.
150 const int kInitialBufferSize = 256;
151 url::RawCanonOutputT<base::char16, kInitialBufferSize> punycode;
152 if (!url::IDNToASCII(widestr.data(), widestr.length(), &punycode))
153 return false;
155 // |punycode_output| should now be ASCII; convert it to a std::string.
156 // (We could use UTF16ToASCII() instead, but that requires an extra string
157 // copy. Since ASCII is a subset of UTF8 the following is equivalent).
158 bool success = base::UTF16ToUTF8(punycode.data(), punycode.length(), domain);
159 DCHECK(success);
160 DCHECK(base::IsStringASCII(*domain));
161 return success && !domain->empty();
164 bool ReadDevolutionSetting(const RegistryReader& reader,
165 DnsSystemSettings::DevolutionSetting* setting) {
166 return reader.ReadDword(L"UseDomainNameDevolution", &setting->enabled) &&
167 reader.ReadDword(L"DomainNameDevolutionLevel", &setting->level);
170 // Reads DnsSystemSettings from IpHelper and registry.
171 ConfigParseWinResult ReadSystemSettings(DnsSystemSettings* settings) {
172 settings->addresses = ReadIpHelper(GAA_FLAG_SKIP_ANYCAST |
173 GAA_FLAG_SKIP_UNICAST |
174 GAA_FLAG_SKIP_MULTICAST |
175 GAA_FLAG_SKIP_FRIENDLY_NAME);
176 if (!settings->addresses.get())
177 return CONFIG_PARSE_WIN_READ_IPHELPER;
179 RegistryReader tcpip_reader(kTcpipPath);
180 RegistryReader tcpip6_reader(kTcpip6Path);
181 RegistryReader dnscache_reader(kDnscachePath);
182 RegistryReader policy_reader(kPolicyPath);
183 RegistryReader primary_dns_suffix_reader(kPrimaryDnsSuffixPath);
185 if (!policy_reader.ReadString(L"SearchList",
186 &settings->policy_search_list)) {
187 return CONFIG_PARSE_WIN_READ_POLICY_SEARCHLIST;
190 if (!tcpip_reader.ReadString(L"SearchList", &settings->tcpip_search_list))
191 return CONFIG_PARSE_WIN_READ_TCPIP_SEARCHLIST;
193 if (!tcpip_reader.ReadString(L"Domain", &settings->tcpip_domain))
194 return CONFIG_PARSE_WIN_READ_DOMAIN;
196 if (!ReadDevolutionSetting(policy_reader, &settings->policy_devolution))
197 return CONFIG_PARSE_WIN_READ_POLICY_DEVOLUTION;
199 if (!ReadDevolutionSetting(dnscache_reader, &settings->dnscache_devolution))
200 return CONFIG_PARSE_WIN_READ_DNSCACHE_DEVOLUTION;
202 if (!ReadDevolutionSetting(tcpip_reader, &settings->tcpip_devolution))
203 return CONFIG_PARSE_WIN_READ_TCPIP_DEVOLUTION;
205 if (!policy_reader.ReadDword(L"AppendToMultiLabelName",
206 &settings->append_to_multi_label_name)) {
207 return CONFIG_PARSE_WIN_READ_APPEND_MULTILABEL;
210 if (!primary_dns_suffix_reader.ReadString(L"PrimaryDnsSuffix",
211 &settings->primary_dns_suffix)) {
212 return CONFIG_PARSE_WIN_READ_PRIMARY_SUFFIX;
215 base::win::RegistryKeyIterator nrpt_rules(HKEY_LOCAL_MACHINE, kNRPTPath);
216 settings->have_name_resolution_policy = (nrpt_rules.SubkeyCount() > 0);
218 return CONFIG_PARSE_WIN_OK;
221 // Default address of "localhost" and local computer name can be overridden
222 // by the HOSTS file, but if it's not there, then we need to fill it in.
223 HostsParseWinResult AddLocalhostEntries(DnsHosts* hosts) {
224 const unsigned char kIPv4Localhost[] = { 127, 0, 0, 1 };
225 const unsigned char kIPv6Localhost[] = { 0, 0, 0, 0, 0, 0, 0, 0,
226 0, 0, 0, 0, 0, 0, 0, 1 };
227 IPAddressNumber loopback_ipv4(kIPv4Localhost,
228 kIPv4Localhost + arraysize(kIPv4Localhost));
229 IPAddressNumber loopback_ipv6(kIPv6Localhost,
230 kIPv6Localhost + arraysize(kIPv6Localhost));
232 // This does not override any pre-existing entries from the HOSTS file.
233 hosts->insert(std::make_pair(DnsHostsKey("localhost", ADDRESS_FAMILY_IPV4),
234 loopback_ipv4));
235 hosts->insert(std::make_pair(DnsHostsKey("localhost", ADDRESS_FAMILY_IPV6),
236 loopback_ipv6));
238 WCHAR buffer[MAX_PATH];
239 DWORD size = MAX_PATH;
240 std::string localname;
241 if (!GetComputerNameExW(ComputerNameDnsHostname, buffer, &size) ||
242 !ParseDomainASCII(buffer, &localname)) {
243 return HOSTS_PARSE_WIN_COMPUTER_NAME_FAILED;
245 base::StringToLowerASCII(&localname);
247 bool have_ipv4 =
248 hosts->count(DnsHostsKey(localname, ADDRESS_FAMILY_IPV4)) > 0;
249 bool have_ipv6 =
250 hosts->count(DnsHostsKey(localname, ADDRESS_FAMILY_IPV6)) > 0;
252 if (have_ipv4 && have_ipv6)
253 return HOSTS_PARSE_WIN_OK;
255 scoped_ptr<IP_ADAPTER_ADDRESSES, base::FreeDeleter> addresses =
256 ReadIpHelper(GAA_FLAG_SKIP_ANYCAST |
257 GAA_FLAG_SKIP_DNS_SERVER |
258 GAA_FLAG_SKIP_MULTICAST |
259 GAA_FLAG_SKIP_FRIENDLY_NAME);
260 if (!addresses.get())
261 return HOSTS_PARSE_WIN_IPHELPER_FAILED;
263 // The order of adapters is the network binding order, so stick to the
264 // first good adapter for each family.
265 for (const IP_ADAPTER_ADDRESSES* adapter = addresses.get();
266 adapter != NULL && (!have_ipv4 || !have_ipv6);
267 adapter = adapter->Next) {
268 if (adapter->OperStatus != IfOperStatusUp)
269 continue;
270 if (adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK)
271 continue;
273 for (const IP_ADAPTER_UNICAST_ADDRESS* address =
274 adapter->FirstUnicastAddress;
275 address != NULL;
276 address = address->Next) {
277 IPEndPoint ipe;
278 if (!ipe.FromSockAddr(address->Address.lpSockaddr,
279 address->Address.iSockaddrLength)) {
280 return HOSTS_PARSE_WIN_BAD_ADDRESS;
282 if (!have_ipv4 && (ipe.GetFamily() == ADDRESS_FAMILY_IPV4)) {
283 have_ipv4 = true;
284 (*hosts)[DnsHostsKey(localname, ADDRESS_FAMILY_IPV4)] = ipe.address();
285 } else if (!have_ipv6 && (ipe.GetFamily() == ADDRESS_FAMILY_IPV6)) {
286 have_ipv6 = true;
287 (*hosts)[DnsHostsKey(localname, ADDRESS_FAMILY_IPV6)] = ipe.address();
291 return HOSTS_PARSE_WIN_OK;
294 // Watches a single registry key for changes.
295 class RegistryWatcher : public base::NonThreadSafe {
296 public:
297 typedef base::Callback<void(bool succeeded)> CallbackType;
298 RegistryWatcher() {}
300 bool Watch(const wchar_t* key, const CallbackType& callback) {
301 DCHECK(CalledOnValidThread());
302 DCHECK(!callback.is_null());
303 DCHECK(callback_.is_null());
304 callback_ = callback;
305 if (key_.Open(HKEY_LOCAL_MACHINE, key, KEY_NOTIFY) != ERROR_SUCCESS)
306 return false;
308 return key_.StartWatching(base::Bind(&RegistryWatcher::OnObjectSignaled,
309 base::Unretained(this)));
312 void OnObjectSignaled() {
313 // TODO(vadimt): Remove ScopedProfile below once crbug.com/418183 is fixed.
314 tracked_objects::ScopedProfile tracking_profile(
315 FROM_HERE_WITH_EXPLICIT_FUNCTION(
316 "RegistryWatcher_OnObjectSignaled"));
318 DCHECK(CalledOnValidThread());
319 DCHECK(!callback_.is_null());
320 if (key_.StartWatching(base::Bind(&RegistryWatcher::OnObjectSignaled,
321 base::Unretained(this)))) {
322 callback_.Run(true);
323 } else {
324 key_.Close();
325 callback_.Run(false);
329 private:
330 CallbackType callback_;
331 base::win::RegKey key_;
333 DISALLOW_COPY_AND_ASSIGN(RegistryWatcher);
336 // Returns true iff |address| is DNS address from IPv6 stateless discovery,
337 // i.e., matches fec0:0:0:ffff::{1,2,3}.
338 // http://tools.ietf.org/html/draft-ietf-ipngwg-dns-discovery
339 bool IsStatelessDiscoveryAddress(const IPAddressNumber& address) {
340 if (address.size() != kIPv6AddressSize)
341 return false;
342 const uint8 kPrefix[] = {
343 0xfe, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
344 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
346 return std::equal(kPrefix, kPrefix + arraysize(kPrefix),
347 address.begin()) && (address.back() < 4);
350 // Returns the path to the HOSTS file.
351 base::FilePath GetHostsPath() {
352 TCHAR buffer[MAX_PATH];
353 UINT rc = GetSystemDirectory(buffer, MAX_PATH);
354 DCHECK(0 < rc && rc < MAX_PATH);
355 return base::FilePath(buffer).Append(
356 FILE_PATH_LITERAL("drivers\\etc\\hosts"));
359 void ConfigureSuffixSearch(const DnsSystemSettings& settings,
360 DnsConfig* config) {
361 // SearchList takes precedence, so check it first.
362 if (settings.policy_search_list.set) {
363 std::vector<std::string> search;
364 if (ParseSearchList(settings.policy_search_list.value, &search)) {
365 config->search.swap(search);
366 return;
368 // Even if invalid, the policy disables the user-specified setting below.
369 } else if (settings.tcpip_search_list.set) {
370 std::vector<std::string> search;
371 if (ParseSearchList(settings.tcpip_search_list.value, &search)) {
372 config->search.swap(search);
373 return;
377 // In absence of explicit search list, suffix search is:
378 // [primary suffix, connection-specific suffix, devolution of primary suffix].
379 // Primary suffix can be set by policy (primary_dns_suffix) or
380 // user setting (tcpip_domain).
382 // The policy (primary_dns_suffix) can be edited via Group Policy Editor
383 // (gpedit.msc) at Local Computer Policy => Computer Configuration
384 // => Administrative Template => Network => DNS Client => Primary DNS Suffix.
386 // The user setting (tcpip_domain) can be configurred at Computer Name in
387 // System Settings
388 std::string primary_suffix;
389 if ((settings.primary_dns_suffix.set &&
390 ParseDomainASCII(settings.primary_dns_suffix.value, &primary_suffix)) ||
391 (settings.tcpip_domain.set &&
392 ParseDomainASCII(settings.tcpip_domain.value, &primary_suffix))) {
393 // Primary suffix goes in front.
394 config->search.insert(config->search.begin(), primary_suffix);
395 } else {
396 return; // No primary suffix, hence no devolution.
399 // Devolution is determined by precedence: policy > dnscache > tcpip.
400 // |enabled|: UseDomainNameDevolution and |level|: DomainNameDevolutionLevel
401 // are overridden independently.
402 DnsSystemSettings::DevolutionSetting devolution = settings.policy_devolution;
404 if (!devolution.enabled.set)
405 devolution.enabled = settings.dnscache_devolution.enabled;
406 if (!devolution.enabled.set)
407 devolution.enabled = settings.tcpip_devolution.enabled;
408 if (devolution.enabled.set && (devolution.enabled.value == 0))
409 return; // Devolution disabled.
411 // By default devolution is enabled.
413 if (!devolution.level.set)
414 devolution.level = settings.dnscache_devolution.level;
415 if (!devolution.level.set)
416 devolution.level = settings.tcpip_devolution.level;
418 // After the recent update, Windows will try to determine a safe default
419 // value by comparing the forest root domain (FRD) to the primary suffix.
420 // See http://support.microsoft.com/kb/957579 for details.
421 // For now, if the level is not set, we disable devolution, assuming that
422 // we will fallback to the system getaddrinfo anyway. This might cause
423 // performance loss for resolutions which depend on the system default
424 // devolution setting.
426 // If the level is explicitly set below 2, devolution is disabled.
427 if (!devolution.level.set || devolution.level.value < 2)
428 return; // Devolution disabled.
430 // Devolve the primary suffix. This naive logic matches the observed
431 // behavior (see also ParseSearchList). If a suffix is not valid, it will be
432 // discarded when the fully-qualified name is converted to DNS format.
434 unsigned num_dots = std::count(primary_suffix.begin(),
435 primary_suffix.end(), '.');
437 for (size_t offset = 0; num_dots >= devolution.level.value; --num_dots) {
438 offset = primary_suffix.find('.', offset + 1);
439 config->search.push_back(primary_suffix.substr(offset + 1));
443 } // namespace
445 bool ParseSearchList(const base::string16& value,
446 std::vector<std::string>* output) {
447 DCHECK(output);
448 if (value.empty())
449 return false;
451 output->clear();
453 // If the list includes an empty hostname (",," or ", ,"), it is terminated.
454 // Although nslookup and network connection property tab ignore such
455 // fragments ("a,b,,c" becomes ["a", "b", "c"]), our reference is getaddrinfo
456 // (which sees ["a", "b"]). WMI queries also return a matching search list.
457 std::vector<base::string16> woutput;
458 base::SplitString(value, ',', &woutput);
459 for (size_t i = 0; i < woutput.size(); ++i) {
460 // Convert non-ASCII to punycode, although getaddrinfo does not properly
461 // handle such suffixes.
462 const base::string16& t = woutput[i];
463 std::string parsed;
464 if (!ParseDomainASCII(t, &parsed))
465 break;
466 output->push_back(parsed);
468 return !output->empty();
471 ConfigParseWinResult ConvertSettingsToDnsConfig(
472 const DnsSystemSettings& settings,
473 DnsConfig* config) {
474 *config = DnsConfig();
476 // Use GetAdapterAddresses to get effective DNS server order and
477 // connection-specific DNS suffix. Ignore disconnected and loopback adapters.
478 // The order of adapters is the network binding order, so stick to the
479 // first good adapter.
480 for (const IP_ADAPTER_ADDRESSES* adapter = settings.addresses.get();
481 adapter != NULL && config->nameservers.empty();
482 adapter = adapter->Next) {
483 if (adapter->OperStatus != IfOperStatusUp)
484 continue;
485 if (adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK)
486 continue;
488 for (const IP_ADAPTER_DNS_SERVER_ADDRESS* address =
489 adapter->FirstDnsServerAddress;
490 address != NULL;
491 address = address->Next) {
492 IPEndPoint ipe;
493 if (ipe.FromSockAddr(address->Address.lpSockaddr,
494 address->Address.iSockaddrLength)) {
495 if (IsStatelessDiscoveryAddress(ipe.address()))
496 continue;
497 // Override unset port.
498 if (!ipe.port())
499 ipe = IPEndPoint(ipe.address(), dns_protocol::kDefaultPort);
500 config->nameservers.push_back(ipe);
501 } else {
502 return CONFIG_PARSE_WIN_BAD_ADDRESS;
506 // IP_ADAPTER_ADDRESSES in Vista+ has a search list at |FirstDnsSuffix|,
507 // but it came up empty in all trials.
508 // |DnsSuffix| stores the effective connection-specific suffix, which is
509 // obtained via DHCP (regkey: Tcpip\Parameters\Interfaces\{XXX}\DhcpDomain)
510 // or specified by the user (regkey: Tcpip\Parameters\Domain).
511 std::string dns_suffix;
512 if (ParseDomainASCII(adapter->DnsSuffix, &dns_suffix))
513 config->search.push_back(dns_suffix);
516 if (config->nameservers.empty())
517 return CONFIG_PARSE_WIN_NO_NAMESERVERS; // No point continuing.
519 // Windows always tries a multi-label name "as is" before using suffixes.
520 config->ndots = 1;
522 if (!settings.append_to_multi_label_name.set) {
523 // The default setting is true for XP, false for Vista+.
524 if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
525 config->append_to_multi_label_name = false;
526 } else {
527 config->append_to_multi_label_name = true;
529 } else {
530 config->append_to_multi_label_name =
531 (settings.append_to_multi_label_name.value != 0);
534 ConfigParseWinResult result = CONFIG_PARSE_WIN_OK;
535 if (settings.have_name_resolution_policy) {
536 config->unhandled_options = true;
537 // TODO(szym): only set this to true if NRPT has DirectAccess rules.
538 config->use_local_ipv6 = true;
539 result = CONFIG_PARSE_WIN_UNHANDLED_OPTIONS;
542 ConfigureSuffixSearch(settings, config);
543 return result;
546 // Watches registry and HOSTS file for changes. Must live on a thread which
547 // allows IO.
548 class DnsConfigServiceWin::Watcher
549 : public NetworkChangeNotifier::IPAddressObserver {
550 public:
551 explicit Watcher(DnsConfigServiceWin* service) : service_(service) {}
552 ~Watcher() {
553 NetworkChangeNotifier::RemoveIPAddressObserver(this);
556 bool Watch() {
557 RegistryWatcher::CallbackType callback =
558 base::Bind(&DnsConfigServiceWin::OnConfigChanged,
559 base::Unretained(service_));
561 bool success = true;
563 // The Tcpip key must be present.
564 if (!tcpip_watcher_.Watch(kTcpipPath, callback)) {
565 LOG(ERROR) << "DNS registry watch failed to start.";
566 success = false;
567 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus",
568 DNS_CONFIG_WATCH_FAILED_TO_START_CONFIG,
569 DNS_CONFIG_WATCH_MAX);
572 // Watch for IPv6 nameservers.
573 tcpip6_watcher_.Watch(kTcpip6Path, callback);
575 // DNS suffix search list and devolution can be configured via group
576 // policy which sets this registry key. If the key is missing, the policy
577 // does not apply, and the DNS client uses Tcpip and Dnscache settings.
578 // If a policy is installed, DnsConfigService will need to be restarted.
579 // BUG=99509
581 dnscache_watcher_.Watch(kDnscachePath, callback);
582 policy_watcher_.Watch(kPolicyPath, callback);
584 if (!hosts_watcher_.Watch(GetHostsPath(), false,
585 base::Bind(&Watcher::OnHostsChanged,
586 base::Unretained(this)))) {
587 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus",
588 DNS_CONFIG_WATCH_FAILED_TO_START_HOSTS,
589 DNS_CONFIG_WATCH_MAX);
590 LOG(ERROR) << "DNS hosts watch failed to start.";
591 success = false;
592 } else {
593 // Also need to observe changes to local non-loopback IP for DnsHosts.
594 NetworkChangeNotifier::AddIPAddressObserver(this);
596 return success;
599 private:
600 void OnHostsChanged(const base::FilePath& path, bool error) {
601 if (error)
602 NetworkChangeNotifier::RemoveIPAddressObserver(this);
603 service_->OnHostsChanged(!error);
606 // NetworkChangeNotifier::IPAddressObserver:
607 virtual void OnIPAddressChanged() override {
608 // Need to update non-loopback IP of local host.
609 service_->OnHostsChanged(true);
612 DnsConfigServiceWin* service_;
614 RegistryWatcher tcpip_watcher_;
615 RegistryWatcher tcpip6_watcher_;
616 RegistryWatcher dnscache_watcher_;
617 RegistryWatcher policy_watcher_;
618 base::FilePathWatcher hosts_watcher_;
620 DISALLOW_COPY_AND_ASSIGN(Watcher);
623 // Reads config from registry and IpHelper. All work performed on WorkerPool.
624 class DnsConfigServiceWin::ConfigReader : public SerialWorker {
625 public:
626 explicit ConfigReader(DnsConfigServiceWin* service)
627 : service_(service),
628 success_(false) {}
630 private:
631 virtual ~ConfigReader() {}
633 virtual void DoWork() override {
634 // Should be called on WorkerPool.
635 base::TimeTicks start_time = base::TimeTicks::Now();
636 DnsSystemSettings settings = {};
637 ConfigParseWinResult result = ReadSystemSettings(&settings);
638 if (result == CONFIG_PARSE_WIN_OK)
639 result = ConvertSettingsToDnsConfig(settings, &dns_config_);
640 success_ = (result == CONFIG_PARSE_WIN_OK ||
641 result == CONFIG_PARSE_WIN_UNHANDLED_OPTIONS);
642 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.ConfigParseWin",
643 result, CONFIG_PARSE_WIN_MAX);
644 UMA_HISTOGRAM_BOOLEAN("AsyncDNS.ConfigParseResult", success_);
645 UMA_HISTOGRAM_TIMES("AsyncDNS.ConfigParseDuration",
646 base::TimeTicks::Now() - start_time);
649 virtual void OnWorkFinished() override {
650 DCHECK(loop()->BelongsToCurrentThread());
651 DCHECK(!IsCancelled());
652 if (success_) {
653 service_->OnConfigRead(dns_config_);
654 } else {
655 LOG(WARNING) << "Failed to read DnsConfig.";
656 // Try again in a while in case DnsConfigWatcher missed the signal.
657 base::MessageLoop::current()->PostDelayedTask(
658 FROM_HERE,
659 base::Bind(&ConfigReader::WorkNow, this),
660 base::TimeDelta::FromSeconds(kRetryIntervalSeconds));
664 DnsConfigServiceWin* service_;
665 // Written in DoWork(), read in OnWorkFinished(). No locking required.
666 DnsConfig dns_config_;
667 bool success_;
670 // Reads hosts from HOSTS file and fills in localhost and local computer name if
671 // necessary. All work performed on WorkerPool.
672 class DnsConfigServiceWin::HostsReader : public SerialWorker {
673 public:
674 explicit HostsReader(DnsConfigServiceWin* service)
675 : path_(GetHostsPath()),
676 service_(service),
677 success_(false) {
680 private:
681 virtual ~HostsReader() {}
683 virtual void DoWork() override {
684 base::TimeTicks start_time = base::TimeTicks::Now();
685 HostsParseWinResult result = HOSTS_PARSE_WIN_UNREADABLE_HOSTS_FILE;
686 if (ParseHostsFile(path_, &hosts_))
687 result = AddLocalhostEntries(&hosts_);
688 success_ = (result == HOSTS_PARSE_WIN_OK);
689 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.HostsParseWin",
690 result, HOSTS_PARSE_WIN_MAX);
691 UMA_HISTOGRAM_BOOLEAN("AsyncDNS.HostParseResult", success_);
692 UMA_HISTOGRAM_TIMES("AsyncDNS.HostsParseDuration",
693 base::TimeTicks::Now() - start_time);
696 virtual void OnWorkFinished() override {
697 DCHECK(loop()->BelongsToCurrentThread());
698 if (success_) {
699 service_->OnHostsRead(hosts_);
700 } else {
701 LOG(WARNING) << "Failed to read DnsHosts.";
705 const base::FilePath path_;
706 DnsConfigServiceWin* service_;
707 // Written in DoWork, read in OnWorkFinished, no locking necessary.
708 DnsHosts hosts_;
709 bool success_;
711 DISALLOW_COPY_AND_ASSIGN(HostsReader);
714 DnsConfigServiceWin::DnsConfigServiceWin()
715 : config_reader_(new ConfigReader(this)),
716 hosts_reader_(new HostsReader(this)) {}
718 DnsConfigServiceWin::~DnsConfigServiceWin() {
719 config_reader_->Cancel();
720 hosts_reader_->Cancel();
723 void DnsConfigServiceWin::ReadNow() {
724 config_reader_->WorkNow();
725 hosts_reader_->WorkNow();
728 bool DnsConfigServiceWin::StartWatching() {
729 // TODO(szym): re-start watcher if that makes sense. http://crbug.com/116139
730 watcher_.reset(new Watcher(this));
731 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus", DNS_CONFIG_WATCH_STARTED,
732 DNS_CONFIG_WATCH_MAX);
733 return watcher_->Watch();
736 void DnsConfigServiceWin::OnConfigChanged(bool succeeded) {
737 InvalidateConfig();
738 config_reader_->WorkNow();
739 if (!succeeded) {
740 LOG(ERROR) << "DNS config watch failed.";
741 set_watch_failed(true);
742 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus",
743 DNS_CONFIG_WATCH_FAILED_CONFIG,
744 DNS_CONFIG_WATCH_MAX);
748 void DnsConfigServiceWin::OnHostsChanged(bool succeeded) {
749 InvalidateHosts();
750 if (succeeded) {
751 hosts_reader_->WorkNow();
752 } else {
753 LOG(ERROR) << "DNS hosts watch failed.";
754 set_watch_failed(true);
755 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus",
756 DNS_CONFIG_WATCH_FAILED_HOSTS,
757 DNS_CONFIG_WATCH_MAX);
761 } // namespace internal
763 // static
764 scoped_ptr<DnsConfigService> DnsConfigService::CreateSystemService() {
765 return scoped_ptr<DnsConfigService>(new internal::DnsConfigServiceWin());
768 } // namespace net