diff options
author | szym@chromium.org <szym@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-08-31 19:52:40 +0000 |
---|---|---|
committer | szym@chromium.org <szym@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-08-31 19:52:40 +0000 |
commit | bb0e34549798b05741950f793b572e600de0c2cd (patch) | |
tree | d8ec98dc3d9378c3036bfa92493ae9b83ea1b92d /net/dns | |
parent | 90c8b9ca6040546b9b08c03188a604a9f348dd89 (diff) | |
download | chromium_src-bb0e34549798b05741950f793b572e600de0c2cd.zip chromium_src-bb0e34549798b05741950f793b572e600de0c2cd.tar.gz chromium_src-bb0e34549798b05741950f793b572e600de0c2cd.tar.bz2 |
[net] Move DnsConfigService to NetworkChangeNotifier.
This merges DnsConfigWatcher back into DnsConfigService and installs
DnsConfigService at NetworkChangeNotifier. It removes |detail| from
OnDNSChanged callback, and exposes NetworkChangeNotifier::GetDnsConfig.
BUG=142142
Review URL: https://chromiumcodereview.appspot.com/10873018
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@154485 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/dns')
-rw-r--r-- | net/dns/dns_config_service.cc | 28 | ||||
-rw-r--r-- | net/dns/dns_config_service.h | 38 | ||||
-rw-r--r-- | net/dns/dns_config_service_posix.cc | 158 | ||||
-rw-r--r-- | net/dns/dns_config_service_posix.h | 19 | ||||
-rw-r--r-- | net/dns/dns_config_service_unittest.cc | 92 | ||||
-rw-r--r-- | net/dns/dns_config_service_win.cc | 166 | ||||
-rw-r--r-- | net/dns/dns_config_service_win.h | 17 | ||||
-rw-r--r-- | net/dns/dns_config_watcher.h | 44 | ||||
-rw-r--r-- | net/dns/dns_config_watcher_posix.cc | 143 | ||||
-rw-r--r-- | net/dns/dns_config_watcher_win.cc | 165 | ||||
-rw-r--r-- | net/dns/dns_test_util.cc | 14 | ||||
-rw-r--r-- | net/dns/dns_test_util.h | 13 |
12 files changed, 387 insertions, 510 deletions
diff --git a/net/dns/dns_config_service.cc b/net/dns/dns_config_service.cc index 2291b4e..1230688 100644 --- a/net/dns/dns_config_service.cc +++ b/net/dns/dns_config_service.cc @@ -75,33 +75,30 @@ base::Value* DnsConfig::ToValue() const { DnsConfigService::DnsConfigService() - : have_config_(false), + : watch_failed_(false), + have_config_(false), have_hosts_(false), need_update_(false), last_sent_empty_(true) {} DnsConfigService::~DnsConfigService() { - // Must always clean up. - NetworkChangeNotifier::RemoveDNSObserver(this); } -void DnsConfigService::Read(const CallbackType& callback) { +void DnsConfigService::ReadConfig(const CallbackType& callback) { DCHECK(CalledOnValidThread()); DCHECK(!callback.is_null()); DCHECK(callback_.is_null()); callback_ = callback; - OnDNSChanged(NetworkChangeNotifier::CHANGE_DNS_WATCH_STARTED); + ReadNow(); } -void DnsConfigService::Watch(const CallbackType& callback) { +void DnsConfigService::WatchConfig(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. + watch_failed_ = !StartWatching(); + ReadNow(); } void DnsConfigService::InvalidateConfig() { @@ -149,7 +146,7 @@ void DnsConfigService::OnConfigRead(const DnsConfig& config) { UMA_HISTOGRAM_BOOLEAN("AsyncDNS.ConfigChange", changed); have_config_ = true; - if (have_hosts_) + if (have_hosts_ || watch_failed_) OnCompleteConfig(); } @@ -169,7 +166,7 @@ void DnsConfigService::OnHostsRead(const DnsHosts& hosts) { UMA_HISTOGRAM_BOOLEAN("AsyncDNS.HostsChange", changed); have_hosts_ = true; - if (have_config_) + if (have_config_ || watch_failed_) OnCompleteConfig(); } @@ -216,7 +213,12 @@ void DnsConfigService::OnCompleteConfig() { return; need_update_ = false; last_sent_empty_ = false; - callback_.Run(dns_config_); + if (watch_failed_) { + // If a watch failed, the config may not be accurate, so report empty. + callback_.Run(DnsConfig()); + } else { + callback_.Run(dns_config_); + } } } // namespace net diff --git a/net/dns/dns_config_service.h b/net/dns/dns_config_service.h index b99babe..cfa0aa8 100644 --- a/net/dns/dns_config_service.h +++ b/net/dns/dns_config_service.h @@ -18,7 +18,6 @@ // 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" @@ -80,13 +79,12 @@ struct NET_EXPORT_PRIVATE DnsConfig { // Service for reading system DNS settings, on demand or when signalled by -// NetworkChangeNotifier. +// internal watchers and NetworkChangeNotifier. class NET_EXPORT_PRIVATE DnsConfigService - : NON_EXPORTED_BASE(public base::NonThreadSafe), - public NetworkChangeNotifier::DNSObserver { + : NON_EXPORTED_BASE(public base::NonThreadSafe) { public: - // Callback interface for the client, called on the same thread as Read() and - // Watch(). + // Callback interface for the client, called on the same thread as + // ReadConfig() and WatchConfig(). typedef base::Callback<void(const DnsConfig& config)> CallbackType; // Creates the platform-specific DnsConfigService. @@ -97,15 +95,20 @@ class NET_EXPORT_PRIVATE DnsConfigService // Attempts to read the configuration. Will run |callback| when succeeded. // Can be called at most once. - void Read(const CallbackType& callback); + void ReadConfig(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); + // Registers systems watchers. Will attempt to read config after watch starts, + // but only if watchers started successfully. Will run |callback| iff config + // changes from last call or has to be withdrawn. Can be called at most once. + // Might require MessageLoopForIO. + void WatchConfig(const CallbackType& callback); protected: + // Immediately attempts to read the current configuration. + virtual void ReadNow() = 0; + // Registers system watchers. Returns true iff succeeds. + virtual bool StartWatching() = 0; + // Called when the current config (except hosts) has changed. void InvalidateConfig(); // Called when the current hosts have changed. @@ -116,21 +119,22 @@ class NET_EXPORT_PRIVATE DnsConfigService // Called with new hosts. Rest of the config is assumed unchanged. void OnHostsRead(const DnsHosts& hosts); - // NetworkChangeNotifier::DNSObserver: - // Must be defined by implementations. - virtual void OnDNSChanged(unsigned detail) OVERRIDE = 0; + void set_watch_failed(bool value) { watch_failed_ = value; } private: + // The timer counts from the last Invalidate* until complete config is read. void StartTimer(); - // Called when the timer expires. void OnTimeout(); - // Called when the config becomes complete. + // Called when the config becomes complete. Stops the timer. void OnCompleteConfig(); CallbackType callback_; DnsConfig dns_config_; + // True if any of the necessary watchers failed. In that case, the service + // will communicate changes via OnTimeout, but will only send empty DnsConfig. + bool watch_failed_; // True after On*Read, before Invalidate*. Tells if the config is complete. bool have_config_; bool have_hosts_; diff --git a/net/dns/dns_config_service_posix.cc b/net/dns/dns_config_service_posix.cc index cd57b44..4dda0fc 100644 --- a/net/dns/dns_config_service_posix.cc +++ b/net/dns/dns_config_service_posix.cc @@ -8,6 +8,7 @@ #include "base/basictypes.h" #include "base/bind.h" +#include "base/files/file_path_watcher.h" #include "base/file_path.h" #include "base/file_util.h" #include "base/memory/scoped_ptr.h" @@ -17,6 +18,7 @@ #include "net/base/net_util.h" #include "net/dns/dns_hosts.h" #include "net/dns/dns_protocol.h" +#include "net/dns/notify_watcher_mac.h" #include "net/dns/serial_worker.h" namespace net { @@ -26,11 +28,51 @@ 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 -const FilePath::CharType* kFilePathHosts = FILE_PATH_LITERAL("/etc/hosts"); +static const FilePath::CharType* kFilePathConfig = + FILE_PATH_LITERAL(_PATH_RESCONF); + +class ConfigWatcher { + public: + typedef base::Callback<void(bool succeeded)> CallbackType; + + bool Watch(const CallbackType& callback) { + callback_ = callback; + return watcher_.Watch(FilePath(kFilePathConfig), + base::Bind(&ConfigWatcher::OnCallback, + base::Unretained(this))); + } + + private: + void OnCallback(const FilePath& path, bool error) { + callback_.Run(!error); + } + + base::files::FilePathWatcher watcher_; + CallbackType callback_; +}; +#endif ConfigParsePosixResult ReadDnsConfig(DnsConfig* config) { ConfigParsePosixResult result; @@ -63,13 +105,48 @@ ConfigParsePosixResult ReadDnsConfig(DnsConfig* config) { return result; } +} // namespace + +class DnsConfigServicePosix::Watcher { + public: + explicit Watcher(DnsConfigServicePosix* service) : service_(service) {} + ~Watcher() {} + + bool Watch() { + bool success = true; + if (!config_watcher_.Watch( + base::Bind(&DnsConfigServicePosix::OnConfigChanged, + base::Unretained(service_)))) { + LOG(ERROR) << "DNS config watch failed to start."; + success = false; + } + if (!hosts_watcher_.Watch(FilePath(kFilePathHosts), + base::Bind(&Watcher::OnHostsChanged, + base::Unretained(this)))) { + LOG(ERROR) << "DNS hosts watch failed to start."; + success = false; + } + return success; + } + + private: + void OnHostsChanged(const FilePath& path, bool error) { + service_->OnHostsChanged(!error); + } + + DnsConfigServicePosix* service_; + ConfigWatcher config_watcher_; + base::files::FilePathWatcher hosts_watcher_; + + DISALLOW_COPY_AND_ASSIGN(Watcher); +}; + // A SerialWorker that uses libresolv to initialize res_state and converts // it to DnsConfig. -class ConfigReader : public SerialWorker { +class DnsConfigServicePosix::ConfigReader : public SerialWorker { public: - typedef base::Callback<void(const DnsConfig& config)> CallbackType; - explicit ConfigReader(const CallbackType& callback) - : callback_(callback), success_(false) {} + explicit ConfigReader(DnsConfigServicePosix* service) + : service_(service), success_(false) {} virtual void DoWork() OVERRIDE { base::TimeTicks start_time = base::TimeTicks::Now(); @@ -85,7 +162,7 @@ class ConfigReader : public SerialWorker { virtual void OnWorkFinished() OVERRIDE { DCHECK(!IsCancelled()); if (success_) { - callback_.Run(dns_config_); + service_->OnConfigRead(dns_config_); } else { LOG(WARNING) << "Failed to read DnsConfig."; } @@ -94,7 +171,7 @@ class ConfigReader : public SerialWorker { private: virtual ~ConfigReader() {} - const CallbackType callback_; + DnsConfigServicePosix* service_; // Written in DoWork, read in OnWorkFinished, no locking necessary. DnsConfig dns_config_; bool success_; @@ -103,11 +180,10 @@ class ConfigReader : public SerialWorker { }; // A SerialWorker that reads the HOSTS file and runs Callback. -class HostsReader : public SerialWorker { +class DnsConfigServicePosix::HostsReader : public SerialWorker { public: - typedef base::Callback<void(const DnsHosts& hosts)> CallbackType; - explicit HostsReader(const CallbackType& callback) - : path_(kFilePathHosts), callback_(callback), success_(false) {} + explicit HostsReader(DnsConfigServicePosix* service) + : service_(service), path_(kFilePathHosts), success_(false) {} private: virtual ~HostsReader() {} @@ -122,12 +198,13 @@ class HostsReader : public SerialWorker { virtual void OnWorkFinished() OVERRIDE { if (success_) { - callback_.Run(hosts_); + service_->OnHostsRead(hosts_); } else { LOG(WARNING) << "Failed to read DnsHosts."; } } + DnsConfigServicePosix* service_; const FilePath path_; const CallbackType callback_; // Written in DoWork, read in OnWorkFinished, no locking necessary. @@ -137,40 +214,43 @@ class HostsReader : public SerialWorker { DISALLOW_COPY_AND_ASSIGN(HostsReader); }; -} // namespace - -DnsConfigServicePosix::DnsConfigServicePosix() { - config_reader_ = new ConfigReader( - base::Bind(&DnsConfigServicePosix::OnConfigRead, - base::Unretained(this))); - hosts_reader_ = new HostsReader( - base::Bind(&DnsConfigServicePosix::OnHostsRead, - base::Unretained(this))); -} +DnsConfigServicePosix::DnsConfigServicePosix() + : watcher_(new Watcher(this)), + config_reader_(new ConfigReader(this)), + hosts_reader_(new HostsReader(this)) {} DnsConfigServicePosix::~DnsConfigServicePosix() { config_reader_->Cancel(); hosts_reader_->Cancel(); } -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; - } - if (detail & NetworkChangeNotifier::CHANGE_DNS_WATCH_STARTED) - detail = ~0; // Assume everything changed. - if (detail & NetworkChangeNotifier::CHANGE_DNS_SETTINGS) { - InvalidateConfig(); +void DnsConfigServicePosix::ReadNow() { + config_reader_->WorkNow(); + hosts_reader_->WorkNow(); +} + +bool DnsConfigServicePosix::StartWatching() { + // TODO(szym): re-start watcher if that makes sense. http://crbug.com/116139 + return watcher_->Watch(); +} + +void DnsConfigServicePosix::OnConfigChanged(bool succeeded) { + InvalidateConfig(); + if (succeeded) { config_reader_->WorkNow(); + } else { + LOG(ERROR) << "DNS config watch failed."; + set_watch_failed(true); } - if (detail & NetworkChangeNotifier::CHANGE_DNS_HOSTS) { - InvalidateHosts(); +} + +void DnsConfigServicePosix::OnHostsChanged(bool succeeded) { + InvalidateHosts(); + if (succeeded) { hosts_reader_->WorkNow(); + } else { + LOG(ERROR) << "DNS hosts watch failed."; + set_watch_failed(true); } } @@ -283,7 +363,9 @@ class StubDnsConfigService : public DnsConfigService { public: StubDnsConfigService() {} virtual ~StubDnsConfigService() {} - virtual void OnDNSChanged(unsigned detail) OVERRIDE {} + private: + virtual void ReadNow() OVERRIDE {} + virtual bool StartWatching() OVERRIDE { return false; } }; // static scoped_ptr<DnsConfigService> DnsConfigService::CreateSystemService() { diff --git a/net/dns/dns_config_service_posix.h b/net/dns/dns_config_service_posix.h index 029e467..1450457 100644 --- a/net/dns/dns_config_service_posix.h +++ b/net/dns/dns_config_service_posix.h @@ -15,8 +15,6 @@ namespace net { -class SerialWorker; - // Use DnsConfigService::CreateSystemService to use it outside of tests. namespace internal { @@ -26,11 +24,20 @@ class NET_EXPORT_PRIVATE DnsConfigServicePosix : public DnsConfigService { virtual ~DnsConfigServicePosix(); private: - // NetworkChangeNotifier::DNSObserver: - virtual void OnDNSChanged(unsigned detail) OVERRIDE; + class Watcher; + class ConfigReader; + class HostsReader; + + // DnsConfigService: + virtual void ReadNow() OVERRIDE; + virtual bool StartWatching() OVERRIDE; + + void OnConfigChanged(bool succeeded); + void OnHostsChanged(bool succeeded); - scoped_refptr<SerialWorker> config_reader_; - scoped_refptr<SerialWorker> hosts_reader_; + scoped_ptr<Watcher> watcher_; + scoped_refptr<ConfigReader> config_reader_; + scoped_refptr<HostsReader> hosts_reader_; DISALLOW_COPY_AND_ASSIGN(DnsConfigServicePosix); }; diff --git a/net/dns/dns_config_service_unittest.cc b/net/dns/dns_config_service_unittest.cc index 2533809..c89f35b 100644 --- a/net/dns/dns_config_service_unittest.cc +++ b/net/dns/dns_config_service_unittest.cc @@ -19,8 +19,6 @@ namespace { class DnsConfigServiceTest : public testing::Test { public: void OnConfigChanged(const DnsConfig& config) { - EXPECT_FALSE(config.Equals(last_config_)) << - "Config must be different from last call."; last_config_ = config; if (quit_on_config_) MessageLoop::current()->Quit(); @@ -29,7 +27,8 @@ class DnsConfigServiceTest : public testing::Test { protected: class TestDnsConfigService : public DnsConfigService { public: - virtual void OnDNSChanged(unsigned detail) OVERRIDE {} + virtual void ReadNow() OVERRIDE {} + virtual bool StartWatching() OVERRIDE { return true; } // Expose the protected methods to this test suite. void InvalidateConfig() { @@ -47,6 +46,10 @@ class DnsConfigServiceTest : public testing::Test { void OnHostsRead(const DnsHosts& hosts) { DnsConfigService::OnHostsRead(hosts); } + + void set_watch_failed(bool value) { + DnsConfigService::set_watch_failed(value); + } }; void WaitForConfig(base::TimeDelta timeout) { @@ -64,7 +67,7 @@ class DnsConfigServiceTest : public testing::Test { DnsConfig MakeConfig(unsigned seed) { DnsConfig config; IPAddressNumber ip; - EXPECT_TRUE(ParseIPLiteralToNumber("1.2.3.4", &ip)); + CHECK(ParseIPLiteralToNumber("1.2.3.4", &ip)); config.nameservers.push_back(IPEndPoint(ip, seed & 0xFFFF)); EXPECT_TRUE(config.IsValid()); return config; @@ -84,8 +87,8 @@ class DnsConfigServiceTest : public testing::Test { quit_on_config_ = false; service_.reset(new TestDnsConfigService()); - service_->Watch(base::Bind(&DnsConfigServiceTest::OnConfigChanged, - base::Unretained(this))); + service_->WatchConfig(base::Bind(&DnsConfigServiceTest::OnConfigChanged, + base::Unretained(this))); EXPECT_FALSE(last_config_.IsValid()); } @@ -103,11 +106,9 @@ TEST_F(DnsConfigServiceTest, FirstConfig) { service_->OnConfigRead(config); // No hosts yet, so no config. - EXPECT_FALSE(last_config_.IsValid()); + EXPECT_TRUE(last_config_.Equals(DnsConfig())); service_->OnHostsRead(config.hosts); - // Empty hosts is acceptable. - EXPECT_TRUE(last_config_.IsValid()); EXPECT_TRUE(last_config_.Equals(config)); } @@ -118,29 +119,34 @@ TEST_F(DnsConfigServiceTest, Timeout) { service_->OnConfigRead(config); service_->OnHostsRead(config.hosts); - EXPECT_TRUE(last_config_.IsValid()); + EXPECT_FALSE(last_config_.Equals(DnsConfig())); EXPECT_TRUE(last_config_.Equals(config)); service_->InvalidateConfig(); WaitForConfig(TestTimeouts::action_timeout()); - EXPECT_FALSE(last_config_.IsValid()); + EXPECT_FALSE(last_config_.Equals(config)); + EXPECT_TRUE(last_config_.Equals(DnsConfig())); service_->OnConfigRead(config); + EXPECT_FALSE(last_config_.Equals(DnsConfig())); EXPECT_TRUE(last_config_.Equals(config)); service_->InvalidateHosts(); WaitForConfig(TestTimeouts::action_timeout()); - EXPECT_FALSE(last_config_.IsValid()); + EXPECT_FALSE(last_config_.Equals(config)); + EXPECT_TRUE(last_config_.Equals(DnsConfig())); + DnsConfig bad_config = last_config_ = MakeConfig(0xBAD); service_->InvalidateConfig(); - // We don't expect an update. This should time out. If we get an update, - // we'll detect unchanged config. + // We don't expect an update. This should time out. WaitForConfig(base::TimeDelta::FromMilliseconds(100) + TestTimeouts::tiny_timeout()); - EXPECT_FALSE(last_config_.IsValid()); + EXPECT_TRUE(last_config_.Equals(bad_config)) << "Unexpected change"; + last_config_ = DnsConfig(); service_->OnConfigRead(config); service_->OnHostsRead(config.hosts); + EXPECT_FALSE(last_config_.Equals(DnsConfig())); EXPECT_TRUE(last_config_.Equals(config)); } @@ -150,14 +156,15 @@ TEST_F(DnsConfigServiceTest, SameConfig) { service_->OnConfigRead(config); service_->OnHostsRead(config.hosts); + EXPECT_FALSE(last_config_.Equals(DnsConfig())); EXPECT_TRUE(last_config_.Equals(config)); - // OnConfigChanged will catch if there was no change. + last_config_ = DnsConfig(); service_->OnConfigRead(config); - EXPECT_TRUE(last_config_.Equals(config)); + EXPECT_TRUE(last_config_.Equals(DnsConfig())) << "Unexpected change"; service_->OnHostsRead(config.hosts); - EXPECT_TRUE(last_config_.Equals(config)); + EXPECT_TRUE(last_config_.Equals(DnsConfig())) << "Unexpected change"; } TEST_F(DnsConfigServiceTest, DifferentConfig) { @@ -174,6 +181,7 @@ TEST_F(DnsConfigServiceTest, DifferentConfig) { service_->OnConfigRead(config1); service_->OnHostsRead(config1.hosts); + EXPECT_FALSE(last_config_.Equals(DnsConfig())); EXPECT_TRUE(last_config_.Equals(config1)); // It doesn't matter for this tests, but increases coverage. @@ -181,14 +189,56 @@ TEST_F(DnsConfigServiceTest, DifferentConfig) { service_->InvalidateHosts(); service_->OnConfigRead(config2); - service_->OnHostsRead(config2.hosts); + EXPECT_TRUE(last_config_.Equals(config1)) << "Unexpected change"; + service_->OnHostsRead(config2.hosts); // Not an actual change. + EXPECT_FALSE(last_config_.Equals(config1)); EXPECT_TRUE(last_config_.Equals(config2)); service_->OnConfigRead(config3); + EXPECT_TRUE(last_config_.EqualsIgnoreHosts(config3)); service_->OnHostsRead(config3.hosts); + EXPECT_FALSE(last_config_.Equals(config2)); EXPECT_TRUE(last_config_.Equals(config3)); } +TEST_F(DnsConfigServiceTest, WatchFailure) { + DnsConfig config1 = MakeConfig(1); + DnsConfig config2 = MakeConfig(2); + config1.hosts = MakeHosts(1); + config2.hosts = MakeHosts(2); + + service_->OnConfigRead(config1); + service_->OnHostsRead(config1.hosts); + EXPECT_FALSE(last_config_.Equals(DnsConfig())); + EXPECT_TRUE(last_config_.Equals(config1)); + + // Simulate watch failure. + service_->set_watch_failed(true); + service_->InvalidateConfig(); + WaitForConfig(TestTimeouts::action_timeout()); + EXPECT_FALSE(last_config_.Equals(config1)); + EXPECT_TRUE(last_config_.Equals(DnsConfig())); + + DnsConfig bad_config = last_config_ = MakeConfig(0xBAD); + // Actual change in config, so expect an update, but it should be empty. + service_->OnConfigRead(config1); + EXPECT_FALSE(last_config_.Equals(bad_config)); + EXPECT_TRUE(last_config_.Equals(DnsConfig())); + + last_config_ = bad_config; + // Actual change in config, so expect an update, but it should be empty. + service_->InvalidateConfig(); + service_->OnConfigRead(config2); + EXPECT_FALSE(last_config_.Equals(bad_config)); + EXPECT_TRUE(last_config_.Equals(DnsConfig())); + + last_config_ = bad_config; + // No change, so no update. + service_->InvalidateConfig(); + service_->OnConfigRead(config2); + EXPECT_TRUE(last_config_.Equals(bad_config)); +} + #if (defined(OS_POSIX) && !defined(OS_ANDROID)) || defined(OS_WIN) // TODO(szym): This is really an integration test and can time out if HOSTS is // huge. http://crbug.com/107810 @@ -196,8 +246,8 @@ TEST_F(DnsConfigServiceTest, FLAKY_GetSystemConfig) { service_.reset(); scoped_ptr<DnsConfigService> service(DnsConfigService::CreateSystemService()); - service->Read(base::Bind(&DnsConfigServiceTest::OnConfigChanged, - base::Unretained(this))); + service->ReadConfig(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 " << diff --git a/net/dns/dns_config_service_win.cc b/net/dns/dns_config_service_win.cc index 54ef1c0..8fa7209 100644 --- a/net/dns/dns_config_service_win.cc +++ b/net/dns/dns_config_service_win.cc @@ -10,6 +10,7 @@ #include "base/bind.h" #include "base/callback.h" #include "base/compiler_specific.h" +#include "base/files/file_path_watcher.h" #include "base/file_path.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" @@ -21,6 +22,7 @@ #include "base/threading/thread_restrictions.h" #include "base/time.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" @@ -272,6 +274,48 @@ HostsParseWinResult AddLocalhostEntries(DnsHosts* hosts) { return HOSTS_PARSE_WIN_OK; } +// 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 && 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 FilePath GetHostsPath() { @@ -450,6 +494,77 @@ ConfigParseWinResult ConvertSettingsToDnsConfig( return CONFIG_PARSE_WIN_OK; } +// Watches registry and HOSTS file for changes. Must live on a thread which +// allows IO. +class DnsConfigServiceWin::Watcher + : public NetworkChangeNotifier::IPAddressObserver { + public: + explicit Watcher(DnsConfigServiceWin* service) : service_(service) {} + ~Watcher() { + NetworkChangeNotifier::RemoveIPAddressObserver(this); + } + + bool Watch() { + RegistryWatcher::CallbackType callback = + base::Bind(&DnsConfigServiceWin::OnConfigChanged, + base::Unretained(service_)); + + 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(&Watcher::OnHostsChanged, + base::Unretained(this)))) { + LOG(ERROR) << "DNS hosts watch failed to start."; + success = false; + } else { + // Also need to observe changes to local non-loopback IP for DnsHosts. + NetworkChangeNotifier::AddIPAddressObserver(this); + } + return success; + } + + private: + void OnHostsChanged(const FilePath& path, bool error) { + if (error) + NetworkChangeNotifier::RemoveIPAddressObserver(this); + service_->OnHostsChanged(!error); + } + + // NetworkChangeNotifier::IPAddressObserver: + virtual void OnIPAddressChanged() OVERRIDE { + // Need to update non-loopback IP of local host. + service_->OnHostsChanged(true); + } + + DnsConfigServiceWin* service_; + + RegistryWatcher tcpip_watcher_; + RegistryWatcher tcpip6_watcher_; + RegistryWatcher dnscache_watcher_; + RegistryWatcher policy_watcher_; + base::files::FilePathWatcher hosts_watcher_; + + DISALLOW_COPY_AND_ASSIGN(Watcher); +}; + // Reads config from registry and IpHelper. All work performed on WorkerPool. class DnsConfigServiceWin::ConfigReader : public SerialWorker { public: @@ -541,46 +656,43 @@ class DnsConfigServiceWin::HostsReader : public SerialWorker { }; DnsConfigServiceWin::DnsConfigServiceWin() - : config_reader_(new ConfigReader(this)), + : watcher_(new Watcher(this)), + config_reader_(new ConfigReader(this)), hosts_reader_(new HostsReader(this)) {} DnsConfigServiceWin::~DnsConfigServiceWin() { config_reader_->Cancel(); hosts_reader_->Cancel(); - NetworkChangeNotifier::RemoveIPAddressObserver(this); } -void DnsConfigServiceWin::Watch(const CallbackType& callback) { - DnsConfigService::Watch(callback); - // Also need to observe changes to local non-loopback IP for DnsHosts. - NetworkChangeNotifier::AddIPAddressObserver(this); +void DnsConfigServiceWin::ReadNow() { + config_reader_->WorkNow(); + hosts_reader_->WorkNow(); } -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 (detail & NetworkChangeNotifier::CHANGE_DNS_WATCH_STARTED) - detail = ~0; // Assume everything changed. - if (detail & NetworkChangeNotifier::CHANGE_DNS_SETTINGS) { - InvalidateConfig(); +bool DnsConfigServiceWin::StartWatching() { + // TODO(szym): re-start watcher if that makes sense. http://crbug.com/116139 + return watcher_->Watch(); +} + +void DnsConfigServiceWin::OnConfigChanged(bool succeeded) { + InvalidateConfig(); + if (succeeded) { config_reader_->WorkNow(); - } - if (detail & NetworkChangeNotifier::CHANGE_DNS_HOSTS) { - InvalidateHosts(); - hosts_reader_->WorkNow(); + } else { + LOG(ERROR) << "DNS config watch failed."; + set_watch_failed(true); } } -void DnsConfigServiceWin::OnIPAddressChanged() { - // Need to update non-loopback IP of local host. - if (NetworkChangeNotifier::IsWatchingDNS()) - OnDNSChanged(NetworkChangeNotifier::CHANGE_DNS_HOSTS); +void DnsConfigServiceWin::OnHostsChanged(bool succeeded) { + InvalidateHosts(); + if (succeeded) { + hosts_reader_->WorkNow(); + } else { + LOG(ERROR) << "DNS hosts watch failed."; + set_watch_failed(true); + } } } // namespace internal diff --git a/net/dns/dns_config_service_win.h b/net/dns/dns_config_service_win.h index b0a29d0..e036e44 100644 --- a/net/dns/dns_config_service_win.h +++ b/net/dns/dns_config_service_win.h @@ -122,25 +122,24 @@ ConfigParseWinResult NET_EXPORT_PRIVATE ConvertSettingsToDnsConfig( DnsConfig* dns_config); // Use DnsConfigService::CreateSystemService to use it outside of tests. -class NET_EXPORT_PRIVATE DnsConfigServiceWin - : public DnsConfigService, - public NetworkChangeNotifier::IPAddressObserver { +class NET_EXPORT_PRIVATE DnsConfigServiceWin : public DnsConfigService { public: DnsConfigServiceWin(); virtual ~DnsConfigServiceWin(); - virtual void Watch(const CallbackType& callback) OVERRIDE; - private: + class Watcher; class ConfigReader; class HostsReader; - // NetworkChangeNotifier::DNSObserver: - virtual void OnDNSChanged(unsigned detail) OVERRIDE; + // DnsConfigService: + virtual void ReadNow() OVERRIDE; + virtual bool StartWatching() OVERRIDE; - // NetworkChangeNotifier::IPAddressObserver: - virtual void OnIPAddressChanged() OVERRIDE; + void OnConfigChanged(bool succeeded); + void OnHostsChanged(bool succeeded); + scoped_ptr<Watcher> watcher_; scoped_refptr<ConfigReader> config_reader_; scoped_refptr<HostsReader> hosts_reader_; diff --git a/net/dns/dns_config_watcher.h b/net/dns/dns_config_watcher.h deleted file mode 100644 index 5a88bb7..0000000 --- a/net/dns/dns_config_watcher.h +++ /dev/null @@ -1,44 +0,0 @@ -// 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_ - -#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 deleted file mode 100644 index 3c0d118..0000000 --- a/net/dns/dns_config_watcher_posix.cc +++ /dev/null @@ -1,143 +0,0 @@ -// 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/files/file_path_watcher.h" -#include "base/file_path.h" -#include "base/logging.h" -#include "base/memory/scoped_ptr.h" -#include "net/base/network_change_notifier.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: - typedef base::Callback<void(bool succeeded)> CallbackType; - - bool Watch(const CallbackType& callback) { - callback_ = callback; - return watcher_.Watch(FilePath(kFilePathConfig), - base::Bind(&ConfigWatcher::OnCallback, - base::Unretained(this))); - } - - private: - void OnCallback(const FilePath& path, bool error) { - callback_.Run(!error); - } - - base::files::FilePathWatcher watcher_; - CallbackType callback_; -}; -#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(const FilePath& path, bool error) { - if (!error) { - NetworkChangeNotifier::NotifyObserversOfDNSChange( - NetworkChangeNotifier::CHANGE_DNS_HOSTS); - } else { - LOG(ERROR) << "DNS hosts watch failed."; - NetworkChangeNotifier::NotifyObserversOfDNSChange( - NetworkChangeNotifier::CHANGE_DNS_WATCH_FAILED); - } - } - - ConfigWatcher config_watcher_; - base::files::FilePathWatcher 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 deleted file mode 100644 index 2cd41e9..0000000 --- a/net/dns/dns_config_watcher_win.cc +++ /dev/null @@ -1,165 +0,0 @@ -// 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/files/file_path_watcher.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" - -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 && 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(const FilePath& path, bool error) { - if (!error) { - 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_; - base::files::FilePathWatcher 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 73d208c..8663370 100644 --- a/net/dns/dns_test_util.cc +++ b/net/dns/dns_test_util.cc @@ -208,18 +208,4 @@ scoped_ptr<DnsClient> CreateMockDnsClient(const DnsConfig& config, return scoped_ptr<DnsClient>(new MockDnsClient(config, rules)); } -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 2d03d0e..1d9e067b 100644 --- a/net/dns/dns_test_util.h +++ b/net/dns/dns_test_util.h @@ -189,19 +189,6 @@ typedef std::vector<MockDnsClientRule> MockDnsClientRuleList; scoped_ptr<DnsClient> CreateMockDnsClient(const DnsConfig& config, const MockDnsClientRuleList& rules); -class MockDnsConfigService : public DnsConfigService { - public: - virtual ~MockDnsConfigService(); - - // NetworkChangeNotifier::DNSObserver: - virtual void OnDNSChanged(unsigned detail) OVERRIDE; - - // Expose the protected methods for tests. - void ChangeConfig(const DnsConfig& config); - void ChangeHosts(const DnsHosts& hosts); -}; - - } // namespace net #endif // NET_DNS_DNS_TEST_UTIL_H_ |