Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(749)

Side by Side Diff: net/dns/dns_config_service_win.cc

Issue 10377092: [net/dns] Isolate DnsConfigWatcher from DnsConfigService. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Added sanity DCHECK. Created 8 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « net/dns/dns_config_service_win.h ('k') | net/dns/dns_config_watcher.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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
OLDNEW
« no previous file with comments | « net/dns/dns_config_service_win.h ('k') | net/dns/dns_config_watcher.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698