OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "net/dns/dns_config_service_win.h" | 5 #include "net/dns/dns_config_service_win.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <string> | 8 #include <string> |
9 | 9 |
10 #include "base/bind.h" | 10 #include "base/bind.h" |
11 #include "base/callback.h" | 11 #include "base/callback.h" |
12 #include "base/compiler_specific.h" | 12 #include "base/compiler_specific.h" |
13 #include "base/file_path.h" | 13 #include "base/file_path.h" |
14 #include "base/logging.h" | 14 #include "base/logging.h" |
15 #include "base/memory/scoped_ptr.h" | 15 #include "base/memory/scoped_ptr.h" |
16 #include "base/string_split.h" | 16 #include "base/string_split.h" |
17 #include "base/string_util.h" | 17 #include "base/string_util.h" |
18 #include "base/synchronization/lock.h" | 18 #include "base/synchronization/lock.h" |
19 #include "base/threading/non_thread_safe.h" | 19 #include "base/threading/non_thread_safe.h" |
20 #include "base/threading/thread_restrictions.h" | 20 #include "base/threading/thread_restrictions.h" |
21 #include "base/utf_string_conversions.h" | 21 #include "base/utf_string_conversions.h" |
22 #include "base/win/object_watcher.h" | |
23 #include "base/win/registry.h" | 22 #include "base/win/registry.h" |
24 #include "base/win/windows_version.h" | 23 #include "base/win/windows_version.h" |
25 #include "googleurl/src/url_canon.h" | 24 #include "googleurl/src/url_canon.h" |
26 #include "net/base/net_util.h" | 25 #include "net/base/net_util.h" |
27 #include "net/base/network_change_notifier.h" | 26 #include "net/base/network_change_notifier.h" |
28 #include "net/dns/dns_protocol.h" | 27 #include "net/dns/dns_protocol.h" |
29 #include "net/dns/file_path_watcher_wrapper.h" | |
30 #include "net/dns/serial_worker.h" | 28 #include "net/dns/serial_worker.h" |
31 | 29 |
32 #pragma comment(lib, "iphlpapi.lib") | 30 #pragma comment(lib, "iphlpapi.lib") |
33 | 31 |
34 namespace net { | 32 namespace net { |
35 | 33 |
36 namespace internal { | 34 namespace internal { |
37 | 35 |
38 namespace { | 36 namespace { |
39 | 37 |
40 // Registry key paths. | |
41 const wchar_t* const kTcpipPath = | |
42 L"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters"; | |
43 const wchar_t* const kTcpip6Path = | |
44 L"SYSTEM\\CurrentControlSet\\Services\\Tcpip6\\Parameters"; | |
45 const wchar_t* const kDnscachePath = | |
46 L"SYSTEM\\CurrentControlSet\\Services\\Dnscache\\Parameters"; | |
47 const wchar_t* const kPolicyPath = | |
48 L"SOFTWARE\\Policies\\Microsoft\\Windows NT\\DNSClient"; | |
49 const wchar_t* const kPrimaryDnsSuffixPath = | 38 const wchar_t* const kPrimaryDnsSuffixPath = |
50 L"SOFTWARE\\Policies\\Microsoft\\System\\DNSClient"; | 39 L"SOFTWARE\\Policies\\Microsoft\\System\\DNSClient"; |
51 | 40 |
52 // Convenience for reading values using RegKey. | 41 // Convenience for reading values using RegKey. |
53 class RegistryReader : public base::NonThreadSafe { | 42 class RegistryReader : public base::NonThreadSafe { |
54 public: | 43 public: |
55 explicit RegistryReader(const wchar_t* key) { | 44 explicit RegistryReader(const wchar_t* key) { |
56 // Ignoring the result. |key_.Valid()| will catch failures. | 45 // Ignoring the result. |key_.Valid()| will catch failures. |
57 key_.Open(HKEY_LOCAL_MACHINE, key, KEY_QUERY_VALUE); | 46 key_.Open(HKEY_LOCAL_MACHINE, key, KEY_QUERY_VALUE); |
58 } | 47 } |
(...skipping 29 matching lines...) Expand all Loading... |
88 } | 77 } |
89 return (result == ERROR_FILE_NOT_FOUND); | 78 return (result == ERROR_FILE_NOT_FOUND); |
90 } | 79 } |
91 | 80 |
92 private: | 81 private: |
93 base::win::RegKey key_; | 82 base::win::RegKey key_; |
94 | 83 |
95 DISALLOW_COPY_AND_ASSIGN(RegistryReader); | 84 DISALLOW_COPY_AND_ASSIGN(RegistryReader); |
96 }; | 85 }; |
97 | 86 |
98 | |
99 // Watches a single registry key for changes. | |
100 class RegistryWatcher : public base::win::ObjectWatcher::Delegate, | |
101 public base::NonThreadSafe { | |
102 public: | |
103 typedef base::Callback<void(bool succeeded)> CallbackType; | |
104 RegistryWatcher() {} | |
105 | |
106 bool Watch(const wchar_t* key, const CallbackType& callback) { | |
107 DCHECK(CalledOnValidThread()); | |
108 DCHECK(!callback.is_null()); | |
109 Cancel(); | |
110 if (key_.Open(HKEY_LOCAL_MACHINE, key, KEY_NOTIFY) != ERROR_SUCCESS) | |
111 return false; | |
112 if (key_.StartWatching() != ERROR_SUCCESS) | |
113 return false; | |
114 if (!watcher_.StartWatching(key_.watch_event(), this)) | |
115 return false; | |
116 callback_ = callback; | |
117 return true; | |
118 } | |
119 | |
120 bool IsWatching() const { | |
121 DCHECK(CalledOnValidThread()); | |
122 return !callback_.is_null(); | |
123 } | |
124 | |
125 void Cancel() { | |
126 DCHECK(CalledOnValidThread()); | |
127 callback_.Reset(); | |
128 if (key_.Valid()) { | |
129 watcher_.StopWatching(); | |
130 key_.StopWatching(); | |
131 key_.Close(); | |
132 } | |
133 } | |
134 | |
135 virtual void OnObjectSignaled(HANDLE object) OVERRIDE { | |
136 DCHECK(CalledOnValidThread()); | |
137 bool succeeded = (key_.StartWatching() == ERROR_SUCCESS) && | |
138 watcher_.StartWatching(key_.watch_event(), this); | |
139 CallbackType callback = callback_; | |
140 if (!succeeded) | |
141 Cancel(); | |
142 if (!callback.is_null()) | |
143 callback.Run(succeeded); | |
144 } | |
145 | |
146 private: | |
147 CallbackType callback_; | |
148 base::win::RegKey key_; | |
149 base::win::ObjectWatcher watcher_; | |
150 | |
151 DISALLOW_COPY_AND_ASSIGN(RegistryWatcher); | |
152 }; | |
153 | |
154 // Returns NULL if failed. | 87 // Returns NULL if failed. |
155 scoped_ptr_malloc<IP_ADAPTER_ADDRESSES> ReadIpHelper(ULONG flags) { | 88 scoped_ptr_malloc<IP_ADAPTER_ADDRESSES> ReadIpHelper(ULONG flags) { |
156 base::ThreadRestrictions::AssertIOAllowed(); | 89 base::ThreadRestrictions::AssertIOAllowed(); |
157 | 90 |
158 scoped_ptr_malloc<IP_ADAPTER_ADDRESSES> out; | 91 scoped_ptr_malloc<IP_ADAPTER_ADDRESSES> out; |
159 ULONG len = 15000; // As recommended by MSDN for GetAdaptersAddresses. | 92 ULONG len = 15000; // As recommended by MSDN for GetAdaptersAddresses. |
160 UINT rv = ERROR_BUFFER_OVERFLOW; | 93 UINT rv = ERROR_BUFFER_OVERFLOW; |
161 // Try up to three times. | 94 // Try up to three times. |
162 for (unsigned tries = 0; (tries < 3) && (rv == ERROR_BUFFER_OVERFLOW); | 95 for (unsigned tries = 0; (tries < 3) && (rv == ERROR_BUFFER_OVERFLOW); |
163 tries++) { | 96 tries++) { |
(...skipping 29 matching lines...) Expand all Loading... |
193 // (We could use UTF16ToASCII() instead, but that requires an extra string | 126 // (We could use UTF16ToASCII() instead, but that requires an extra string |
194 // copy. Since ASCII is a subset of UTF8 the following is equivalent). | 127 // copy. Since ASCII is a subset of UTF8 the following is equivalent). |
195 bool success = UTF16ToUTF8(punycode.data(), punycode.length(), domain); | 128 bool success = UTF16ToUTF8(punycode.data(), punycode.length(), domain); |
196 DCHECK(success); | 129 DCHECK(success); |
197 DCHECK(IsStringASCII(*domain)); | 130 DCHECK(IsStringASCII(*domain)); |
198 return success && !domain->empty(); | 131 return success && !domain->empty(); |
199 } | 132 } |
200 | 133 |
201 } // namespace | 134 } // namespace |
202 | 135 |
| 136 FilePath GetHostsPath() { |
| 137 TCHAR buffer[MAX_PATH]; |
| 138 UINT rc = GetSystemDirectory(buffer, MAX_PATH); |
| 139 DCHECK(0 < rc && rc < MAX_PATH); |
| 140 return FilePath(buffer).Append(FILE_PATH_LITERAL("drivers\\etc\\hosts")); |
| 141 } |
| 142 |
203 bool ParseSearchList(const string16& value, std::vector<std::string>* output) { | 143 bool ParseSearchList(const string16& value, std::vector<std::string>* output) { |
204 DCHECK(output); | 144 DCHECK(output); |
205 if (value.empty()) | 145 if (value.empty()) |
206 return false; | 146 return false; |
207 | 147 |
208 output->clear(); | 148 output->clear(); |
209 | 149 |
210 // If the list includes an empty hostname (",," or ", ,"), it is terminated. | 150 // If the list includes an empty hostname (",," or ", ,"), it is terminated. |
211 // Although nslookup and network connection property tab ignore such | 151 // Although nslookup and network connection property tab ignore such |
212 // fragments ("a,b,,c" becomes ["a", "b", "c"]), our reference is getaddrinfo | 152 // fragments ("a,b,,c" becomes ["a", "b", "c"]), our reference is getaddrinfo |
(...skipping 158 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
371 | 311 |
372 // Watches registry for changes and reads config from registry and IP helper. | 312 // Watches registry for changes and reads config from registry and IP helper. |
373 // Reading and opening of reg keys is always performed on WorkerPool. Setting | 313 // Reading and opening of reg keys is always performed on WorkerPool. Setting |
374 // up watches requires IO loop. | 314 // up watches requires IO loop. |
375 class DnsConfigServiceWin::ConfigReader : public SerialWorker { | 315 class DnsConfigServiceWin::ConfigReader : public SerialWorker { |
376 public: | 316 public: |
377 explicit ConfigReader(DnsConfigServiceWin* service) | 317 explicit ConfigReader(DnsConfigServiceWin* service) |
378 : service_(service), | 318 : service_(service), |
379 success_(false) {} | 319 success_(false) {} |
380 | 320 |
381 bool Watch() { | |
382 DCHECK(loop()->BelongsToCurrentThread()); | |
383 | |
384 RegistryWatcher::CallbackType callback = | |
385 base::Bind(&ConfigReader::OnChange, base::Unretained(this)); | |
386 | |
387 // The Tcpip key must be present. | |
388 if (!tcpip_watcher_.Watch(kTcpipPath, callback)) | |
389 return false; | |
390 | |
391 // Watch for IPv6 nameservers. | |
392 tcpip6_watcher_.Watch(kTcpip6Path, callback); | |
393 | |
394 // DNS suffix search list and devolution can be configured via group | |
395 // policy which sets this registry key. If the key is missing, the policy | |
396 // does not apply, and the DNS client uses Tcpip and Dnscache settings. | |
397 // If a policy is installed, DnsConfigService will need to be restarted. | |
398 // BUG=99509 | |
399 | |
400 dnscache_watcher_.Watch(kDnscachePath, callback); | |
401 policy_watcher_.Watch(kPolicyPath, callback); | |
402 | |
403 WorkNow(); | |
404 return true; | |
405 } | |
406 | |
407 void Cancel() { | |
408 DCHECK(loop()->BelongsToCurrentThread()); | |
409 SerialWorker::Cancel(); | |
410 policy_watcher_.Cancel(); | |
411 dnscache_watcher_.Cancel(); | |
412 tcpip6_watcher_.Cancel(); | |
413 tcpip_watcher_.Cancel(); | |
414 } | |
415 | |
416 private: | 321 private: |
417 virtual ~ConfigReader() { | |
418 DCHECK(IsCancelled()); | |
419 } | |
420 | |
421 void OnChange(bool succeeded) { | |
422 DCHECK(loop()->BelongsToCurrentThread()); | |
423 if (!IsCancelled()) | |
424 service_->InvalidateConfig(); | |
425 // We don't trust a config that we cannot watch in the future. | |
426 // TODO(szym): re-start watcher if that makes sense. http://crbug.com/116139 | |
427 if (succeeded) | |
428 WorkNow(); | |
429 else | |
430 LOG(ERROR) << "Failed to watch DNS config"; | |
431 } | |
432 | |
433 bool ReadDevolutionSetting(const RegistryReader& reader, | 322 bool ReadDevolutionSetting(const RegistryReader& reader, |
434 DnsSystemSettings::DevolutionSetting& setting) { | 323 DnsSystemSettings::DevolutionSetting& setting) { |
435 return reader.ReadDword(L"UseDomainNameDevolution", &setting.enabled) && | 324 return reader.ReadDword(L"UseDomainNameDevolution", &setting.enabled) && |
436 reader.ReadDword(L"DomainNameDevolutionLevel", &setting.level); | 325 reader.ReadDword(L"DomainNameDevolutionLevel", &setting.level); |
437 } | 326 } |
438 | 327 |
439 virtual void DoWork() OVERRIDE { | 328 virtual void DoWork() OVERRIDE { |
440 // Should be called on WorkerPool. | 329 // Should be called on WorkerPool. |
441 success_ = false; | 330 success_ = false; |
442 | 331 |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
493 service_->OnConfigRead(dns_config_); | 382 service_->OnConfigRead(dns_config_); |
494 } else { | 383 } else { |
495 LOG(WARNING) << "Failed to read config."; | 384 LOG(WARNING) << "Failed to read config."; |
496 } | 385 } |
497 } | 386 } |
498 | 387 |
499 DnsConfigServiceWin* service_; | 388 DnsConfigServiceWin* service_; |
500 // Written in DoRead(), read in OnReadFinished(). No locking required. | 389 // Written in DoRead(), read in OnReadFinished(). No locking required. |
501 DnsConfig dns_config_; | 390 DnsConfig dns_config_; |
502 bool success_; | 391 bool success_; |
503 | |
504 RegistryWatcher tcpip_watcher_; | |
505 RegistryWatcher tcpip6_watcher_; | |
506 RegistryWatcher dnscache_watcher_; | |
507 RegistryWatcher policy_watcher_; | |
508 }; | 392 }; |
509 | 393 |
510 FilePath GetHostsPath() { | |
511 TCHAR buffer[MAX_PATH]; | |
512 UINT rc = GetSystemDirectory(buffer, MAX_PATH); | |
513 DCHECK(0 < rc && rc < MAX_PATH); | |
514 return FilePath(buffer).Append(FILE_PATH_LITERAL("drivers\\etc\\hosts")); | |
515 } | |
516 | |
517 // An extension for DnsHostsReader which also watches the HOSTS file, | 394 // An extension for DnsHostsReader which also watches the HOSTS file, |
518 // reads local name from GetComputerNameEx, local IP from GetAdaptersAddresses, | 395 // reads local name from GetComputerNameEx, local IP from GetAdaptersAddresses, |
519 // and observes changes to local IP address. | 396 // and observes changes to local IP address. |
520 class DnsConfigServiceWin::HostsReader | 397 class DnsConfigServiceWin::HostsReader : public DnsHostsReader { |
521 : public DnsHostsReader, | |
522 public NetworkChangeNotifier::IPAddressObserver { | |
523 public: | 398 public: |
524 explicit HostsReader(DnsConfigServiceWin* service) | 399 explicit HostsReader(DnsConfigServiceWin* service) |
525 : DnsHostsReader(GetHostsPath()), service_(service) { | 400 : DnsHostsReader(GetHostsPath()), service_(service) { |
526 } | 401 } |
527 | 402 |
528 bool Watch() { | |
529 DCHECK(loop()->BelongsToCurrentThread()); | |
530 DCHECK(!IsCancelled()); | |
531 | |
532 // In case the reader is restarted, remove it from the observer list. | |
533 NetworkChangeNotifier::RemoveIPAddressObserver(this); | |
534 | |
535 if (!hosts_watcher_.Watch(path(), | |
536 base::Bind(&HostsReader::OnHostsChanged, | |
537 base::Unretained(this)))) { | |
538 return false; | |
539 } | |
540 NetworkChangeNotifier::AddIPAddressObserver(this); | |
541 WorkNow(); | |
542 return true; | |
543 } | |
544 | |
545 // Cancels the underlying SerialWorker. Cannot be undone. | |
546 void Cancel() { | |
547 DnsHostsReader::Cancel(); | |
548 hosts_watcher_.Cancel(); | |
549 NetworkChangeNotifier::RemoveIPAddressObserver(this); | |
550 } | |
551 | |
552 private: | 403 private: |
553 virtual void OnIPAddressChanged() OVERRIDE { | |
554 DCHECK(loop()->BelongsToCurrentThread()); | |
555 service_->InvalidateHosts(); | |
556 if (!hosts_watcher_.IsWatching()) | |
557 return; | |
558 WorkNow(); | |
559 } | |
560 | |
561 void OnHostsChanged(bool succeeded) { | |
562 DCHECK(loop()->BelongsToCurrentThread()); | |
563 service_->InvalidateHosts(); | |
564 if (succeeded) | |
565 WorkNow(); | |
566 else | |
567 LOG(ERROR) << "Failed to watch DNS hosts"; | |
568 } | |
569 | |
570 virtual void DoWork() OVERRIDE { | 404 virtual void DoWork() OVERRIDE { |
571 DnsHostsReader::DoWork(); | 405 DnsHostsReader::DoWork(); |
572 | 406 |
573 if (!success_) | 407 if (!success_) |
574 return; | 408 return; |
575 | 409 |
576 success_ = false; | 410 success_ = false; |
577 | 411 |
578 // Default address of "localhost" and local computer name can be overridden | 412 // Default address of "localhost" and local computer name can be overridden |
579 // by the HOSTS file, but if it's not there, then we need to fill it in. | 413 // by the HOSTS file, but if it's not there, then we need to fill it in. |
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
651 ipe.address(); | 485 ipe.address(); |
652 } | 486 } |
653 } | 487 } |
654 } | 488 } |
655 | 489 |
656 success_ = true; | 490 success_ = true; |
657 } | 491 } |
658 | 492 |
659 virtual void OnWorkFinished() OVERRIDE { | 493 virtual void OnWorkFinished() OVERRIDE { |
660 DCHECK(loop()->BelongsToCurrentThread()); | 494 DCHECK(loop()->BelongsToCurrentThread()); |
661 if (!success_ || !hosts_watcher_.IsWatching()) | 495 if (success_) { |
662 return; | 496 service_->OnHostsRead(dns_hosts_); |
663 service_->OnHostsRead(dns_hosts_); | 497 } else { |
| 498 LOG(WARNING) << "Failed to read hosts."; |
| 499 } |
664 } | 500 } |
665 | 501 |
666 DnsConfigServiceWin* service_; | 502 DnsConfigServiceWin* service_; |
667 FilePathWatcherWrapper hosts_watcher_; | |
668 | 503 |
669 DISALLOW_COPY_AND_ASSIGN(HostsReader); | 504 DISALLOW_COPY_AND_ASSIGN(HostsReader); |
670 }; | 505 }; |
671 | 506 |
672 | |
673 DnsConfigServiceWin::DnsConfigServiceWin() | 507 DnsConfigServiceWin::DnsConfigServiceWin() |
674 : config_reader_(new ConfigReader(this)), | 508 : config_reader_(new ConfigReader(this)), |
675 hosts_reader_(new HostsReader(this)) {} | 509 hosts_reader_(new HostsReader(this)) {} |
676 | 510 |
677 DnsConfigServiceWin::~DnsConfigServiceWin() { | 511 DnsConfigServiceWin::~DnsConfigServiceWin() { |
678 DCHECK(CalledOnValidThread()); | 512 DCHECK(CalledOnValidThread()); |
679 config_reader_->Cancel(); | 513 config_reader_->Cancel(); |
680 hosts_reader_->Cancel(); | 514 hosts_reader_->Cancel(); |
| 515 NetworkChangeNotifier::RemoveIPAddressObserver(this); |
681 } | 516 } |
682 | 517 |
683 void DnsConfigServiceWin::Watch(const CallbackType& callback) { | 518 void DnsConfigServiceWin::Watch(const CallbackType& callback) { |
684 DCHECK(CalledOnValidThread()); | 519 DnsConfigService::Watch(callback); |
685 DCHECK(!callback.is_null()); | 520 // Also need to observe changes to local non-loopback IP for DnsHosts. |
686 set_callback(callback); | 521 NetworkChangeNotifier::AddIPAddressObserver(this); |
| 522 } |
687 | 523 |
688 // This is done only once per lifetime so open the keys and file watcher | 524 void DnsConfigServiceWin::OnDNSChanged(unsigned detail) { |
689 // handles on this thread. | 525 if (detail & NetworkChangeNotifier::CHANGE_DNS_WATCH_FAILED) { |
690 // TODO(szym): Should/can this be avoided? http://crbug.com/114223 | 526 InvalidateConfig(); |
691 base::ThreadRestrictions::ScopedAllowIO allow_io; | 527 InvalidateHosts(); |
| 528 // We don't trust a config that we cannot watch in the future. |
| 529 config_reader_->Cancel(); |
| 530 hosts_reader_->Cancel(); |
| 531 return; |
| 532 } |
| 533 if (detail & NetworkChangeNotifier::CHANGE_DNS_WATCH_STARTED) |
| 534 detail = ~0; // Assume everything changed. |
| 535 if (detail & NetworkChangeNotifier::CHANGE_DNS_SETTINGS) { |
| 536 InvalidateConfig(); |
| 537 config_reader_->WorkNow(); |
| 538 } |
| 539 if (detail & NetworkChangeNotifier::CHANGE_DNS_HOSTS) { |
| 540 InvalidateHosts(); |
| 541 hosts_reader_->WorkNow(); |
| 542 } |
| 543 } |
692 | 544 |
693 if (!config_reader_->Watch()) { | 545 void DnsConfigServiceWin::OnIPAddressChanged() { |
694 LOG(ERROR) << "Failed to start watching DNS config"; | 546 // Need to update non-loopback IP of local host. |
695 InvalidateConfig(); | 547 if (NetworkChangeNotifier::IsWatchingDNS()) |
696 } | 548 OnDNSChanged(NetworkChangeNotifier::CHANGE_DNS_HOSTS); |
697 | |
698 if (!hosts_reader_->Watch()) { | |
699 LOG(ERROR) << "Failed to start watching HOSTS"; | |
700 InvalidateHosts(); | |
701 } | |
702 } | 549 } |
703 | 550 |
704 } // namespace internal | 551 } // namespace internal |
705 | 552 |
706 // static | 553 // static |
707 scoped_ptr<DnsConfigService> DnsConfigService::CreateSystemService() { | 554 scoped_ptr<DnsConfigService> DnsConfigService::CreateSystemService() { |
708 return scoped_ptr<DnsConfigService>(new internal::DnsConfigServiceWin()); | 555 return scoped_ptr<DnsConfigService>(new internal::DnsConfigServiceWin()); |
709 } | 556 } |
710 | 557 |
711 } // namespace net | 558 } // namespace net |
712 | 559 |
OLD | NEW |