diff options
Diffstat (limited to 'net/dns/dns_config_service_win.cc')
-rw-r--r-- | net/dns/dns_config_service_win.cc | 147 |
1 files changed, 99 insertions, 48 deletions
diff --git a/net/dns/dns_config_service_win.cc b/net/dns/dns_config_service_win.cc index 14bb773..047b92d 100644 --- a/net/dns/dns_config_service_win.cc +++ b/net/dns/dns_config_service_win.cc @@ -11,9 +11,11 @@ #include "base/callback.h" #include "base/compiler_specific.h" #include "base/file_path.h" +#include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/string_split.h" #include "base/string_util.h" +#include "base/synchronization/lock.h" #include "base/threading/thread_restrictions.h" #include "base/utf_string_conversions.h" #include "base/win/object_watcher.h" @@ -22,26 +24,27 @@ #include "googleurl/src/url_canon.h" #include "net/base/net_util.h" #include "net/dns/dns_protocol.h" +#include "net/dns/file_path_watcher_wrapper.h" #include "net/dns/serial_worker.h" -#include "net/dns/watching_file_reader.h" #pragma comment(lib, "iphlpapi.lib") namespace net { +namespace internal { + namespace { -// Watches a single registry key for changes. This class is thread-safe with -// the exception of StartWatch which must be called once and only once. +// Watches a single registry key for changes. Thread-safe. class RegistryWatcher : public base::win::ObjectWatcher::Delegate { public: + typedef base::Callback<void(bool succeeded)> CallbackType; RegistryWatcher() {} - bool StartWatch(const wchar_t* key, const base::Closure& callback) { + bool Watch(const wchar_t* key, const CallbackType& callback) { + base::AutoLock lock(lock_); DCHECK(!callback.is_null()); - DCHECK(callback_.is_null()); - // TODO(szym): This will need to change to address http://crbug.com/99509 - DCHECK(!key_.Valid()); + CancelLocked(); if (key_.Open(HKEY_LOCAL_MACHINE, key, KEY_NOTIFY | KEY_QUERY_VALUE) != ERROR_SUCCESS) return false; @@ -54,22 +57,21 @@ class RegistryWatcher : public base::win::ObjectWatcher::Delegate { } void Cancel() { - callback_.Reset(); - key_.StopWatching(); - watcher_.StopWatching(); + base::AutoLock lock(lock_); + CancelLocked(); } virtual void OnObjectSignaled(HANDLE object) OVERRIDE { + base::AutoLock lock(lock_); + bool succeeded = (key_.StartWatching() == ERROR_SUCCESS) && + watcher_.StartWatching(key_.watch_event(), this); if (!callback_.is_null()) - callback_.Run(); - // TODO(szym): handle errors - bool success = key_.StartWatching() && - watcher_.StartWatching(key_.watch_event(), this); - DCHECK(success); + callback_.Run(succeeded); } bool ReadString(const wchar_t* name, DnsSystemSettings::RegString* out) const { + base::AutoLock lock(lock_); out->set = false; if (!key_.Valid()) { // Assume that if the |key_| is invalid then the key is missing. @@ -85,6 +87,7 @@ class RegistryWatcher : public base::win::ObjectWatcher::Delegate { bool ReadDword(const wchar_t* name, DnsSystemSettings::RegDword* out) const { + base::AutoLock lock(lock_); out->set = false; if (!key_.Valid()) { // Assume that if the |key_| is invalid then the key is missing. @@ -99,7 +102,17 @@ class RegistryWatcher : public base::win::ObjectWatcher::Delegate { } private: - base::Closure callback_; + void CancelLocked() { + lock_.AssertAcquired(); + callback_.Reset(); + if (key_.Valid()) { + watcher_.StopWatching(); + key_.StopWatching(); + } + } + + mutable base::Lock lock_; + CallbackType callback_; base::win::RegKey key_; base::win::ObjectWatcher watcher_; @@ -123,9 +136,8 @@ bool ParseDomainASCII(const string16& widestr, std::string* domain) { // Otherwise try to convert it from IDN to punycode. const int kInitialBufferSize = 256; url_canon::RawCanonOutputT<char16, kInitialBufferSize> punycode; - if (!url_canon::IDNToASCII(widestr.data(), widestr.length(), &punycode)) { + if (!url_canon::IDNToASCII(widestr.data(), widestr.length(), &punycode)) return false; - } // |punycode_output| should now be ASCII; convert it to a std::string. // (We could use UTF16ToASCII() instead, but that requires an extra string @@ -169,9 +181,10 @@ bool ConvertSettingsToDnsConfig(const DnsSystemSettings& settings, // Use GetAdapterAddresses to get effective DNS server order and // connection-specific DNS suffix. Ignore disconnected and loopback adapters. - // TODO(szym): Also ignore VM adapters. http://crbug.com/112856 + // The order of adapters is the network binding order, so stick to the + // first good adapter. for (const IP_ADAPTER_ADDRESSES* adapter = settings.addresses.get(); - adapter != NULL; + adapter != NULL && config->nameservers.empty(); adapter = adapter->Next) { if (adapter->OperStatus != IfOperStatusUp) continue; @@ -200,9 +213,8 @@ bool ConvertSettingsToDnsConfig(const DnsSystemSettings& settings, // obtained via DHCP (regkey: Tcpip\Parameters\Interfaces\{XXX}\DhcpDomain) // or specified by the user (regkey: Tcpip\Parameters\Domain). std::string dns_suffix; - if (ParseDomainASCII(adapter->DnsSuffix, &dns_suffix)) { + if (ParseDomainASCII(adapter->DnsSuffix, &dns_suffix)) config->search.push_back(dns_suffix); - } } if (config->nameservers.empty()) @@ -304,31 +316,36 @@ class DnsConfigServiceWin::ConfigReader : public SerialWorker { : service_(service), success_(false) {} - bool StartWatch() { + bool Watch() { DCHECK(loop()->BelongsToCurrentThread()); - base::Closure callback = base::Bind(&SerialWorker::WorkNow, - base::Unretained(this)); + RegistryWatcher::CallbackType callback = + base::Bind(&ConfigReader::OnChange, base::Unretained(this)); + + // The Tcpip key must be present. + if (!tcpip_watcher_.Watch( + L"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters", + callback)) + return false; + + // Watch for IPv6 nameservers. + tcpip6_watcher_.Watch( + L"SYSTEM\\CurrentControlSet\\Services\\Tcpip6\\Parameters", + callback); // DNS suffix search list and devolution can be configured via group // policy which sets this registry key. If the key is missing, the policy // does not apply, and the DNS client uses Tcpip and Dnscache settings. // If a policy is installed, DnsConfigService will need to be restarted. // BUG=99509 - policy_watcher_.StartWatch( - L"SOFTWARE\\Policies\\Microsoft\\Windows NT\\DNSClient", - callback); - // The Dnscache key might also be missing. - dnscache_watcher_.StartWatch( + dnscache_watcher_.Watch( L"SYSTEM\\CurrentControlSet\\Services\\Dnscache\\Parameters", callback); - // The Tcpip key must be present. - if (!tcpip_watcher_.StartWatch( - L"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters", - callback)) - return false; + policy_watcher_.Watch( + L"SOFTWARE\\Policies\\Microsoft\\Windows NT\\DNSClient", + callback); WorkNow(); return true; @@ -339,18 +356,27 @@ class DnsConfigServiceWin::ConfigReader : public SerialWorker { SerialWorker::Cancel(); policy_watcher_.Cancel(); dnscache_watcher_.Cancel(); + tcpip6_watcher_.Cancel(); tcpip_watcher_.Cancel(); } - // Delay between calls to WorkerPool::PostTask - static const int kWorkerPoolRetryDelayMs = 100; - private: - friend class RegistryWatcher; virtual ~ConfigReader() { DCHECK(IsCancelled()); } + void OnChange(bool succeeded) { + DCHECK(loop()->BelongsToCurrentThread()); + if (!IsCancelled()) + service_->InvalidateConfig(); + // We don't trust a config that we cannot watch in the future. + // TODO(szym): re-start watcher if that makes sense. http://crbug.com/116139 + if (succeeded) + WorkNow(); + else + LOG(ERROR) << "Failed to watch DNS config"; + } + bool ReadIpHelper(DnsSystemSettings* settings) { base::ThreadRestrictions::AssertIOAllowed(); @@ -429,43 +455,68 @@ class DnsConfigServiceWin::ConfigReader : public SerialWorker { DnsConfig dns_config_; bool success_; - RegistryWatcher policy_watcher_; - RegistryWatcher dnscache_watcher_; RegistryWatcher tcpip_watcher_; + RegistryWatcher tcpip6_watcher_; + RegistryWatcher dnscache_watcher_; + RegistryWatcher policy_watcher_; }; DnsConfigServiceWin::DnsConfigServiceWin() : config_reader_(new ConfigReader(this)), - hosts_watcher_(new WatchingFileReader()) {} + hosts_watcher_(new FilePathWatcherWrapper()) {} DnsConfigServiceWin::~DnsConfigServiceWin() { DCHECK(CalledOnValidThread()); config_reader_->Cancel(); + if (hosts_reader_) + hosts_reader_->Cancel(); } -void DnsConfigServiceWin::Watch() { +void DnsConfigServiceWin::Watch(const CallbackType& callback) { DCHECK(CalledOnValidThread()); + DCHECK(!callback.is_null()); + set_callback(callback); // This is done only once per lifetime so open the keys and file watcher // handles on this thread. // TODO(szym): Should/can this be avoided? http://crbug.com/114223 base::ThreadRestrictions::ScopedAllowIO allow_io; - bool started = config_reader_->StartWatch(); - // TODO(szym): handle possible failure - DCHECK(started); + if (!config_reader_->Watch()) { + LOG(ERROR) << "Failed to start watching DNS config"; + InvalidateConfig(); + } TCHAR buffer[MAX_PATH]; UINT rc = GetSystemDirectory(buffer, MAX_PATH); DCHECK(0 < rc && rc < MAX_PATH); FilePath hosts_path = FilePath(buffer). Append(FILE_PATH_LITERAL("drivers\\etc\\hosts")); - hosts_watcher_->StartWatch(hosts_path, new DnsHostsReader(hosts_path, this)); + hosts_reader_ = new DnsHostsReader( + hosts_path, + base::Bind(&DnsConfigServiceWin::OnHostsRead, base::Unretained(this))); + if (hosts_watcher_->Watch(hosts_path, + base::Bind(&DnsConfigServiceWin::OnHostsChanged, + base::Unretained(this)))) { + OnHostsChanged(true); + } else { + OnHostsChanged(false); + } +} + +void DnsConfigServiceWin::OnHostsChanged(bool succeeded) { + InvalidateHosts(); + if (succeeded) + hosts_reader_->WorkNow(); + else + LOG(ERROR) << "Failed to watch DNS hosts"; } +} // namespace internal + // static scoped_ptr<DnsConfigService> DnsConfigService::CreateSystemService() { - return scoped_ptr<DnsConfigService>(new DnsConfigServiceWin()); + return scoped_ptr<DnsConfigService>(new internal::DnsConfigServiceWin()); } } // namespace net |