summaryrefslogtreecommitdiffstats
path: root/net/dns
diff options
context:
space:
mode:
authorszym@chromium.org <szym@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-05-16 18:10:53 +0000
committerszym@chromium.org <szym@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-05-16 18:10:53 +0000
commit05aad32d741012a173ea8f1a4b5072dabe7c0188 (patch)
treef9b8535284f0503ef96bac3a451f995b5fcd508f /net/dns
parent1a92b28469774982203a8c88be0bb49d8275cf68 (diff)
downloadchromium_src-05aad32d741012a173ea8f1a4b5072dabe7c0188.zip
chromium_src-05aad32d741012a173ea8f1a4b5072dabe7c0188.tar.gz
chromium_src-05aad32d741012a173ea8f1a4b5072dabe7c0188.tar.bz2
[net/dns] Isolate DnsConfigWatcher from DnsConfigService.
DnsConfigWatcher is installed at NetworkChangeNotifier and provides signals to DNSObservers. DnsConfigService becomes a DNSObserver. BUG=114827,114223,128166 TEST=./net_unittests --gtest_filter=DnsConfigService* Review URL: https://chromiumcodereview.appspot.com/10377092 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@137457 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/dns')
-rw-r--r--net/dns/dns_config_service.cc24
-rw-r--r--net/dns/dns_config_service.h33
-rw-r--r--net/dns/dns_config_service_posix.cc90
-rw-r--r--net/dns/dns_config_service_posix.h16
-rw-r--r--net/dns/dns_config_service_unittest.cc12
-rw-r--r--net/dns/dns_config_service_win.cc231
-rw-r--r--net/dns/dns_config_service_win.h71
-rw-r--r--net/dns/dns_config_watcher.h45
-rw-r--r--net/dns/dns_config_watcher_posix.cc131
-rw-r--r--net/dns/dns_config_watcher_win.cc167
-rw-r--r--net/dns/dns_test_util.cc14
-rw-r--r--net/dns/dns_test_util.h14
12 files changed, 513 insertions, 335 deletions
diff --git a/net/dns/dns_config_service.cc b/net/dns/dns_config_service.cc
index ffd5fe9..d189def 100644
--- a/net/dns/dns_config_service.cc
+++ b/net/dns/dns_config_service.cc
@@ -77,7 +77,29 @@ DnsConfigService::DnsConfigService()
have_hosts_(false),
need_update_(false) {}
-DnsConfigService::~DnsConfigService() {}
+DnsConfigService::~DnsConfigService() {
+ // Must always clean up.
+ NetworkChangeNotifier::RemoveDNSObserver(this);
+}
+
+void DnsConfigService::Read(const CallbackType& callback) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(!callback.is_null());
+ DCHECK(callback_.is_null());
+ callback_ = callback;
+ OnDNSChanged(NetworkChangeNotifier::CHANGE_DNS_WATCH_STARTED);
+}
+
+void DnsConfigService::Watch(const CallbackType& callback) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(!callback.is_null());
+ DCHECK(callback_.is_null());
+ NetworkChangeNotifier::AddDNSObserver(this);
+ callback_ = callback;
+ if (NetworkChangeNotifier::IsWatchingDNS())
+ OnDNSChanged(NetworkChangeNotifier::CHANGE_DNS_WATCH_STARTED);
+ // else: Wait until signal before reading.
+}
void DnsConfigService::InvalidateConfig() {
DCHECK(CalledOnValidThread());
diff --git a/net/dns/dns_config_service.h b/net/dns/dns_config_service.h
index 6b3e9f3..bc3542d 100644
--- a/net/dns/dns_config_service.h
+++ b/net/dns/dns_config_service.h
@@ -19,6 +19,7 @@
// std::vector<IPEndPoint>.
#include "net/base/address_list.h"
#include "net/base/ip_endpoint.h" // win requires size of IPEndPoint
+#include "net/base/network_change_notifier.h"
#include "net/base/net_export.h"
#include "net/dns/dns_hosts.h"
@@ -75,12 +76,14 @@ struct NET_EXPORT_PRIVATE DnsConfig {
};
-// Service for watching when the system DNS settings have changed.
-// Depending on the platform, watches files in /etc/ or Windows registry.
+// Service for reading system DNS settings, on demand or when signalled by
+// NetworkChangeNotifier.
class NET_EXPORT_PRIVATE DnsConfigService
- : NON_EXPORTED_BASE(public base::NonThreadSafe) {
+ : NON_EXPORTED_BASE(public base::NonThreadSafe),
+ public NetworkChangeNotifier::DNSObserver {
public:
- // Callback interface for the client, called on the same thread as Watch().
+ // Callback interface for the client, called on the same thread as Read() and
+ // Watch().
typedef base::Callback<void(const DnsConfig& config)> CallbackType;
// Creates the platform-specific DnsConfigService.
@@ -89,10 +92,15 @@ class NET_EXPORT_PRIVATE DnsConfigService
DnsConfigService();
virtual ~DnsConfigService();
- // Immediately starts watching system configuration for changes and attempts
- // to read the configuration. For some platform implementations, the current
- // thread must have an IO loop (for base::files::FilePathWatcher).
- virtual void Watch(const CallbackType& callback) = 0;
+ // Attempts to read the configuration. Will run |callback| when succeeded.
+ // Can be called at most once.
+ void Read(const CallbackType& callback);
+
+ // Registers for notifications at NetworkChangeNotifier. Will attempt to read
+ // config after watch is started by NetworkChangeNotifier. Will run |callback|
+ // iff config changes from last call or should be withdrawn.
+ // Can be called at most once.
+ virtual void Watch(const CallbackType& callback);
protected:
friend class DnsHostsReader;
@@ -107,9 +115,9 @@ class NET_EXPORT_PRIVATE DnsConfigService
// Called with new hosts. Rest of the config is assumed unchanged.
void OnHostsRead(const DnsHosts& hosts);
- void set_callback(const CallbackType& callback) {
- callback_ = callback;
- }
+ // NetworkChangeNotifier::DNSObserver:
+ // Must be defined by implementations.
+ virtual void OnDNSChanged(unsigned detail) OVERRIDE = 0;
private:
void StartTimer();
@@ -122,7 +130,7 @@ class NET_EXPORT_PRIVATE DnsConfigService
DnsConfig dns_config_;
- // True after On*Read, before Invalidate*. Tell if the config is complete.
+ // True after On*Read, before Invalidate*. Tells if the config is complete.
bool have_config_;
bool have_hosts_;
// True if receiver needs to be updated when the config becomes complete.
@@ -131,7 +139,6 @@ class NET_EXPORT_PRIVATE DnsConfigService
// Started in Invalidate*, cleared in On*Read.
base::OneShotTimer<DnsConfigService> timer_;
- private:
DISALLOW_COPY_AND_ASSIGN(DnsConfigService);
};
diff --git a/net/dns/dns_config_service_posix.cc b/net/dns/dns_config_service_posix.cc
index a53739e..11a48a5 100644
--- a/net/dns/dns_config_service_posix.cc
+++ b/net/dns/dns_config_service_posix.cc
@@ -16,18 +16,14 @@
#include "net/dns/file_path_watcher_wrapper.h"
#include "net/dns/serial_worker.h"
-#if defined(OS_MACOSX)
-#include "net/dns/notify_watcher_mac.h"
-#endif
+namespace net {
+
+namespace {
#ifndef _PATH_RESCONF // Normally defined in <resolv.h>
#define _PATH_RESCONF "/etc/resolv.conf"
#endif
-namespace net {
-
-namespace {
-
const FilePath::CharType* kFilePathHosts = FILE_PATH_LITERAL("/etc/hosts");
// A SerialWorker that uses libresolv to initialize res_state and converts
@@ -85,32 +81,7 @@ class ConfigReader : public SerialWorker {
namespace internal {
-#if defined(OS_MACOSX)
-// From 10.7.3 configd-395.10/dnsinfo/dnsinfo.h
-static const char* kDnsNotifyKey =
- "com.apple.system.SystemConfiguration.dns_configuration";
-
-class DnsConfigServicePosix::ConfigWatcher : public NotifyWatcherMac {
- public:
- bool Watch(const base::Callback<void(bool succeeded)>& callback) {
- return NotifyWatcherMac::Watch(kDnsNotifyKey, callback);
- }
-};
-#else
-static const FilePath::CharType* kFilePathConfig =
- FILE_PATH_LITERAL(_PATH_RESCONF);
-
-class DnsConfigServicePosix::ConfigWatcher : public FilePathWatcherWrapper {
- public:
- bool Watch(const base::Callback<void(bool succeeded)>& callback) {
- return FilePathWatcherWrapper::Watch(FilePath(kFilePathConfig), callback);
- }
-};
-#endif
-
-DnsConfigServicePosix::DnsConfigServicePosix()
- : config_watcher_(new ConfigWatcher()),
- hosts_watcher_(new FilePathWatcherWrapper()) {
+DnsConfigServicePosix::DnsConfigServicePosix() {
config_reader_ = new ConfigReader(
base::Bind(&DnsConfigServicePosix::OnConfigRead,
base::Unretained(this)));
@@ -125,46 +96,25 @@ DnsConfigServicePosix::~DnsConfigServicePosix() {
hosts_reader_->Cancel();
}
-void DnsConfigServicePosix::Watch(const CallbackType& callback) {
- DCHECK(CalledOnValidThread());
- DCHECK(!callback.is_null());
- set_callback(callback);
-
- // Even if watchers fail, we keep the other one as it provides useful signals.
- if (config_watcher_->Watch(
- base::Bind(&DnsConfigServicePosix::OnConfigChanged,
- base::Unretained(this)))) {
- OnConfigChanged(true);
- } else {
- OnConfigChanged(false);
- }
-
- if (hosts_watcher_->Watch(
- FilePath(kFilePathHosts),
- base::Bind(&DnsConfigServicePosix::OnHostsChanged,
- base::Unretained(this)))) {
- OnHostsChanged(true);
- } else {
- OnHostsChanged(false);
+void DnsConfigServicePosix::OnDNSChanged(unsigned detail) {
+ if (detail & NetworkChangeNotifier::CHANGE_DNS_WATCH_FAILED) {
+ InvalidateConfig();
+ InvalidateHosts();
+ // We don't trust a config that we cannot watch in the future.
+ config_reader_->Cancel();
+ hosts_reader_->Cancel();
+ return;
}
-}
-
-void DnsConfigServicePosix::OnConfigChanged(bool watch_succeeded) {
- 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 (watch_succeeded)
+ if (detail & NetworkChangeNotifier::CHANGE_DNS_WATCH_STARTED)
+ detail = ~0; // Assume everything changed.
+ if (detail & NetworkChangeNotifier::CHANGE_DNS_SETTINGS) {
+ InvalidateConfig();
config_reader_->WorkNow();
- else
- LOG(ERROR) << "Failed to watch DNS config";
-}
-
-void DnsConfigServicePosix::OnHostsChanged(bool watch_succeeded) {
- InvalidateHosts();
- if (watch_succeeded)
+ }
+ if (detail & NetworkChangeNotifier::CHANGE_DNS_HOSTS) {
+ InvalidateHosts();
hosts_reader_->WorkNow();
- else
- LOG(ERROR) << "Failed to watch DNS hosts";
+ }
}
#if !defined(OS_ANDROID)
diff --git a/net/dns/dns_config_service_posix.h b/net/dns/dns_config_service_posix.h
index f28609b..30588efc 100644
--- a/net/dns/dns_config_service_posix.h
+++ b/net/dns/dns_config_service_posix.h
@@ -17,27 +17,17 @@
namespace net {
-class FilePathWatcherWrapper;
-
// Use DnsConfigService::CreateSystemService to use it outside of tests.
namespace internal {
-class NET_EXPORT_PRIVATE DnsConfigServicePosix
- : NON_EXPORTED_BASE(public DnsConfigService) {
+class NET_EXPORT_PRIVATE DnsConfigServicePosix : public DnsConfigService {
public:
DnsConfigServicePosix();
virtual ~DnsConfigServicePosix();
- virtual void Watch(const CallbackType& callback) OVERRIDE;
-
private:
- class ConfigWatcher;
-
- void OnConfigChanged(bool watch_succeeded);
- void OnHostsChanged(bool watch_succeeded);
-
- scoped_ptr<ConfigWatcher> config_watcher_;
- scoped_ptr<FilePathWatcherWrapper> hosts_watcher_;
+ // NetworkChangeNotifier::DNSObserver:
+ virtual void OnDNSChanged(unsigned detail) OVERRIDE;
scoped_refptr<SerialWorker> config_reader_;
scoped_refptr<SerialWorker> hosts_reader_;
diff --git a/net/dns/dns_config_service_unittest.cc b/net/dns/dns_config_service_unittest.cc
index 9d27cb7..e68db27 100644
--- a/net/dns/dns_config_service_unittest.cc
+++ b/net/dns/dns_config_service_unittest.cc
@@ -29,9 +29,7 @@ class DnsConfigServiceTest : public testing::Test {
protected:
class TestDnsConfigService : public DnsConfigService {
public:
- virtual void Watch(const CallbackType& callback) OVERRIDE {
- set_callback(callback);
- }
+ virtual void OnDNSChanged(unsigned detail) OVERRIDE {}
// Expose the protected methods to this test suite.
void InvalidateConfig() {
@@ -189,16 +187,12 @@ TEST_F(DnsConfigServiceTest, FLAKY_GetSystemConfig) {
service_.reset();
scoped_ptr<DnsConfigService> service(DnsConfigService::CreateSystemService());
- service->Watch(base::Bind(&DnsConfigServiceTest::OnConfigChanged,
- base::Unretained(this)));
+ service->Read(base::Bind(&DnsConfigServiceTest::OnConfigChanged,
+ base::Unretained(this)));
base::TimeDelta kTimeout = TestTimeouts::action_max_timeout();
WaitForConfig(kTimeout);
ASSERT_TRUE(last_config_.IsValid()) << "Did not receive DnsConfig in " <<
kTimeout.InSecondsF() << "s";
-
- // Restart watch to confirm it's allowed.
- service->Watch(base::Bind(&DnsConfigServiceTest::OnConfigChanged,
- base::Unretained(this)));
}
#endif // OS_POSIX || OS_WIN
diff --git a/net/dns/dns_config_service_win.cc b/net/dns/dns_config_service_win.cc
index 327ea2a..c64f651 100644
--- a/net/dns/dns_config_service_win.cc
+++ b/net/dns/dns_config_service_win.cc
@@ -19,14 +19,12 @@
#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"
#include "base/win/registry.h"
#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"
#pragma comment(lib, "iphlpapi.lib")
@@ -37,15 +35,6 @@ namespace internal {
namespace {
-// 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";
const wchar_t* const kPrimaryDnsSuffixPath =
L"SOFTWARE\\Policies\\Microsoft\\System\\DNSClient";
@@ -95,62 +84,6 @@ class RegistryReader : public base::NonThreadSafe {
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();
- }
- }
-
- 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_;
-
- DISALLOW_COPY_AND_ASSIGN(RegistryWatcher);
-};
-
// Returns NULL if failed.
scoped_ptr_malloc<IP_ADAPTER_ADDRESSES> ReadIpHelper(ULONG flags) {
base::ThreadRestrictions::AssertIOAllowed();
@@ -200,6 +133,13 @@ bool ParseDomainASCII(const string16& widestr, std::string* domain) {
} // namespace
+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"));
+}
+
bool ParseSearchList(const string16& value, std::vector<std::string>* output) {
DCHECK(output);
if (value.empty())
@@ -378,58 +318,7 @@ class DnsConfigServiceWin::ConfigReader : public SerialWorker {
: service_(service),
success_(false) {}
- bool Watch() {
- DCHECK(loop()->BelongsToCurrentThread());
-
- RegistryWatcher::CallbackType callback =
- base::Bind(&ConfigReader::OnChange, base::Unretained(this));
-
- // The Tcpip key must be present.
- if (!tcpip_watcher_.Watch(kTcpipPath, callback))
- return false;
-
- // Watch for IPv6 nameservers.
- 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
- // 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
-
- dnscache_watcher_.Watch(kDnscachePath, callback);
- policy_watcher_.Watch(kPolicyPath, callback);
-
- WorkNow();
- return true;
- }
-
- void Cancel() {
- DCHECK(loop()->BelongsToCurrentThread());
- SerialWorker::Cancel();
- policy_watcher_.Cancel();
- dnscache_watcher_.Cancel();
- tcpip6_watcher_.Cancel();
- tcpip_watcher_.Cancel();
- }
-
private:
- 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 ReadDevolutionSetting(const RegistryReader& reader,
DnsSystemSettings::DevolutionSetting& setting) {
return reader.ReadDword(L"UseDomainNameDevolution", &setting.enabled) &&
@@ -500,73 +389,18 @@ class DnsConfigServiceWin::ConfigReader : public SerialWorker {
// Written in DoRead(), read in OnReadFinished(). No locking required.
DnsConfig dns_config_;
bool success_;
-
- RegistryWatcher tcpip_watcher_;
- RegistryWatcher tcpip6_watcher_;
- RegistryWatcher dnscache_watcher_;
- 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 watches the HOSTS file,
// reads local name from GetComputerNameEx, local IP from GetAdaptersAddresses,
// and observes changes to local IP address.
-class DnsConfigServiceWin::HostsReader
- : public DnsHostsReader,
- public NetworkChangeNotifier::IPAddressObserver {
+class DnsConfigServiceWin::HostsReader : public DnsHostsReader {
public:
explicit HostsReader(DnsConfigServiceWin* service)
: DnsHostsReader(GetHostsPath()), service_(service) {
}
- bool Watch() {
- DCHECK(loop()->BelongsToCurrentThread());
- DCHECK(!IsCancelled());
-
- // In case the reader is restarted, remove it from the observer list.
- NetworkChangeNotifier::RemoveIPAddressObserver(this);
-
- if (!hosts_watcher_.Watch(path(),
- base::Bind(&HostsReader::OnHostsChanged,
- base::Unretained(this)))) {
- return false;
- }
- NetworkChangeNotifier::AddIPAddressObserver(this);
- WorkNow();
- return true;
- }
-
- // Cancels the underlying SerialWorker. Cannot be undone.
- 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();
@@ -658,18 +492,18 @@ class DnsConfigServiceWin::HostsReader
virtual void OnWorkFinished() OVERRIDE {
DCHECK(loop()->BelongsToCurrentThread());
- if (!success_ || !hosts_watcher_.IsWatching())
- return;
- service_->OnHostsRead(dns_hosts_);
+ if (success_) {
+ service_->OnHostsRead(dns_hosts_);
+ } else {
+ LOG(WARNING) << "Failed to read hosts.";
+ }
}
DnsConfigServiceWin* service_;
- FilePathWatcherWrapper hosts_watcher_;
DISALLOW_COPY_AND_ASSIGN(HostsReader);
};
-
DnsConfigServiceWin::DnsConfigServiceWin()
: config_reader_(new ConfigReader(this)),
hosts_reader_(new HostsReader(this)) {}
@@ -678,29 +512,42 @@ DnsConfigServiceWin::~DnsConfigServiceWin() {
DCHECK(CalledOnValidThread());
config_reader_->Cancel();
hosts_reader_->Cancel();
+ NetworkChangeNotifier::RemoveIPAddressObserver(this);
}
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;
+ DnsConfigService::Watch(callback);
+ // Also need to observe changes to local non-loopback IP for DnsHosts.
+ NetworkChangeNotifier::AddIPAddressObserver(this);
+}
- if (!config_reader_->Watch()) {
- LOG(ERROR) << "Failed to start watching DNS config";
+void DnsConfigServiceWin::OnDNSChanged(unsigned detail) {
+ if (detail & NetworkChangeNotifier::CHANGE_DNS_WATCH_FAILED) {
InvalidateConfig();
+ InvalidateHosts();
+ // We don't trust a config that we cannot watch in the future.
+ config_reader_->Cancel();
+ hosts_reader_->Cancel();
+ return;
}
-
- if (!hosts_reader_->Watch()) {
- LOG(ERROR) << "Failed to start watching HOSTS";
+ if (detail & NetworkChangeNotifier::CHANGE_DNS_WATCH_STARTED)
+ detail = ~0; // Assume everything changed.
+ if (detail & NetworkChangeNotifier::CHANGE_DNS_SETTINGS) {
+ InvalidateConfig();
+ config_reader_->WorkNow();
+ }
+ if (detail & NetworkChangeNotifier::CHANGE_DNS_HOSTS) {
InvalidateHosts();
+ hosts_reader_->WorkNow();
}
}
+void DnsConfigServiceWin::OnIPAddressChanged() {
+ // Need to update non-loopback IP of local host.
+ if (NetworkChangeNotifier::IsWatchingDNS())
+ OnDNSChanged(NetworkChangeNotifier::CHANGE_DNS_HOSTS);
+}
+
} // namespace internal
// static
diff --git a/net/dns/dns_config_service_win.h b/net/dns/dns_config_service_win.h
index 703eb7a..a502e8b 100644
--- a/net/dns/dns_config_service_win.h
+++ b/net/dns/dns_config_service_win.h
@@ -33,28 +33,27 @@
namespace net {
-class FilePathWatcherWrapper;
-
-// Use DnsConfigService::CreateSystemService to use it outside of tests.
namespace internal {
-class NET_EXPORT_PRIVATE DnsConfigServiceWin
- : NON_EXPORTED_BASE(public DnsConfigService) {
- public:
- DnsConfigServiceWin();
- virtual ~DnsConfigServiceWin();
+// 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";
- virtual void Watch(const CallbackType& callback) OVERRIDE;
+// Returns the path to the HOSTS file.
+FilePath GetHostsPath();
- private:
- class ConfigReader;
- class HostsReader;
-
- scoped_refptr<ConfigReader> config_reader_;
- scoped_refptr<HostsReader> hosts_reader_;
-
- DISALLOW_COPY_AND_ASSIGN(DnsConfigServiceWin);
-};
+// Parses |value| as search list (comma-delimited list of domain names) from
+// a registry key and stores it in |out|. Returns true on success. Empty
+// entries (e.g., "chromium.org,,org") terminate the list. Non-ascii hostnames
+// are converted to punycode.
+bool NET_EXPORT_PRIVATE ParseSearchList(const string16& value,
+ std::vector<std::string>* out);
// All relevant settings read from registry and IP Helper. This isolates our
// logic from system calls and is exposed for unit tests. Keep it an aggregate
@@ -102,16 +101,36 @@ struct NET_EXPORT_PRIVATE DnsSystemSettings {
RegDword append_to_multi_label_name;
};
-// Parses |value| as search list (comma-delimited list of domain names) from
-// a registry key and stores it in |out|. Returns true on success. Empty
-// entries (e.g., "chromium.org,,org") terminate the list. Non-ascii hostnames
-// are converted to punycode.
-bool NET_EXPORT_PRIVATE ParseSearchList(const string16& value,
- std::vector<std::string>* out);
-
// Fills in |dns_config| from |settings|. Exposed for tests.
bool NET_EXPORT_PRIVATE ConvertSettingsToDnsConfig(
- const DnsSystemSettings& settings, DnsConfig* dns_config);
+ const DnsSystemSettings& settings,
+ DnsConfig* dns_config);
+
+// Use DnsConfigService::CreateSystemService to use it outside of tests.
+class NET_EXPORT_PRIVATE DnsConfigServiceWin
+ : public DnsConfigService,
+ public NetworkChangeNotifier::IPAddressObserver {
+ public:
+ DnsConfigServiceWin();
+ virtual ~DnsConfigServiceWin();
+
+ virtual void Watch(const CallbackType& callback) OVERRIDE;
+
+ private:
+ class ConfigReader;
+ class HostsReader;
+
+ // NetworkChangeNotifier::DNSObserver:
+ virtual void OnDNSChanged(unsigned detail) OVERRIDE;
+
+ // NetworkChangeNotifier::IPAddressObserver:
+ virtual void OnIPAddressChanged() OVERRIDE;
+
+ scoped_refptr<ConfigReader> config_reader_;
+ scoped_refptr<HostsReader> hosts_reader_;
+
+ DISALLOW_COPY_AND_ASSIGN(DnsConfigServiceWin);
+};
} // namespace internal
diff --git a/net/dns/dns_config_watcher.h b/net/dns/dns_config_watcher.h
new file mode 100644
index 0000000..04d7056
--- /dev/null
+++ b/net/dns/dns_config_watcher.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_DNS_DNS_CONFIG_WATCHER_H_
+#define NET_DNS_DNS_CONFIG_WATCHER_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+
+namespace net {
+namespace internal {
+
+// Watches when the system DNS settings have changed. It is used by
+// NetworkChangeNotifier to provide signals to registered DNSObservers.
+// Depending on the platform, watches files, Windows registry, or libnotify key.
+// If some watches fail, we keep the working parts, but NetworkChangeNotifier
+// will mark notifications to DNSObserver with the CHANGE_DNS_WATCH_FAILED bit.
+class DnsConfigWatcher {
+ public:
+ DnsConfigWatcher();
+ ~DnsConfigWatcher();
+
+ // Starts watching system configuration for changes. The current thread must
+ // have a MessageLoopForIO. The signals will be delivered directly to
+ // the global NetworkChangeNotifier.
+ void Init();
+
+ // Must be called on the same thread as Init. Required if dtor will be called
+ // on a different thread.
+ void CleanUp();
+
+ private:
+ // Platform-specific implementation.
+ class Core;
+ scoped_ptr<Core> core_;
+
+ DISALLOW_COPY_AND_ASSIGN(DnsConfigWatcher);
+};
+
+} // namespace internal
+} // namespace net
+
+#endif // NET_DNS_DNS_CONFIG_WATCHER_H_
diff --git a/net/dns/dns_config_watcher_posix.cc b/net/dns/dns_config_watcher_posix.cc
new file mode 100644
index 0000000..ebebf51
--- /dev/null
+++ b/net/dns/dns_config_watcher_posix.cc
@@ -0,0 +1,131 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/dns/dns_config_watcher.h"
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/file_path.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/base/network_change_notifier.h"
+#include "net/dns/file_path_watcher_wrapper.h"
+
+#if defined(OS_MACOSX)
+#include "net/dns/notify_watcher_mac.h"
+#endif
+
+namespace net {
+namespace internal {
+
+namespace {
+
+const FilePath::CharType* kFilePathHosts = FILE_PATH_LITERAL("/etc/hosts");
+
+#if defined(OS_MACOSX)
+// From 10.7.3 configd-395.10/dnsinfo/dnsinfo.h
+static const char* kDnsNotifyKey =
+ "com.apple.system.SystemConfiguration.dns_configuration";
+
+class ConfigWatcher {
+ public:
+ bool Watch(const base::Callback<void(bool succeeded)>& callback) {
+ return watcher_.Watch(kDnsNotifyKey, callback);
+ }
+ private:
+ NotifyWatcherMac watcher_;
+};
+#else
+
+#ifndef _PATH_RESCONF // Normally defined in <resolv.h>
+#define _PATH_RESCONF "/etc/resolv.conf"
+#endif
+
+static const FilePath::CharType* kFilePathConfig =
+ FILE_PATH_LITERAL(_PATH_RESCONF);
+
+class ConfigWatcher {
+ public:
+ bool Watch(const base::Callback<void(bool succeeded)>& callback) {
+ return watcher_.Watch(FilePath(kFilePathConfig), callback);
+ }
+ private:
+ FilePathWatcherWrapper watcher_;
+};
+#endif
+
+} // namespace
+
+class DnsConfigWatcher::Core {
+ public:
+ Core() {}
+ ~Core() {}
+
+ bool Watch() {
+ bool success = true;
+ if (!config_watcher_.Watch(base::Bind(&Core::OnConfigChanged,
+ base::Unretained(this)))) {
+ LOG(ERROR) << "DNS config watch failed to start.";
+ success = false;
+ }
+ if (!hosts_watcher_.Watch(FilePath(kFilePathHosts),
+ base::Bind(&Core::OnHostsChanged,
+ base::Unretained(this)))) {
+ LOG(ERROR) << "DNS hosts watch failed to start.";
+ success = false;
+ }
+ return success;
+ }
+
+ private:
+ void OnConfigChanged(bool succeeded) {
+ if (succeeded) {
+ NetworkChangeNotifier::NotifyObserversOfDNSChange(
+ NetworkChangeNotifier::CHANGE_DNS_SETTINGS);
+ } else {
+ LOG(ERROR) << "DNS config watch failed.";
+ NetworkChangeNotifier::NotifyObserversOfDNSChange(
+ NetworkChangeNotifier::CHANGE_DNS_WATCH_FAILED);
+ }
+ }
+
+ void OnHostsChanged(bool succeeded) {
+ if (succeeded) {
+ NetworkChangeNotifier::NotifyObserversOfDNSChange(
+ NetworkChangeNotifier::CHANGE_DNS_HOSTS);
+ } else {
+ LOG(ERROR) << "DNS hosts watch failed.";
+ NetworkChangeNotifier::NotifyObserversOfDNSChange(
+ NetworkChangeNotifier::CHANGE_DNS_WATCH_FAILED);
+ }
+ }
+
+ ConfigWatcher config_watcher_;
+ FilePathWatcherWrapper hosts_watcher_;
+
+ DISALLOW_COPY_AND_ASSIGN(Core);
+};
+
+DnsConfigWatcher::DnsConfigWatcher() {}
+
+DnsConfigWatcher::~DnsConfigWatcher() {}
+
+void DnsConfigWatcher::Init() {
+ core_.reset(new Core());
+ if (core_->Watch()) {
+ NetworkChangeNotifier::NotifyObserversOfDNSChange(
+ NetworkChangeNotifier::CHANGE_DNS_WATCH_STARTED);
+ }
+ // TODO(szym): re-start watcher if that makes sense. http://crbug.com/116139
+}
+
+void DnsConfigWatcher::CleanUp() {
+ core_.reset();
+}
+
+} // namespace internal
+} // namespace net
diff --git a/net/dns/dns_config_watcher_win.cc b/net/dns/dns_config_watcher_win.cc
new file mode 100644
index 0000000..47a12d0
--- /dev/null
+++ b/net/dns/dns_config_watcher_win.cc
@@ -0,0 +1,167 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/dns/dns_config_watcher.h"
+
+#include <winsock2.h>
+
+#include <string>
+
+#include "base/bind.h"
+#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/synchronization/lock.h"
+#include "base/threading/non_thread_safe.h"
+#include "base/win/object_watcher.h"
+#include "base/win/registry.h"
+#include "net/base/net_util.h"
+#include "net/base/network_change_notifier.h"
+#include "net/dns/dns_config_service_win.h"
+#include "net/dns/file_path_watcher_wrapper.h"
+
+namespace net {
+namespace internal {
+
+namespace {
+
+// 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());
+ DCHECK(callback_.is_null());
+ callback_ = callback;
+ 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;
+ return true;
+ }
+
+ virtual void OnObjectSignaled(HANDLE object) OVERRIDE {
+ DCHECK(CalledOnValidThread());
+ bool succeeded = (key_.StartWatching() == ERROR_SUCCESS) &&
+ watcher_.StartWatching(key_.watch_event(), this);
+ if (!succeeded) {
+ if (key_.Valid()) {
+ watcher_.StopWatching();
+ key_.StopWatching();
+ key_.Close();
+ }
+ }
+ if (!callback_.is_null())
+ callback_.Run(succeeded);
+ }
+
+ private:
+ CallbackType callback_;
+ base::win::RegKey key_;
+ base::win::ObjectWatcher watcher_;
+
+ DISALLOW_COPY_AND_ASSIGN(RegistryWatcher);
+};
+
+} // namespace
+
+// Watches registry for changes. Setting up watches requires IO loop.
+class DnsConfigWatcher::Core {
+ public:
+ Core() {}
+ ~Core() {}
+
+ bool Watch() {
+ RegistryWatcher::CallbackType callback =
+ base::Bind(&Core::OnRegistryChanged, base::Unretained(this));
+
+ bool success = true;
+
+ // The Tcpip key must be present.
+ if (!tcpip_watcher_.Watch(kTcpipPath, callback)) {
+ LOG(ERROR) << "DNS registry watch failed to start.";
+ success = false;
+ }
+
+ // Watch for IPv6 nameservers.
+ 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
+ // 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
+
+ dnscache_watcher_.Watch(kDnscachePath, callback);
+ policy_watcher_.Watch(kPolicyPath, callback);
+
+ if (!hosts_watcher_.Watch(GetHostsPath(),
+ base::Bind(&Core::OnHostsChanged,
+ base::Unretained(this)))) {
+ LOG(ERROR) << "DNS hosts watch failed to start.";
+ success = false;
+ }
+ return success;
+ }
+
+ private:
+ void OnRegistryChanged(bool succeeded) {
+ if (succeeded) {
+ NetworkChangeNotifier::NotifyObserversOfDNSChange(
+ NetworkChangeNotifier::CHANGE_DNS_SETTINGS);
+ } else {
+ LOG(ERROR) << "DNS config watch failed.";
+ NetworkChangeNotifier::NotifyObserversOfDNSChange(
+ NetworkChangeNotifier::CHANGE_DNS_WATCH_FAILED);
+ }
+ }
+
+ void OnHostsChanged(bool succeeded) {
+ if (succeeded) {
+ NetworkChangeNotifier::NotifyObserversOfDNSChange(
+ NetworkChangeNotifier::CHANGE_DNS_HOSTS);
+ } else {
+ LOG(ERROR) << "DNS hosts watch failed.";
+ NetworkChangeNotifier::NotifyObserversOfDNSChange(
+ NetworkChangeNotifier::CHANGE_DNS_WATCH_FAILED);
+ }
+ }
+
+ RegistryWatcher tcpip_watcher_;
+ RegistryWatcher tcpip6_watcher_;
+ RegistryWatcher dnscache_watcher_;
+ RegistryWatcher policy_watcher_;
+ FilePathWatcherWrapper hosts_watcher_;
+
+ DISALLOW_COPY_AND_ASSIGN(Core);
+};
+
+DnsConfigWatcher::DnsConfigWatcher() {}
+
+DnsConfigWatcher::~DnsConfigWatcher() {}
+
+void DnsConfigWatcher::Init() {
+ core_.reset(new Core());
+ if (core_->Watch()) {
+ NetworkChangeNotifier::NotifyObserversOfDNSChange(
+ NetworkChangeNotifier::CHANGE_DNS_WATCH_STARTED);
+ }
+ // TODO(szym): re-start watcher if that makes sense. http://crbug.com/116139
+}
+
+void DnsConfigWatcher::CleanUp() {
+ core_.reset();
+}
+
+} // namespace internal
+} // namespace net
+
diff --git a/net/dns/dns_test_util.cc b/net/dns/dns_test_util.cc
index 0cbdf94..051f595 100644
--- a/net/dns/dns_test_util.cc
+++ b/net/dns/dns_test_util.cc
@@ -163,8 +163,18 @@ scoped_ptr<DnsClient> CreateMockDnsClient(const DnsConfig& config) {
return scoped_ptr<DnsClient>(new MockDnsClient(config));
}
-void MockDnsConfigService::Watch(const CallbackType& callback) {
- set_callback(callback);
+MockDnsConfigService::~MockDnsConfigService() {
+}
+
+void MockDnsConfigService::OnDNSChanged(unsigned detail) {
+}
+
+void MockDnsConfigService::ChangeConfig(const DnsConfig& config) {
+ DnsConfigService::OnConfigRead(config);
+}
+
+void MockDnsConfigService::ChangeHosts(const DnsHosts& hosts) {
+ DnsConfigService::OnHostsRead(hosts);
}
} // namespace net
diff --git a/net/dns/dns_test_util.h b/net/dns/dns_test_util.h
index e7bd457..3ca6e11 100644
--- a/net/dns/dns_test_util.h
+++ b/net/dns/dns_test_util.h
@@ -172,18 +172,14 @@ scoped_ptr<DnsClient> CreateMockDnsClient(const DnsConfig& config);
class MockDnsConfigService : public DnsConfigService {
public:
- virtual ~MockDnsConfigService() {}
+ virtual ~MockDnsConfigService();
- virtual void Watch(const CallbackType& callback) OVERRIDE;
+ // NetworkChangeNotifier::DNSObserver:
+ virtual void OnDNSChanged(unsigned detail) OVERRIDE;
// Expose the protected methods for tests.
- void ChangeConfig(const DnsConfig& config) {
- DnsConfigService::OnConfigRead(config);
- }
-
- void ChangeHosts(const DnsHosts& hosts) {
- DnsConfigService::OnHostsRead(hosts);
- }
+ void ChangeConfig(const DnsConfig& config);
+ void ChangeHosts(const DnsHosts& hosts);
};