Chromium Code Reviews| Index: net/dns/dns_config_service_win.cc |
| diff --git a/net/dns/dns_config_service_win.cc b/net/dns/dns_config_service_win.cc |
| index 047b92dd03f8d3ecc5a5e77583655ffabeed7881..b85d192961cd55efb55764d16d75d34b8a279662 100644 |
| --- a/net/dns/dns_config_service_win.cc |
| +++ b/net/dns/dns_config_service_win.cc |
| @@ -16,6 +16,7 @@ |
| #include "base/string_split.h" |
| #include "base/string_util.h" |
| #include "base/synchronization/lock.h" |
| +#include "base/threading/non_thread_safe.h" |
| #include "base/threading/thread_restrictions.h" |
| #include "base/utf_string_conversions.h" |
| #include "base/win/object_watcher.h" |
| @@ -35,43 +36,27 @@ namespace internal { |
| namespace { |
| -// Watches a single registry key for changes. Thread-safe. |
| -class RegistryWatcher : public base::win::ObjectWatcher::Delegate { |
| +// Registry key paths. |
| +const wchar_t* const kTcpipPath = |
| + L"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters"; |
| +const wchar_t* const kTcpip6Path = |
| + L"SYSTEM\\CurrentControlSet\\Services\\Tcpip6\\Parameters"; |
| +const wchar_t* const kDnscachePath = |
| + L"SYSTEM\\CurrentControlSet\\Services\\Dnscache\\Parameters"; |
| +const wchar_t* const kPolicyPath = |
| + L"SOFTWARE\\Policies\\Microsoft\\Windows NT\\DNSClient"; |
| + |
| +// Convenience for reading values using RegKey. |
| +class RegistryReader : public base::NonThreadSafe { |
| public: |
| - typedef base::Callback<void(bool succeeded)> CallbackType; |
| - RegistryWatcher() {} |
| - |
| - bool Watch(const wchar_t* key, const CallbackType& callback) { |
| - base::AutoLock lock(lock_); |
| - DCHECK(!callback.is_null()); |
| - CancelLocked(); |
| - if (key_.Open(HKEY_LOCAL_MACHINE, key, |
| - KEY_NOTIFY | KEY_QUERY_VALUE) != ERROR_SUCCESS) |
| - return false; |
| - if (key_.StartWatching() != ERROR_SUCCESS) |
| - return false; |
| - if (!watcher_.StartWatching(key_.watch_event(), this)) |
| - return false; |
| - callback_ = callback; |
| - return true; |
| - } |
| - |
| - void Cancel() { |
| - 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(succeeded); |
| + explicit RegistryReader(const wchar_t* key) { |
| + // Ignoring the result. |key_.Valid()| will catch failures. |
| + key_.Open(HKEY_LOCAL_MACHINE, key, KEY_QUERY_VALUE); |
| } |
| bool ReadString(const wchar_t* name, |
| DnsSystemSettings::RegString* out) const { |
| - base::AutoLock lock(lock_); |
| + DCHECK(CalledOnValidThread()); |
| out->set = false; |
| if (!key_.Valid()) { |
| // Assume that if the |key_| is invalid then the key is missing. |
| @@ -87,7 +72,7 @@ class RegistryWatcher : public base::win::ObjectWatcher::Delegate { |
| bool ReadDword(const wchar_t* name, |
| DnsSystemSettings::RegDword* out) const { |
| - base::AutoLock lock(lock_); |
| + DCHECK(CalledOnValidThread()); |
| out->set = false; |
| if (!key_.Valid()) { |
| // Assume that if the |key_| is invalid then the key is missing. |
| @@ -102,8 +87,35 @@ class RegistryWatcher : public base::win::ObjectWatcher::Delegate { |
| } |
| private: |
| - void CancelLocked() { |
| - lock_.AssertAcquired(); |
| + base::win::RegKey key_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(RegistryReader); |
| +}; |
| + |
| + |
| +// Watches a single registry key for changes. |
| +class RegistryWatcher : public base::win::ObjectWatcher::Delegate, |
| + public base::NonThreadSafe { |
| + public: |
| + typedef base::Callback<void(bool succeeded)> CallbackType; |
| + RegistryWatcher() {} |
| + |
| + bool Watch(const wchar_t* key, const CallbackType& callback) { |
| + DCHECK(CalledOnValidThread()); |
| + DCHECK(!callback.is_null()); |
| + Cancel(); |
| + if (key_.Open(HKEY_LOCAL_MACHINE, key, KEY_NOTIFY) != ERROR_SUCCESS) |
| + return false; |
| + if (key_.StartWatching() != ERROR_SUCCESS) |
| + return false; |
| + if (!watcher_.StartWatching(key_.watch_event(), this)) |
| + return false; |
| + callback_ = callback; |
| + return true; |
| + } |
| + |
| + void Cancel() { |
| + DCHECK(CalledOnValidThread()); |
| callback_.Reset(); |
| if (key_.Valid()) { |
| watcher_.StopWatching(); |
| @@ -111,7 +123,15 @@ class RegistryWatcher : public base::win::ObjectWatcher::Delegate { |
| } |
| } |
| - mutable base::Lock lock_; |
| + virtual void OnObjectSignaled(HANDLE object) OVERRIDE { |
| + DCHECK(CalledOnValidThread()); |
| + bool succeeded = (key_.StartWatching() == ERROR_SUCCESS) && |
| + watcher_.StartWatching(key_.watch_event(), this); |
|
mmenke
2012/03/19 15:18:01
nit: Think this should be indented one more space
|
| + if (!callback_.is_null()) |
| + callback_.Run(succeeded); |
| + } |
| + |
| + private: |
| CallbackType callback_; |
| base::win::RegKey key_; |
| base::win::ObjectWatcher watcher_; |
| @@ -323,15 +343,11 @@ class DnsConfigServiceWin::ConfigReader : public SerialWorker { |
| base::Bind(&ConfigReader::OnChange, base::Unretained(this)); |
| // The Tcpip key must be present. |
| - if (!tcpip_watcher_.Watch( |
| - L"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters", |
| - callback)) |
| + if (!tcpip_watcher_.Watch(kTcpipPath, callback)) |
| return false; |
| // Watch for IPv6 nameservers. |
| - tcpip6_watcher_.Watch( |
| - L"SYSTEM\\CurrentControlSet\\Services\\Tcpip6\\Parameters", |
| - callback); |
| + tcpip6_watcher_.Watch(kTcpip6Path, 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 |
| @@ -339,13 +355,8 @@ class DnsConfigServiceWin::ConfigReader : public SerialWorker { |
| // If a policy is installed, DnsConfigService will need to be restarted. |
| // BUG=99509 |
| - dnscache_watcher_.Watch( |
| - L"SYSTEM\\CurrentControlSet\\Services\\Dnscache\\Parameters", |
| - callback); |
| - |
| - policy_watcher_.Watch( |
| - L"SOFTWARE\\Policies\\Microsoft\\Windows NT\\DNSClient", |
| - callback); |
| + dnscache_watcher_.Watch(kDnscachePath, callback); |
| + policy_watcher_.Watch(kPolicyPath, callback); |
| WorkNow(); |
| return true; |
| @@ -398,10 +409,10 @@ class DnsConfigServiceWin::ConfigReader : public SerialWorker { |
| return (rv == NO_ERROR); |
| } |
| - bool ReadDevolutionSetting(const RegistryWatcher& watcher, |
| + bool ReadDevolutionSetting(const RegistryReader& reader, |
| DnsSystemSettings::DevolutionSetting& setting) { |
| - return watcher.ReadDword(L"UseDomainNameDevolution", &setting.enabled) && |
| - watcher.ReadDword(L"DomainNameDevolutionLevel", &setting.level); |
| + return reader.ReadDword(L"UseDomainNameDevolution", &setting.enabled) && |
| + reader.ReadDword(L"DomainNameDevolutionLevel", &setting.level); |
| } |
| virtual void DoWork() OVERRIDE { |
| @@ -413,28 +424,33 @@ class DnsConfigServiceWin::ConfigReader : public SerialWorker { |
| if (!ReadIpHelper(&settings)) |
| return; // no point reading the rest |
| - if (!policy_watcher_.ReadString(L"SearchList", |
| - &settings.policy_search_list)) |
| + RegistryReader tcpip_reader(kTcpipPath); |
| + RegistryReader tcpip6_reader(kTcpip6Path); |
| + RegistryReader dnscache_reader(kDnscachePath); |
| + RegistryReader policy_reader(kPolicyPath); |
| + |
| + if (!policy_reader.ReadString(L"SearchList", |
| + &settings.policy_search_list)) |
| return; |
| - if (!tcpip_watcher_.ReadString(L"SearchList", &settings.tcpip_search_list)) |
| + if (!tcpip_reader.ReadString(L"SearchList", &settings.tcpip_search_list)) |
| return; |
| - if (!tcpip_watcher_.ReadString(L"Domain", &settings.tcpip_domain)) |
| + if (!tcpip_reader.ReadString(L"Domain", &settings.tcpip_domain)) |
| return; |
| - if (!ReadDevolutionSetting(policy_watcher_, settings.policy_devolution)) |
| + if (!ReadDevolutionSetting(policy_reader, settings.policy_devolution)) |
| return; |
| - if (!ReadDevolutionSetting(dnscache_watcher_, |
| + if (!ReadDevolutionSetting(dnscache_reader, |
| settings.dnscache_devolution)) |
| return; |
| - if (!ReadDevolutionSetting(tcpip_watcher_, settings.tcpip_devolution)) |
| + if (!ReadDevolutionSetting(tcpip_reader, settings.tcpip_devolution)) |
| return; |
| - if (!policy_watcher_.ReadDword(L"AppendToMultiLabelName", |
| - &settings.append_to_multi_label_name)) |
| + if (!policy_reader.ReadDword(L"AppendToMultiLabelName", |
| + &settings.append_to_multi_label_name)) |
| return; |
| success_ = ConvertSettingsToDnsConfig(settings, &dns_config_); |
| @@ -461,15 +477,120 @@ class DnsConfigServiceWin::ConfigReader : public SerialWorker { |
| RegistryWatcher policy_watcher_; |
| }; |
| + |
| +// An extension for DnsHostsReader which also reads local name from |
| +// GetComputerNameEx and watches registry for changes to local name. |
| +class DnsConfigServiceWin::HostsReader : public base::NonThreadSafe { |
| + public: |
| + explicit HostsReader(DnsConfigServiceWin* service) : service_(service) { |
| + 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_reader_ = new DnsHostsReader( |
| + hosts_path, |
| + base::Bind(&HostsReader::OnHostsRead, base::Unretained(this))); |
| + } |
| + |
| + bool Watch() { |
| + DCHECK(CalledOnValidThread()); |
| + if (!hosts_watcher_.Watch(hosts_reader_->path(), |
| + base::Bind(&HostsReader::OnHostsChanged, |
| + base::Unretained(this)))) { |
| + return false; |
| + } |
| + if (!registry_watcher_.Watch(kTcpipPath, |
| + base::Bind(&HostsReader::OnNameChanged, |
| + base::Unretained(this)))) { |
|
mmenke
2012/03/19 15:18:01
nit: Indent this to the open paren, if it fits, s
|
| + return false; |
| + } |
| + |
| + hosts_reader_->WorkNow(); |
| + return true; |
| + } |
| + |
| + ~HostsReader() { |
| + hosts_reader_->Cancel(); |
| + } |
| + |
| + private: |
| + void OnHostsChanged(bool succeeded) { |
| + DCHECK(CalledOnValidThread()); |
| + service_->InvalidateHosts(); |
| + if (succeeded) |
| + hosts_reader_->WorkNow(); |
| + else |
| + LOG(ERROR) << "Failed to watch DNS hosts"; |
| + } |
| + |
| + void OnHostsRead(const DnsHosts& hosts) { |
| + DCHECK(CalledOnValidThread()); |
| + hosts_ = hosts; |
| + UpdateService(); |
| + } |
| + |
| + void OnNameChanged(bool succeeded) { |
| + DCHECK(CalledOnValidThread()); |
| + if (!succeeded) { |
| + LOG(ERROR) << "Failed to watch local computer name"; |
| + service_->InvalidateHosts(); |
| + return; |
| + } |
| + WCHAR buffer[MAX_PATH]; |
| + DWORD size = MAX_PATH; |
| + std::string localname; |
| + if (!GetComputerNameExW(ComputerNameDnsHostname, buffer, &size) || |
| + !ParseDomainASCII(buffer, &localname)) { |
| + service_->InvalidateHosts(); |
| + LOG(ERROR) << "Failed to read local computer name"; |
| + return; |
| + } |
| + |
| + if (localname != localname_) { |
| + localname_ = localname; |
| + UpdateService(); |
|
mmenke
2012/03/19 15:18:01
What if the last call to OnHostsChanged failed?
S
|
| + } |
| + } |
| + |
| + void UpdateService() { |
| + DCHECK(CalledOnValidThread()); |
| + DnsHosts complete_hosts = hosts_; |
| + |
| + const unsigned char kIPv4Localhost[] = { 127, 0, 0, 1 }; |
| + const unsigned char kIPv6Localhost[] = { 0, 0, 0, 0, 0, 0, 0, 0, |
| + 0, 0, 0, 0, 0, 0, 0, 1 }; |
| + |
| + complete_hosts[DnsHostsKey(localname_, ADDRESS_FAMILY_IPV4)] = |
| + IPAddressNumber(kIPv4Localhost, |
| + kIPv4Localhost + arraysize(kIPv4Localhost)); |
| + complete_hosts[DnsHostsKey(localname_, ADDRESS_FAMILY_IPV6)] = |
| + IPAddressNumber(kIPv6Localhost, |
| + kIPv6Localhost + arraysize(kIPv6Localhost)); |
| + service_->OnHostsRead(complete_hosts); |
| + } |
| + |
| + DnsConfigServiceWin* service_; |
| + FilePathWatcherWrapper hosts_watcher_; |
| + RegistryWatcher registry_watcher_; |
| + scoped_refptr<DnsHostsReader> hosts_reader_; |
| + |
| + // Contents of the HOSTS file. |
| + DnsHosts hosts_; |
| + // Local computer name in ASCII. |
| + std::string localname_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(HostsReader); |
| +}; |
| + |
| + |
| DnsConfigServiceWin::DnsConfigServiceWin() |
| : config_reader_(new ConfigReader(this)), |
| - hosts_watcher_(new FilePathWatcherWrapper()) {} |
| + hosts_reader_(new HostsReader(this)) {} |
| DnsConfigServiceWin::~DnsConfigServiceWin() { |
| DCHECK(CalledOnValidThread()); |
| config_reader_->Cancel(); |
| - if (hosts_reader_) |
| - hosts_reader_->Cancel(); |
| } |
| void DnsConfigServiceWin::Watch(const CallbackType& callback) { |
| @@ -487,30 +608,12 @@ void DnsConfigServiceWin::Watch(const CallbackType& callback) { |
| 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_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); |
| + if (!hosts_reader_->Watch()) { |
| + LOG(ERROR) << "Failed to start watching HOSTS"; |
| + InvalidateHosts(); |
| } |
| } |
| -void DnsConfigServiceWin::OnHostsChanged(bool succeeded) { |
| - InvalidateHosts(); |
| - if (succeeded) |
| - hosts_reader_->WorkNow(); |
| - else |
| - LOG(ERROR) << "Failed to watch DNS hosts"; |
| -} |
|
mmenke
2012/03/19 15:18:01
nit: Remove extra blank line.
|
| } // namespace internal |