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

Unified Diff: net/dns/dns_config_service_win.cc

Issue 9721002: [net/dns] Removes locking from DnsConfigServiceWin and adds local computer name. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Extract local IP from GetAdapterAddresses. Created 8 years, 9 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « net/dns/dns_config_service_win.h ('k') | net/dns/dns_hosts.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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..edd32dbc6d5618401f28010e4b7d986c919257e9 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"
@@ -23,6 +24,7 @@
#include "base/win/windows_version.h"
#include "googleurl/src/url_canon.h"
#include "net/base/net_util.h"
+#include "net/base/network_change_notifier.h"
#include "net/dns/dns_protocol.h"
#include "net/dns/file_path_watcher_wrapper.h"
#include "net/dns/serial_worker.h"
@@ -35,43 +37,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 +73,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,16 +88,60 @@ 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;
+ }
+
+ bool IsWatching() const {
+ DCHECK(CalledOnValidThread());
+ return !callback_.is_null();
+ }
+
+ void Cancel() {
+ DCHECK(CalledOnValidThread());
callback_.Reset();
if (key_.Valid()) {
watcher_.StopWatching();
key_.StopWatching();
+ key_.Close();
}
}
- mutable base::Lock lock_;
+ virtual void OnObjectSignaled(HANDLE object) OVERRIDE {
+ DCHECK(CalledOnValidThread());
+ bool succeeded = (key_.StartWatching() == ERROR_SUCCESS) &&
+ watcher_.StartWatching(key_.watch_event(), this);
+ CallbackType callback = callback_;
+ if (!succeeded)
+ Cancel();
+ if (!callback.is_null())
+ callback.Run(succeeded);
+ }
+
+ private:
CallbackType callback_;
base::win::RegKey key_;
base::win::ObjectWatcher watcher_;
@@ -119,6 +149,27 @@ class RegistryWatcher : public base::win::ObjectWatcher::Delegate {
DISALLOW_COPY_AND_ASSIGN(RegistryWatcher);
};
+// Returns NULL if failed.
+scoped_ptr_malloc<IP_ADAPTER_ADDRESSES> ReadIpHelper(ULONG flags) {
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ scoped_ptr_malloc<IP_ADAPTER_ADDRESSES> out;
+ ULONG len = 15000; // As recommended by MSDN for GetAdaptersAddresses.
+ UINT rv = ERROR_BUFFER_OVERFLOW;
+ // Try up to three times.
+ for (unsigned tries = 0;
+ (tries < 3) && (rv == ERROR_BUFFER_OVERFLOW);
+ tries++) {
+ out.reset(
+ reinterpret_cast<PIP_ADAPTER_ADDRESSES>(malloc(len)));
+ rv = GetAdaptersAddresses(AF_UNSPEC, flags, NULL,
+ out.get(), &len);
+ }
+ if (rv != NO_ERROR)
+ out.reset();
+ return out.Pass();
+}
+
// Converts a string16 domain name to ASCII, possibly using punycode.
// Returns true if the conversion succeeds and output is not empty. In case of
// failure, |domain| might become dirty.
@@ -323,15 +374,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 +386,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;
@@ -377,31 +419,10 @@ class DnsConfigServiceWin::ConfigReader : public SerialWorker {
LOG(ERROR) << "Failed to watch DNS config";
}
- bool ReadIpHelper(DnsSystemSettings* settings) {
- base::ThreadRestrictions::AssertIOAllowed();
-
- ULONG flags = GAA_FLAG_SKIP_ANYCAST |
- GAA_FLAG_SKIP_UNICAST |
- GAA_FLAG_SKIP_MULTICAST |
- GAA_FLAG_SKIP_FRIENDLY_NAME;
- ULONG len = 15000; // As recommended by MSDN for GetAdaptersAddresses.
- UINT rv = ERROR_BUFFER_OVERFLOW;
- // Try up to three times.
- for (unsigned tries = 0;
- (tries < 3) && (rv == ERROR_BUFFER_OVERFLOW);
- tries++) {
- settings->addresses.reset(
- reinterpret_cast<PIP_ADAPTER_ADDRESSES>(malloc(len)));
- rv = GetAdaptersAddresses(AF_UNSPEC, flags, NULL,
- settings->addresses.get(), &len);
- }
- 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 {
@@ -410,31 +431,40 @@ class DnsConfigServiceWin::ConfigReader : public SerialWorker {
DnsSystemSettings settings;
memset(&settings, 0, sizeof(settings));
- if (!ReadIpHelper(&settings))
+ settings.addresses = ReadIpHelper(GAA_FLAG_SKIP_ANYCAST |
+ GAA_FLAG_SKIP_UNICAST |
+ GAA_FLAG_SKIP_MULTICAST |
+ GAA_FLAG_SKIP_FRIENDLY_NAME);
+ if (!settings.addresses.get())
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 +491,172 @@ class DnsConfigServiceWin::ConfigReader : public SerialWorker {
RegistryWatcher policy_watcher_;
};
+FilePath GetHostsPath() {
+ TCHAR buffer[MAX_PATH];
+ UINT rc = GetSystemDirectory(buffer, MAX_PATH);
+ DCHECK(0 < rc && rc < MAX_PATH);
+ return FilePath(buffer).Append(FILE_PATH_LITERAL("drivers\\etc\\hosts"));
+}
+
+// An extension for DnsHostsReader which also reads local name from
+// GetComputerNameEx and watches registry for changes to local name.
mmenke 2012/03/22 16:35:05 It also watches the HOSTs file, which DnsHostsRead
+class DnsConfigServiceWin::HostsReader
+ : public DnsHostsReader,
+ public NetworkChangeNotifier::IPAddressObserver {
+ public:
+ explicit HostsReader(DnsConfigServiceWin* service)
+ : DnsHostsReader(GetHostsPath()), service_(service) {
+ }
+
+ bool Watch() {
+ DCHECK(loop()->BelongsToCurrentThread());
+
+ // In case the reader is restarted, remove it from the observer list.
+ NetworkChangeNotifier::RemoveIPAddressObserver(this);
mmenke 2012/03/22 16:35:05 Shouldn't we just call Cancel()? Alternatively, h
szym 2012/03/22 18:42:45 SerialWorker::Cancel() cannot be undone. Making it
+
+ if (!hosts_watcher_.Watch(path(),
+ base::Bind(&HostsReader::OnHostsChanged,
+ base::Unretained(this)))) {
+ return false;
+ }
+ NetworkChangeNotifier::AddIPAddressObserver(this);
+ WorkNow();
+ return true;
+ }
+
+ void Cancel() {
+ DnsHostsReader::Cancel();
+ hosts_watcher_.Cancel();
+ NetworkChangeNotifier::RemoveIPAddressObserver(this);
+ }
+
+ private:
+ virtual void OnIPAddressChanged() OVERRIDE {
+ DCHECK(loop()->BelongsToCurrentThread());
+ service_->InvalidateHosts();
+ if (!hosts_watcher_.IsWatching())
+ return;
+ WorkNow();
+ }
+
+ void OnHostsChanged(bool succeeded) {
+ DCHECK(loop()->BelongsToCurrentThread());
+ service_->InvalidateHosts();
+ if (succeeded)
+ WorkNow();
+ else
+ LOG(ERROR) << "Failed to watch DNS hosts";
+ }
+
+ virtual void DoWork() OVERRIDE {
+ DnsHostsReader::DoWork();
+
+ if (!success_)
+ return;
+
+ // Default address of local computer name and "localhost" can be overridden
+ // by the HOSTS file, but if it's not there, then we need to fill it in.
+
+ success_ = false;
+
+ WCHAR buffer[MAX_PATH];
+ DWORD size = MAX_PATH;
+ std::string localname;
+ if (!GetComputerNameExW(ComputerNameDnsHostname, buffer, &size) ||
+ !ParseDomainASCII(buffer, &localname)) {
+ LOG(ERROR) << "Failed to read local computer name";
+ return;
+ }
+ // TODO(szym): normalize |localname| to lowercase. http://crbug.com/118995
+
+ scoped_ptr_malloc<IP_ADAPTER_ADDRESSES> addresses =
+ ReadIpHelper(GAA_FLAG_SKIP_ANYCAST |
+ GAA_FLAG_SKIP_DNS_SERVER |
+ GAA_FLAG_SKIP_MULTICAST |
+ GAA_FLAG_SKIP_FRIENDLY_NAME);
+ if (!addresses.get())
+ return;
+
+ // The order of adapters is the network binding order, so stick to the
+ // first good adapter for each family.
+ IPAddressNumber& local_ipv4 =
+ dns_hosts_[DnsHostsKey(localname, ADDRESS_FAMILY_IPV4)];
+ IPAddressNumber& local_ipv6 =
+ dns_hosts_[DnsHostsKey(localname, ADDRESS_FAMILY_IPV6)];
mmenke 2012/03/22 16:35:05 This isn't safe. If we insert an entry into dns_h
mmenke 2012/03/22 16:35:05 Also, suggest you use pointers here instead of ref
szym 2012/03/22 18:42:45 The references (like iterators) in std::map are no
mmenke 2012/03/22 19:36:08 I had not realized that Very handy, though I agre
+ for (const IP_ADAPTER_ADDRESSES* adapter = addresses.get();
+ adapter != NULL && (local_ipv4.empty() || local_ipv6.empty());
+ adapter = adapter->Next) {
+ if (adapter->OperStatus != IfOperStatusUp)
+ continue;
+ if (adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK)
+ continue;
+
+ for (const IP_ADAPTER_UNICAST_ADDRESS* address =
+ adapter->FirstUnicastAddress;
mmenke 2012/03/22 16:35:05 nit: 4-space indent
+ address != NULL;
+ address = address->Next) {
+ IPEndPoint ipe;
+ if (ipe.FromSockAddr(address->Address.lpSockaddr,
+ address->Address.iSockaddrLength)) {
mmenke 2012/03/22 16:35:05 nit: Think this would be a little easier to read
+ if (local_ipv4.empty() && (ipe.GetFamily() == AF_INET)) {
+ local_ipv4 = ipe.address();
+ } else if (local_ipv6.empty() && (ipe.GetFamily() == AF_INET6)) {
+ local_ipv6 = ipe.address();
+ }
+ } else {
+ return;
+ }
+ }
+ }
+
+ if (local_ipv4.empty())
+ dns_hosts_.erase(DnsHostsKey(localname, ADDRESS_FAMILY_IPV4));
+
+ if (local_ipv6.empty())
+ dns_hosts_.erase(DnsHostsKey(localname, ADDRESS_FAMILY_IPV6));
+
+ IPAddressNumber& loop_ipv4 =
+ dns_hosts_[DnsHostsKey("localhost", ADDRESS_FAMILY_IPV4)];
+ IPAddressNumber& loop_ipv6 =
+ dns_hosts_[DnsHostsKey("localhost", ADDRESS_FAMILY_IPV6)];
mmenke 2012/03/22 16:35:05 Again, suggest you use pointers. Also think "loop
szym 2012/03/22 18:42:45 I re-discovered that std::map::insert will not ove
+ if (loop_ipv4.empty()) {
+ const unsigned char kIPv4Localhost[] = { 127, 0, 0, 1 };
+ loop_ipv4 = IPAddressNumber(kIPv4Localhost,
+ kIPv4Localhost + arraysize(kIPv4Localhost));
+ }
+
+ if (loop_ipv6.empty()) {
+ const unsigned char kIPv6Localhost[] = { 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1 };
+ loop_ipv6 = IPAddressNumber(kIPv6Localhost,
+ kIPv6Localhost + arraysize(kIPv6Localhost));
+ }
+
+ success_ = true;
+ }
+
+ virtual void OnWorkFinished() OVERRIDE {
+ DCHECK(loop()->BelongsToCurrentThread());
+ if (!success_ || !hosts_watcher_.IsWatching())
+ return;
+ service_->OnHostsRead(dns_hosts_);
+ }
+
+ DnsConfigServiceWin* service_;
+ FilePathWatcherWrapper hosts_watcher_;
+
+ 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();
+ hosts_reader_->Cancel();
}
void DnsConfigServiceWin::Watch(const CallbackType& callback) {
@@ -487,31 +674,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";
-}
-
} // namespace internal
// static
« no previous file with comments | « net/dns/dns_config_service_win.h ('k') | net/dns/dns_hosts.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698