Fix uninitialized reads in DnsConfigServiceWin tests.
[chromium-blink-merge.git] / net / dns / dns_config_service_win.cc
blobdb7af982aa54dcf35a40ac4b52bacbe4b5e4441f
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/strings/string_split.h"
19 #include "base/strings/string_util.h"
20 #include "base/strings/utf_string_conversions.h"
21 #include "base/synchronization/lock.h"
22 #include "base/threading/non_thread_safe.h"
23 #include "base/threading/thread_restrictions.h"
24 #include "base/time/time.h"
25 #include "base/win/registry.h"
26 #include "base/win/scoped_handle.h"
27 #include "base/win/windows_version.h"
28 #include "net/base/net_util.h"
29 #include "net/base/network_change_notifier.h"
30 #include "net/dns/dns_hosts.h"
31 #include "net/dns/dns_protocol.h"
32 #include "net/dns/serial_worker.h"
33 #include "url/url_canon.h"
35 #pragma comment(lib, "iphlpapi.lib")
37 namespace net {
39 namespace internal {
41 namespace {
43 // Interval between retries to parse config. Used only until parsing succeeds.
44 const int kRetryIntervalSeconds = 5;
46 // Registry key paths.
47 const wchar_t* const kTcpipPath =
48 L"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters";
49 const wchar_t* const kTcpip6Path =
50 L"SYSTEM\\CurrentControlSet\\Services\\Tcpip6\\Parameters";
51 const wchar_t* const kDnscachePath =
52 L"SYSTEM\\CurrentControlSet\\Services\\Dnscache\\Parameters";
53 const wchar_t* const kPolicyPath =
54 L"SOFTWARE\\Policies\\Microsoft\\Windows NT\\DNSClient";
55 const wchar_t* const kPrimaryDnsSuffixPath =
56 L"SOFTWARE\\Policies\\Microsoft\\System\\DNSClient";
57 const wchar_t* const kNRPTPath =
58 L"SOFTWARE\\Policies\\Microsoft\\Windows NT\\DNSClient\\DnsPolicyConfig";
60 enum HostsParseWinResult {
61 HOSTS_PARSE_WIN_OK = 0,
62 HOSTS_PARSE_WIN_UNREADABLE_HOSTS_FILE,
63 HOSTS_PARSE_WIN_COMPUTER_NAME_FAILED,
64 HOSTS_PARSE_WIN_IPHELPER_FAILED,
65 HOSTS_PARSE_WIN_BAD_ADDRESS,
66 HOSTS_PARSE_WIN_MAX // Bounding values for enumeration.
69 // Convenience for reading values using RegKey.
70 class RegistryReader : public base::NonThreadSafe {
71 public:
72 explicit RegistryReader(const wchar_t* key) {
73 // Ignoring the result. |key_.Valid()| will catch failures.
74 key_.Open(HKEY_LOCAL_MACHINE, key, KEY_QUERY_VALUE);
77 bool ReadString(const wchar_t* name,
78 DnsSystemSettings::RegString* out) const {
79 DCHECK(CalledOnValidThread());
80 out->set = false;
81 if (!key_.Valid()) {
82 // Assume that if the |key_| is invalid then the key is missing.
83 return true;
85 LONG result = key_.ReadValue(name, &out->value);
86 if (result == ERROR_SUCCESS) {
87 out->set = true;
88 return true;
90 return (result == ERROR_FILE_NOT_FOUND);
93 bool ReadDword(const wchar_t* name,
94 DnsSystemSettings::RegDword* out) const {
95 DCHECK(CalledOnValidThread());
96 out->set = false;
97 if (!key_.Valid()) {
98 // Assume that if the |key_| is invalid then the key is missing.
99 return true;
101 LONG result = key_.ReadValueDW(name, &out->value);
102 if (result == ERROR_SUCCESS) {
103 out->set = true;
104 return true;
106 return (result == ERROR_FILE_NOT_FOUND);
109 private:
110 base::win::RegKey key_;
112 DISALLOW_COPY_AND_ASSIGN(RegistryReader);
115 // Wrapper for GetAdaptersAddresses. Returns NULL if failed.
116 scoped_ptr<IP_ADAPTER_ADDRESSES, base::FreeDeleter> ReadIpHelper(ULONG flags) {
117 base::ThreadRestrictions::AssertIOAllowed();
119 scoped_ptr<IP_ADAPTER_ADDRESSES, base::FreeDeleter> out;
120 ULONG len = 15000; // As recommended by MSDN for GetAdaptersAddresses.
121 UINT rv = ERROR_BUFFER_OVERFLOW;
122 // Try up to three times.
123 for (unsigned tries = 0; (tries < 3) && (rv == ERROR_BUFFER_OVERFLOW);
124 tries++) {
125 out.reset(static_cast<PIP_ADAPTER_ADDRESSES>(malloc(len)));
126 memset(out.get(), 0, len);
127 rv = GetAdaptersAddresses(AF_UNSPEC, flags, NULL, out.get(), &len);
129 if (rv != NO_ERROR)
130 out.reset();
131 return out.Pass();
134 // Converts a base::string16 domain name to ASCII, possibly using punycode.
135 // Returns true if the conversion succeeds and output is not empty. In case of
136 // failure, |domain| might become dirty.
137 bool ParseDomainASCII(const base::string16& widestr, std::string* domain) {
138 DCHECK(domain);
139 if (widestr.empty())
140 return false;
142 // Check if already ASCII.
143 if (base::IsStringASCII(widestr)) {
144 *domain = base::UTF16ToASCII(widestr);
145 return true;
148 // Otherwise try to convert it from IDN to punycode.
149 const int kInitialBufferSize = 256;
150 url::RawCanonOutputT<base::char16, kInitialBufferSize> punycode;
151 if (!url::IDNToASCII(widestr.data(), widestr.length(), &punycode))
152 return false;
154 // |punycode_output| should now be ASCII; convert it to a std::string.
155 // (We could use UTF16ToASCII() instead, but that requires an extra string
156 // copy. Since ASCII is a subset of UTF8 the following is equivalent).
157 bool success = base::UTF16ToUTF8(punycode.data(), punycode.length(), domain);
158 DCHECK(success);
159 DCHECK(base::IsStringASCII(*domain));
160 return success && !domain->empty();
163 bool ReadDevolutionSetting(const RegistryReader& reader,
164 DnsSystemSettings::DevolutionSetting* setting) {
165 return reader.ReadDword(L"UseDomainNameDevolution", &setting->enabled) &&
166 reader.ReadDword(L"DomainNameDevolutionLevel", &setting->level);
169 // Reads DnsSystemSettings from IpHelper and registry.
170 ConfigParseWinResult ReadSystemSettings(DnsSystemSettings* settings) {
171 settings->addresses = ReadIpHelper(GAA_FLAG_SKIP_ANYCAST |
172 GAA_FLAG_SKIP_UNICAST |
173 GAA_FLAG_SKIP_MULTICAST |
174 GAA_FLAG_SKIP_FRIENDLY_NAME);
175 if (!settings->addresses.get())
176 return CONFIG_PARSE_WIN_READ_IPHELPER;
178 RegistryReader tcpip_reader(kTcpipPath);
179 RegistryReader tcpip6_reader(kTcpip6Path);
180 RegistryReader dnscache_reader(kDnscachePath);
181 RegistryReader policy_reader(kPolicyPath);
182 RegistryReader primary_dns_suffix_reader(kPrimaryDnsSuffixPath);
184 if (!policy_reader.ReadString(L"SearchList",
185 &settings->policy_search_list)) {
186 return CONFIG_PARSE_WIN_READ_POLICY_SEARCHLIST;
189 if (!tcpip_reader.ReadString(L"SearchList", &settings->tcpip_search_list))
190 return CONFIG_PARSE_WIN_READ_TCPIP_SEARCHLIST;
192 if (!tcpip_reader.ReadString(L"Domain", &settings->tcpip_domain))
193 return CONFIG_PARSE_WIN_READ_DOMAIN;
195 if (!ReadDevolutionSetting(policy_reader, &settings->policy_devolution))
196 return CONFIG_PARSE_WIN_READ_POLICY_DEVOLUTION;
198 if (!ReadDevolutionSetting(dnscache_reader, &settings->dnscache_devolution))
199 return CONFIG_PARSE_WIN_READ_DNSCACHE_DEVOLUTION;
201 if (!ReadDevolutionSetting(tcpip_reader, &settings->tcpip_devolution))
202 return CONFIG_PARSE_WIN_READ_TCPIP_DEVOLUTION;
204 if (!policy_reader.ReadDword(L"AppendToMultiLabelName",
205 &settings->append_to_multi_label_name)) {
206 return CONFIG_PARSE_WIN_READ_APPEND_MULTILABEL;
209 if (!primary_dns_suffix_reader.ReadString(L"PrimaryDnsSuffix",
210 &settings->primary_dns_suffix)) {
211 return CONFIG_PARSE_WIN_READ_PRIMARY_SUFFIX;
214 base::win::RegistryKeyIterator nrpt_rules(HKEY_LOCAL_MACHINE, kNRPTPath);
215 settings->have_name_resolution_policy = (nrpt_rules.SubkeyCount() > 0);
217 return CONFIG_PARSE_WIN_OK;
220 // Default address of "localhost" and local computer name can be overridden
221 // by the HOSTS file, but if it's not there, then we need to fill it in.
222 HostsParseWinResult AddLocalhostEntries(DnsHosts* hosts) {
223 const unsigned char kIPv4Localhost[] = { 127, 0, 0, 1 };
224 const unsigned char kIPv6Localhost[] = { 0, 0, 0, 0, 0, 0, 0, 0,
225 0, 0, 0, 0, 0, 0, 0, 1 };
226 IPAddressNumber loopback_ipv4(kIPv4Localhost,
227 kIPv4Localhost + arraysize(kIPv4Localhost));
228 IPAddressNumber loopback_ipv6(kIPv6Localhost,
229 kIPv6Localhost + arraysize(kIPv6Localhost));
231 // This does not override any pre-existing entries from the HOSTS file.
232 hosts->insert(std::make_pair(DnsHostsKey("localhost", ADDRESS_FAMILY_IPV4),
233 loopback_ipv4));
234 hosts->insert(std::make_pair(DnsHostsKey("localhost", ADDRESS_FAMILY_IPV6),
235 loopback_ipv6));
237 WCHAR buffer[MAX_PATH];
238 DWORD size = MAX_PATH;
239 std::string localname;
240 if (!GetComputerNameExW(ComputerNameDnsHostname, buffer, &size) ||
241 !ParseDomainASCII(buffer, &localname)) {
242 return HOSTS_PARSE_WIN_COMPUTER_NAME_FAILED;
244 base::StringToLowerASCII(&localname);
246 bool have_ipv4 =
247 hosts->count(DnsHostsKey(localname, ADDRESS_FAMILY_IPV4)) > 0;
248 bool have_ipv6 =
249 hosts->count(DnsHostsKey(localname, ADDRESS_FAMILY_IPV6)) > 0;
251 if (have_ipv4 && have_ipv6)
252 return HOSTS_PARSE_WIN_OK;
254 scoped_ptr<IP_ADAPTER_ADDRESSES, base::FreeDeleter> addresses =
255 ReadIpHelper(GAA_FLAG_SKIP_ANYCAST |
256 GAA_FLAG_SKIP_DNS_SERVER |
257 GAA_FLAG_SKIP_MULTICAST |
258 GAA_FLAG_SKIP_FRIENDLY_NAME);
259 if (!addresses.get())
260 return HOSTS_PARSE_WIN_IPHELPER_FAILED;
262 // The order of adapters is the network binding order, so stick to the
263 // first good adapter for each family.
264 for (const IP_ADAPTER_ADDRESSES* adapter = addresses.get();
265 adapter != NULL && (!have_ipv4 || !have_ipv6);
266 adapter = adapter->Next) {
267 if (adapter->OperStatus != IfOperStatusUp)
268 continue;
269 if (adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK)
270 continue;
272 for (const IP_ADAPTER_UNICAST_ADDRESS* address =
273 adapter->FirstUnicastAddress;
274 address != NULL;
275 address = address->Next) {
276 IPEndPoint ipe;
277 if (!ipe.FromSockAddr(address->Address.lpSockaddr,
278 address->Address.iSockaddrLength)) {
279 return HOSTS_PARSE_WIN_BAD_ADDRESS;
281 if (!have_ipv4 && (ipe.GetFamily() == ADDRESS_FAMILY_IPV4)) {
282 have_ipv4 = true;
283 (*hosts)[DnsHostsKey(localname, ADDRESS_FAMILY_IPV4)] = ipe.address();
284 } else if (!have_ipv6 && (ipe.GetFamily() == ADDRESS_FAMILY_IPV6)) {
285 have_ipv6 = true;
286 (*hosts)[DnsHostsKey(localname, ADDRESS_FAMILY_IPV6)] = ipe.address();
290 return HOSTS_PARSE_WIN_OK;
293 // Watches a single registry key for changes.
294 class RegistryWatcher : public base::NonThreadSafe {
295 public:
296 typedef base::Callback<void(bool succeeded)> CallbackType;
297 RegistryWatcher() {}
299 bool Watch(const wchar_t* key, const CallbackType& callback) {
300 DCHECK(CalledOnValidThread());
301 DCHECK(!callback.is_null());
302 DCHECK(callback_.is_null());
303 callback_ = callback;
304 if (key_.Open(HKEY_LOCAL_MACHINE, key, KEY_NOTIFY) != ERROR_SUCCESS)
305 return false;
307 return key_.StartWatching(base::Bind(&RegistryWatcher::OnObjectSignaled,
308 base::Unretained(this)));
311 void OnObjectSignaled() {
312 DCHECK(CalledOnValidThread());
313 DCHECK(!callback_.is_null());
314 if (key_.StartWatching(base::Bind(&RegistryWatcher::OnObjectSignaled,
315 base::Unretained(this)))) {
316 callback_.Run(true);
317 } else {
318 key_.Close();
319 callback_.Run(false);
323 private:
324 CallbackType callback_;
325 base::win::RegKey key_;
327 DISALLOW_COPY_AND_ASSIGN(RegistryWatcher);
330 // Returns true iff |address| is DNS address from IPv6 stateless discovery,
331 // i.e., matches fec0:0:0:ffff::{1,2,3}.
332 // http://tools.ietf.org/html/draft-ietf-ipngwg-dns-discovery
333 bool IsStatelessDiscoveryAddress(const IPAddressNumber& address) {
334 if (address.size() != kIPv6AddressSize)
335 return false;
336 const uint8 kPrefix[] = {
337 0xfe, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
338 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
340 return std::equal(kPrefix, kPrefix + arraysize(kPrefix),
341 address.begin()) && (address.back() < 4);
344 // Returns the path to the HOSTS file.
345 base::FilePath GetHostsPath() {
346 TCHAR buffer[MAX_PATH];
347 UINT rc = GetSystemDirectory(buffer, MAX_PATH);
348 DCHECK(0 < rc && rc < MAX_PATH);
349 return base::FilePath(buffer).Append(
350 FILE_PATH_LITERAL("drivers\\etc\\hosts"));
353 void ConfigureSuffixSearch(const DnsSystemSettings& settings,
354 DnsConfig* config) {
355 // SearchList takes precedence, so check it first.
356 if (settings.policy_search_list.set) {
357 std::vector<std::string> search;
358 if (ParseSearchList(settings.policy_search_list.value, &search)) {
359 config->search.swap(search);
360 return;
362 // Even if invalid, the policy disables the user-specified setting below.
363 } else if (settings.tcpip_search_list.set) {
364 std::vector<std::string> search;
365 if (ParseSearchList(settings.tcpip_search_list.value, &search)) {
366 config->search.swap(search);
367 return;
371 // In absence of explicit search list, suffix search is:
372 // [primary suffix, connection-specific suffix, devolution of primary suffix].
373 // Primary suffix can be set by policy (primary_dns_suffix) or
374 // user setting (tcpip_domain).
376 // The policy (primary_dns_suffix) can be edited via Group Policy Editor
377 // (gpedit.msc) at Local Computer Policy => Computer Configuration
378 // => Administrative Template => Network => DNS Client => Primary DNS Suffix.
380 // The user setting (tcpip_domain) can be configurred at Computer Name in
381 // System Settings
382 std::string primary_suffix;
383 if ((settings.primary_dns_suffix.set &&
384 ParseDomainASCII(settings.primary_dns_suffix.value, &primary_suffix)) ||
385 (settings.tcpip_domain.set &&
386 ParseDomainASCII(settings.tcpip_domain.value, &primary_suffix))) {
387 // Primary suffix goes in front.
388 config->search.insert(config->search.begin(), primary_suffix);
389 } else {
390 return; // No primary suffix, hence no devolution.
393 // Devolution is determined by precedence: policy > dnscache > tcpip.
394 // |enabled|: UseDomainNameDevolution and |level|: DomainNameDevolutionLevel
395 // are overridden independently.
396 DnsSystemSettings::DevolutionSetting devolution = settings.policy_devolution;
398 if (!devolution.enabled.set)
399 devolution.enabled = settings.dnscache_devolution.enabled;
400 if (!devolution.enabled.set)
401 devolution.enabled = settings.tcpip_devolution.enabled;
402 if (devolution.enabled.set && (devolution.enabled.value == 0))
403 return; // Devolution disabled.
405 // By default devolution is enabled.
407 if (!devolution.level.set)
408 devolution.level = settings.dnscache_devolution.level;
409 if (!devolution.level.set)
410 devolution.level = settings.tcpip_devolution.level;
412 // After the recent update, Windows will try to determine a safe default
413 // value by comparing the forest root domain (FRD) to the primary suffix.
414 // See http://support.microsoft.com/kb/957579 for details.
415 // For now, if the level is not set, we disable devolution, assuming that
416 // we will fallback to the system getaddrinfo anyway. This might cause
417 // performance loss for resolutions which depend on the system default
418 // devolution setting.
420 // If the level is explicitly set below 2, devolution is disabled.
421 if (!devolution.level.set || devolution.level.value < 2)
422 return; // Devolution disabled.
424 // Devolve the primary suffix. This naive logic matches the observed
425 // behavior (see also ParseSearchList). If a suffix is not valid, it will be
426 // discarded when the fully-qualified name is converted to DNS format.
428 unsigned num_dots = std::count(primary_suffix.begin(),
429 primary_suffix.end(), '.');
431 for (size_t offset = 0; num_dots >= devolution.level.value; --num_dots) {
432 offset = primary_suffix.find('.', offset + 1);
433 config->search.push_back(primary_suffix.substr(offset + 1));
437 } // namespace
439 DnsSystemSettings::DnsSystemSettings()
440 : policy_search_list(),
441 tcpip_search_list(),
442 tcpip_domain(),
443 primary_dns_suffix(),
444 policy_devolution(),
445 dnscache_devolution(),
446 tcpip_devolution(),
447 append_to_multi_label_name(),
448 have_name_resolution_policy(false) {
449 policy_search_list.set = false;
450 tcpip_search_list.set = false;
451 tcpip_domain.set = false;
452 primary_dns_suffix.set = false;
454 policy_devolution.enabled.set = false;
455 policy_devolution.level.set = false;
456 dnscache_devolution.enabled.set = false;
457 dnscache_devolution.level.set = false;
458 tcpip_devolution.enabled.set = false;
459 tcpip_devolution.level.set = false;
461 append_to_multi_label_name.set = false;
464 DnsSystemSettings::~DnsSystemSettings() {
467 bool ParseSearchList(const base::string16& value,
468 std::vector<std::string>* output) {
469 DCHECK(output);
470 if (value.empty())
471 return false;
473 output->clear();
475 // If the list includes an empty hostname (",," or ", ,"), it is terminated.
476 // Although nslookup and network connection property tab ignore such
477 // fragments ("a,b,,c" becomes ["a", "b", "c"]), our reference is getaddrinfo
478 // (which sees ["a", "b"]). WMI queries also return a matching search list.
479 std::vector<base::string16> woutput;
480 base::SplitString(value, ',', &woutput);
481 for (size_t i = 0; i < woutput.size(); ++i) {
482 // Convert non-ASCII to punycode, although getaddrinfo does not properly
483 // handle such suffixes.
484 const base::string16& t = woutput[i];
485 std::string parsed;
486 if (!ParseDomainASCII(t, &parsed))
487 break;
488 output->push_back(parsed);
490 return !output->empty();
493 ConfigParseWinResult ConvertSettingsToDnsConfig(
494 const DnsSystemSettings& settings,
495 DnsConfig* config) {
496 *config = DnsConfig();
498 // Use GetAdapterAddresses to get effective DNS server order and
499 // connection-specific DNS suffix. Ignore disconnected and loopback adapters.
500 // The order of adapters is the network binding order, so stick to the
501 // first good adapter.
502 for (const IP_ADAPTER_ADDRESSES* adapter = settings.addresses.get();
503 adapter != NULL && config->nameservers.empty();
504 adapter = adapter->Next) {
505 if (adapter->OperStatus != IfOperStatusUp)
506 continue;
507 if (adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK)
508 continue;
510 for (const IP_ADAPTER_DNS_SERVER_ADDRESS* address =
511 adapter->FirstDnsServerAddress;
512 address != NULL;
513 address = address->Next) {
514 IPEndPoint ipe;
515 if (ipe.FromSockAddr(address->Address.lpSockaddr,
516 address->Address.iSockaddrLength)) {
517 if (IsStatelessDiscoveryAddress(ipe.address()))
518 continue;
519 // Override unset port.
520 if (!ipe.port())
521 ipe = IPEndPoint(ipe.address(), dns_protocol::kDefaultPort);
522 config->nameservers.push_back(ipe);
523 } else {
524 return CONFIG_PARSE_WIN_BAD_ADDRESS;
528 // IP_ADAPTER_ADDRESSES in Vista+ has a search list at |FirstDnsSuffix|,
529 // but it came up empty in all trials.
530 // |DnsSuffix| stores the effective connection-specific suffix, which is
531 // obtained via DHCP (regkey: Tcpip\Parameters\Interfaces\{XXX}\DhcpDomain)
532 // or specified by the user (regkey: Tcpip\Parameters\Domain).
533 std::string dns_suffix;
534 if (ParseDomainASCII(adapter->DnsSuffix, &dns_suffix))
535 config->search.push_back(dns_suffix);
538 if (config->nameservers.empty())
539 return CONFIG_PARSE_WIN_NO_NAMESERVERS; // No point continuing.
541 // Windows always tries a multi-label name "as is" before using suffixes.
542 config->ndots = 1;
544 if (!settings.append_to_multi_label_name.set) {
545 // The default setting is true for XP, false for Vista+.
546 if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
547 config->append_to_multi_label_name = false;
548 } else {
549 config->append_to_multi_label_name = true;
551 } else {
552 config->append_to_multi_label_name =
553 (settings.append_to_multi_label_name.value != 0);
556 ConfigParseWinResult result = CONFIG_PARSE_WIN_OK;
557 if (settings.have_name_resolution_policy) {
558 config->unhandled_options = true;
559 // TODO(szym): only set this to true if NRPT has DirectAccess rules.
560 config->use_local_ipv6 = true;
561 result = CONFIG_PARSE_WIN_UNHANDLED_OPTIONS;
564 ConfigureSuffixSearch(settings, config);
565 return result;
568 // Watches registry and HOSTS file for changes. Must live on a thread which
569 // allows IO.
570 class DnsConfigServiceWin::Watcher
571 : public NetworkChangeNotifier::IPAddressObserver {
572 public:
573 explicit Watcher(DnsConfigServiceWin* service) : service_(service) {}
574 ~Watcher() override { NetworkChangeNotifier::RemoveIPAddressObserver(this); }
576 bool Watch() {
577 RegistryWatcher::CallbackType callback =
578 base::Bind(&DnsConfigServiceWin::OnConfigChanged,
579 base::Unretained(service_));
581 bool success = true;
583 // The Tcpip key must be present.
584 if (!tcpip_watcher_.Watch(kTcpipPath, callback)) {
585 LOG(ERROR) << "DNS registry watch failed to start.";
586 success = false;
587 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus",
588 DNS_CONFIG_WATCH_FAILED_TO_START_CONFIG,
589 DNS_CONFIG_WATCH_MAX);
592 // Watch for IPv6 nameservers.
593 tcpip6_watcher_.Watch(kTcpip6Path, callback);
595 // DNS suffix search list and devolution can be configured via group
596 // policy which sets this registry key. If the key is missing, the policy
597 // does not apply, and the DNS client uses Tcpip and Dnscache settings.
598 // If a policy is installed, DnsConfigService will need to be restarted.
599 // BUG=99509
601 dnscache_watcher_.Watch(kDnscachePath, callback);
602 policy_watcher_.Watch(kPolicyPath, callback);
604 if (!hosts_watcher_.Watch(GetHostsPath(), false,
605 base::Bind(&Watcher::OnHostsChanged,
606 base::Unretained(this)))) {
607 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus",
608 DNS_CONFIG_WATCH_FAILED_TO_START_HOSTS,
609 DNS_CONFIG_WATCH_MAX);
610 LOG(ERROR) << "DNS hosts watch failed to start.";
611 success = false;
612 } else {
613 // Also need to observe changes to local non-loopback IP for DnsHosts.
614 NetworkChangeNotifier::AddIPAddressObserver(this);
616 return success;
619 private:
620 void OnHostsChanged(const base::FilePath& path, bool error) {
621 if (error)
622 NetworkChangeNotifier::RemoveIPAddressObserver(this);
623 service_->OnHostsChanged(!error);
626 // NetworkChangeNotifier::IPAddressObserver:
627 void OnIPAddressChanged() override {
628 // Need to update non-loopback IP of local host.
629 service_->OnHostsChanged(true);
632 DnsConfigServiceWin* service_;
634 RegistryWatcher tcpip_watcher_;
635 RegistryWatcher tcpip6_watcher_;
636 RegistryWatcher dnscache_watcher_;
637 RegistryWatcher policy_watcher_;
638 base::FilePathWatcher hosts_watcher_;
640 DISALLOW_COPY_AND_ASSIGN(Watcher);
643 // Reads config from registry and IpHelper. All work performed on WorkerPool.
644 class DnsConfigServiceWin::ConfigReader : public SerialWorker {
645 public:
646 explicit ConfigReader(DnsConfigServiceWin* service)
647 : service_(service),
648 success_(false) {}
650 private:
651 ~ConfigReader() override {}
653 void DoWork() override {
654 // Should be called on WorkerPool.
655 base::TimeTicks start_time = base::TimeTicks::Now();
656 DnsSystemSettings settings = {};
657 ConfigParseWinResult result = ReadSystemSettings(&settings);
658 if (result == CONFIG_PARSE_WIN_OK)
659 result = ConvertSettingsToDnsConfig(settings, &dns_config_);
660 success_ = (result == CONFIG_PARSE_WIN_OK ||
661 result == CONFIG_PARSE_WIN_UNHANDLED_OPTIONS);
662 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.ConfigParseWin",
663 result, CONFIG_PARSE_WIN_MAX);
664 UMA_HISTOGRAM_BOOLEAN("AsyncDNS.ConfigParseResult", success_);
665 UMA_HISTOGRAM_TIMES("AsyncDNS.ConfigParseDuration",
666 base::TimeTicks::Now() - start_time);
669 void OnWorkFinished() override {
670 DCHECK(loop()->BelongsToCurrentThread());
671 DCHECK(!IsCancelled());
672 if (success_) {
673 service_->OnConfigRead(dns_config_);
674 } else {
675 LOG(WARNING) << "Failed to read DnsConfig.";
676 // Try again in a while in case DnsConfigWatcher missed the signal.
677 base::MessageLoop::current()->PostDelayedTask(
678 FROM_HERE,
679 base::Bind(&ConfigReader::WorkNow, this),
680 base::TimeDelta::FromSeconds(kRetryIntervalSeconds));
684 DnsConfigServiceWin* service_;
685 // Written in DoWork(), read in OnWorkFinished(). No locking required.
686 DnsConfig dns_config_;
687 bool success_;
690 // Reads hosts from HOSTS file and fills in localhost and local computer name if
691 // necessary. All work performed on WorkerPool.
692 class DnsConfigServiceWin::HostsReader : public SerialWorker {
693 public:
694 explicit HostsReader(DnsConfigServiceWin* service)
695 : path_(GetHostsPath()),
696 service_(service),
697 success_(false) {
700 private:
701 ~HostsReader() override {}
703 void DoWork() override {
704 base::TimeTicks start_time = base::TimeTicks::Now();
705 HostsParseWinResult result = HOSTS_PARSE_WIN_UNREADABLE_HOSTS_FILE;
706 if (ParseHostsFile(path_, &hosts_))
707 result = AddLocalhostEntries(&hosts_);
708 success_ = (result == HOSTS_PARSE_WIN_OK);
709 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.HostsParseWin",
710 result, HOSTS_PARSE_WIN_MAX);
711 UMA_HISTOGRAM_BOOLEAN("AsyncDNS.HostParseResult", success_);
712 UMA_HISTOGRAM_TIMES("AsyncDNS.HostsParseDuration",
713 base::TimeTicks::Now() - start_time);
716 void OnWorkFinished() override {
717 DCHECK(loop()->BelongsToCurrentThread());
718 if (success_) {
719 service_->OnHostsRead(hosts_);
720 } else {
721 LOG(WARNING) << "Failed to read DnsHosts.";
725 const base::FilePath path_;
726 DnsConfigServiceWin* service_;
727 // Written in DoWork, read in OnWorkFinished, no locking necessary.
728 DnsHosts hosts_;
729 bool success_;
731 DISALLOW_COPY_AND_ASSIGN(HostsReader);
734 DnsConfigServiceWin::DnsConfigServiceWin()
735 : config_reader_(new ConfigReader(this)),
736 hosts_reader_(new HostsReader(this)) {}
738 DnsConfigServiceWin::~DnsConfigServiceWin() {
739 config_reader_->Cancel();
740 hosts_reader_->Cancel();
743 void DnsConfigServiceWin::ReadNow() {
744 config_reader_->WorkNow();
745 hosts_reader_->WorkNow();
748 bool DnsConfigServiceWin::StartWatching() {
749 // TODO(szym): re-start watcher if that makes sense. http://crbug.com/116139
750 watcher_.reset(new Watcher(this));
751 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus", DNS_CONFIG_WATCH_STARTED,
752 DNS_CONFIG_WATCH_MAX);
753 return watcher_->Watch();
756 void DnsConfigServiceWin::OnConfigChanged(bool succeeded) {
757 InvalidateConfig();
758 config_reader_->WorkNow();
759 if (!succeeded) {
760 LOG(ERROR) << "DNS config watch failed.";
761 set_watch_failed(true);
762 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus",
763 DNS_CONFIG_WATCH_FAILED_CONFIG,
764 DNS_CONFIG_WATCH_MAX);
768 void DnsConfigServiceWin::OnHostsChanged(bool succeeded) {
769 InvalidateHosts();
770 if (succeeded) {
771 hosts_reader_->WorkNow();
772 } else {
773 LOG(ERROR) << "DNS hosts watch failed.";
774 set_watch_failed(true);
775 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus",
776 DNS_CONFIG_WATCH_FAILED_HOSTS,
777 DNS_CONFIG_WATCH_MAX);
781 } // namespace internal
783 // static
784 scoped_ptr<DnsConfigService> DnsConfigService::CreateSystemService() {
785 return scoped_ptr<DnsConfigService>(new internal::DnsConfigServiceWin());
788 } // namespace net