diff options
author | szym@chromium.org <szym@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-05-16 18:10:53 +0000 |
---|---|---|
committer | szym@chromium.org <szym@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-05-16 18:10:53 +0000 |
commit | 05aad32d741012a173ea8f1a4b5072dabe7c0188 (patch) | |
tree | f9b8535284f0503ef96bac3a451f995b5fcd508f | |
parent | 1a92b28469774982203a8c88be0bb49d8275cf68 (diff) | |
download | chromium_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
-rw-r--r-- | net/base/network_change_notifier.cc | 27 | ||||
-rw-r--r-- | net/base/network_change_notifier.h | 23 | ||||
-rw-r--r-- | net/base/network_change_notifier_linux.cc | 74 | ||||
-rw-r--r-- | net/base/network_change_notifier_linux.h | 3 | ||||
-rw-r--r-- | net/base/network_change_notifier_mac.cc | 36 | ||||
-rw-r--r-- | net/base/network_change_notifier_mac.h | 7 | ||||
-rw-r--r-- | net/base/network_change_notifier_win.cc | 33 | ||||
-rw-r--r-- | net/base/network_change_notifier_win.h | 7 | ||||
-rw-r--r-- | net/dns/dns_config_service.cc | 24 | ||||
-rw-r--r-- | net/dns/dns_config_service.h | 33 | ||||
-rw-r--r-- | net/dns/dns_config_service_posix.cc | 90 | ||||
-rw-r--r-- | net/dns/dns_config_service_posix.h | 16 | ||||
-rw-r--r-- | net/dns/dns_config_service_unittest.cc | 12 | ||||
-rw-r--r-- | net/dns/dns_config_service_win.cc | 231 | ||||
-rw-r--r-- | net/dns/dns_config_service_win.h | 71 | ||||
-rw-r--r-- | net/dns/dns_config_watcher.h | 45 | ||||
-rw-r--r-- | net/dns/dns_config_watcher_posix.cc | 131 | ||||
-rw-r--r-- | net/dns/dns_config_watcher_win.cc | 167 | ||||
-rw-r--r-- | net/dns/dns_test_util.cc | 14 | ||||
-rw-r--r-- | net/dns/dns_test_util.h | 14 | ||||
-rw-r--r-- | net/net.gyp | 3 |
21 files changed, 655 insertions, 406 deletions
diff --git a/net/base/network_change_notifier.cc b/net/base/network_change_notifier.cc index ec9cae0..7a14d0e 100644 --- a/net/base/network_change_notifier.cc +++ b/net/base/network_change_notifier.cc @@ -81,6 +81,14 @@ bool NetworkChangeNotifier::IsOffline() { } // static +bool NetworkChangeNotifier::IsWatchingDNS() { + if (!g_network_change_notifier) + return false; + base::AutoLock(g_network_change_notifier->watching_dns_lock_); + return g_network_change_notifier->watching_dns_; +} + +// static NetworkChangeNotifier* NetworkChangeNotifier::CreateMock() { return new MockNetworkChangeNotifier(); } @@ -137,11 +145,13 @@ NetworkChangeNotifier::NetworkChangeNotifier() ObserverListBase<OnlineStateObserver>::NOTIFY_EXISTING_ONLY)), resolver_state_observer_list_( new ObserverListThreadSafe<DNSObserver>( - ObserverListBase<DNSObserver>::NOTIFY_EXISTING_ONLY)) { + ObserverListBase<DNSObserver>::NOTIFY_EXISTING_ONLY)), + watching_dns_(false) { DCHECK(!g_network_change_notifier); g_network_change_notifier = this; } +// static void NetworkChangeNotifier::NotifyObserversOfIPAddressChange() { if (g_network_change_notifier) { g_network_change_notifier->ip_address_observer_list_->Notify( @@ -149,13 +159,28 @@ void NetworkChangeNotifier::NotifyObserversOfIPAddressChange() { } } +// static void NetworkChangeNotifier::NotifyObserversOfDNSChange(unsigned detail) { if (g_network_change_notifier) { + { + base::AutoLock(g_network_change_notifier->watching_dns_lock_); + if (detail & NetworkChangeNotifier::CHANGE_DNS_WATCH_STARTED) { + g_network_change_notifier->watching_dns_ = true; + } else if (detail & NetworkChangeNotifier::CHANGE_DNS_WATCH_FAILED) { + g_network_change_notifier->watching_dns_ = false; + } + // Include detail that watch is off to spare the call to IsWatchingDNS. + if (!g_network_change_notifier->watching_dns_) + detail |= NetworkChangeNotifier::CHANGE_DNS_WATCH_FAILED; + } + DCHECK(!(detail & NetworkChangeNotifier::CHANGE_DNS_WATCH_FAILED) || + !(detail & NetworkChangeNotifier::CHANGE_DNS_WATCH_STARTED)); g_network_change_notifier->resolver_state_observer_list_->Notify( &DNSObserver::OnDNSChanged, detail); } } +// static void NetworkChangeNotifier::NotifyObserversOfOnlineStateChange() { if (g_network_change_notifier) { g_network_change_notifier->online_state_observer_list_->Notify( diff --git a/net/base/network_change_notifier.h b/net/base/network_change_notifier.h index f487501..477d1f7 100644 --- a/net/base/network_change_notifier.h +++ b/net/base/network_change_notifier.h @@ -8,12 +8,17 @@ #include "base/basictypes.h" #include "base/observer_list_threadsafe.h" +#include "base/synchronization/lock.h" #include "net/base/net_export.h" namespace net { class NetworkChangeNotifierFactory; +namespace internal { +class DnsConfigWatcher; +} + // NetworkChangeNotifier monitors the system for network changes, and notifies // registered observers of those events. Observers may register on any thread, // and will be called back on the thread from which they registered. @@ -27,8 +32,10 @@ class NET_EXPORT NetworkChangeNotifier { CHANGE_DNS_SETTINGS = 1 << 0, // The HOSTS file has changed. CHANGE_DNS_HOSTS = 1 << 1, - // Computer name has changed. - CHANGE_DNS_LOCALHOST = 1 << 2, + // The watcher has started. + CHANGE_DNS_WATCH_STARTED = 1 << 2, + // The watcher has failed and will not be available until further notice. + CHANGE_DNS_WATCH_FAILED = 1 << 3, }; class NET_EXPORT IPAddressObserver { @@ -105,6 +112,9 @@ class NET_EXPORT NetworkChangeNotifier { // will be successfully. static bool IsOffline(); + // Returns true if DNS watcher is operational. + static bool IsWatchingDNS(); + // Like Create(), but for use in tests. The mock object doesn't monitor any // events, it merely rebroadcasts notifications when requested. static NetworkChangeNotifier* CreateMock(); @@ -135,6 +145,8 @@ class NET_EXPORT NetworkChangeNotifier { } protected: + friend class internal::DnsConfigWatcher; + NetworkChangeNotifier(); // Broadcasts a notification to all registered observers. Note that this @@ -172,6 +184,13 @@ class NET_EXPORT NetworkChangeNotifier { const scoped_refptr<ObserverListThreadSafe<DNSObserver> > resolver_state_observer_list_; + // True iff DNS watchers are operational. + // Otherwise, OnDNSChanged might not be issued for future changes. + // TODO(szym): This is a temporary interface, consider restarting them. + // http://crbug.com/116139 + base::Lock watching_dns_lock_; + bool watching_dns_; + DISALLOW_COPY_AND_ASSIGN(NetworkChangeNotifier); }; diff --git a/net/base/network_change_notifier_linux.cc b/net/base/network_change_notifier_linux.cc index a4bc595..679593e 100644 --- a/net/base/network_change_notifier_linux.cc +++ b/net/base/network_change_notifier_linux.cc @@ -10,6 +10,7 @@ #include "net/base/network_change_notifier_linux.h" #include <errno.h> +#include <resolv.h> #include <sys/socket.h> #include "base/bind.h" @@ -17,8 +18,6 @@ #include "base/callback.h" #include "base/compiler_specific.h" #include "base/eintr_wrapper.h" -#include "base/file_util.h" -#include "base/files/file_path_watcher.h" #include "base/memory/weak_ptr.h" #include "base/synchronization/lock.h" #include "base/synchronization/waitable_event.h" @@ -30,8 +29,7 @@ #include "dbus/object_proxy.h" #include "net/base/net_errors.h" #include "net/base/network_change_notifier_netlink_linux.h" - -using ::base::files::FilePathWatcher; +#include "net/dns/dns_config_watcher.h" namespace net { @@ -64,28 +62,6 @@ enum { NM_STATE_CONNECTED_GLOBAL = 70 }; -class DNSWatchDelegate : public FilePathWatcher::Delegate { - public: - explicit DNSWatchDelegate(const base::Closure& callback) - : callback_(callback) {} - virtual ~DNSWatchDelegate() {} - // FilePathWatcher::Delegate interface - virtual void OnFilePathChanged(const FilePath& path) OVERRIDE; - virtual void OnFilePathError(const FilePath& path) OVERRIDE; - private: - base::Closure callback_; - DISALLOW_COPY_AND_ASSIGN(DNSWatchDelegate); -}; - -void DNSWatchDelegate::OnFilePathChanged(const FilePath& path) { - // Calls NetworkChangeNotifier::NotifyObserversOfDNSChange(). - callback_.Run(); -} - -void DNSWatchDelegate::OnFilePathError(const FilePath& path) { - LOG(ERROR) << "DNSWatchDelegate::OnFilePathError for " << path.value(); -} - } // namespace // A wrapper around NetworkManager's D-Bus API. @@ -271,8 +247,8 @@ class NetworkChangeNotifierLinux::Thread virtual ~Thread(); // MessageLoopForIO::Watcher: - virtual void OnFileCanReadWithoutBlocking(int fd); - virtual void OnFileCanWriteWithoutBlocking(int /* fd */); + virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE; + virtual void OnFileCanWriteWithoutBlocking(int /* fd */) OVERRIDE; // Plumbing for NetworkChangeNotifier::IsCurrentlyOffline. // Safe to call from any thread. @@ -282,8 +258,8 @@ class NetworkChangeNotifierLinux::Thread protected: // base::Thread - virtual void Init(); - virtual void CleanUp(); + virtual void Init() OVERRIDE; + virtual void CleanUp() OVERRIDE; private: // Starts listening for netlink messages. Also handles the messages if there @@ -299,25 +275,17 @@ class NetworkChangeNotifierLinux::Thread int netlink_fd_; MessageLoopForIO::FileDescriptorWatcher netlink_watcher_; - // Technically only needed for ChromeOS, but it's ugly to #ifdef out. - base::WeakPtrFactory<Thread> ptr_factory_; - - // Used to watch for changes to /etc/resolv.conf and /etc/hosts. - scoped_ptr<base::files::FilePathWatcher> resolv_file_watcher_; - scoped_ptr<base::files::FilePathWatcher> hosts_file_watcher_; - scoped_refptr<DNSWatchDelegate> resolv_watcher_delegate_; - scoped_refptr<DNSWatchDelegate> hosts_watcher_delegate_; - // Used to detect online/offline state changes. NetworkManagerApi network_manager_api_; + internal::DnsConfigWatcher dns_watcher_; + DISALLOW_COPY_AND_ASSIGN(Thread); }; NetworkChangeNotifierLinux::Thread::Thread(dbus::Bus* bus) : base::Thread("NetworkChangeNotifier"), netlink_fd_(kInvalidSocket), - ALLOW_THIS_IN_INITIALIZER_LIST(ptr_factory_(this)), network_manager_api_( base::Bind(&NetworkChangeNotifier ::NotifyObserversOfOnlineStateChange), @@ -329,23 +297,6 @@ NetworkChangeNotifierLinux::Thread::~Thread() { } void NetworkChangeNotifierLinux::Thread::Init() { - resolv_file_watcher_.reset(new FilePathWatcher); - hosts_file_watcher_.reset(new FilePathWatcher); - resolv_watcher_delegate_ = new DNSWatchDelegate(base::Bind( - &NetworkChangeNotifier::NotifyObserversOfDNSChange, - static_cast<unsigned>(CHANGE_DNS_SETTINGS))); - hosts_watcher_delegate_ = new DNSWatchDelegate(base::Bind( - &NetworkChangeNotifier::NotifyObserversOfDNSChange, - static_cast<unsigned>(CHANGE_DNS_HOSTS))); - if (!resolv_file_watcher_->Watch( - FilePath(FILE_PATH_LITERAL("/etc/resolv.conf")), - resolv_watcher_delegate_.get())) { - LOG(ERROR) << "Failed to setup watch for /etc/resolv.conf"; - } - if (!hosts_file_watcher_->Watch(FilePath(FILE_PATH_LITERAL("/etc/hosts")), - hosts_watcher_delegate_.get())) { - LOG(ERROR) << "Failed to setup watch for /etc/hosts"; - } netlink_fd_ = InitializeNetlinkSocket(); if (netlink_fd_ < 0) { netlink_fd_ = kInvalidSocket; @@ -354,6 +305,8 @@ void NetworkChangeNotifierLinux::Thread::Init() { ListenForNotifications(); network_manager_api_.Init(); + + dns_watcher_.Init(); } void NetworkChangeNotifierLinux::Thread::CleanUp() { @@ -363,12 +316,9 @@ void NetworkChangeNotifierLinux::Thread::CleanUp() { netlink_fd_ = kInvalidSocket; netlink_watcher_.StopWatchingFileDescriptor(); } - // Kill watchers early to make sure they won't try to call - // into us via the delegate during destruction. - resolv_file_watcher_.reset(); - hosts_file_watcher_.reset(); - network_manager_api_.CleanUp(); + + dns_watcher_.CleanUp(); } void NetworkChangeNotifierLinux::Thread::OnFileCanReadWithoutBlocking(int fd) { diff --git a/net/base/network_change_notifier_linux.h b/net/base/network_change_notifier_linux.h index 3dbb814..e9426d0 100644 --- a/net/base/network_change_notifier_linux.h +++ b/net/base/network_change_notifier_linux.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// 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. @@ -38,6 +38,7 @@ class NET_EXPORT_PRIVATE NetworkChangeNotifierLinux // The thread used to listen for notifications. This relays the notification // to the registered observers without posting back to the thread the object // was created on. + // Also used for DnsConfigWatcher which requires TYPE_IO message loop. scoped_ptr<Thread> notifier_thread_; DISALLOW_COPY_AND_ASSIGN(NetworkChangeNotifierLinux); diff --git a/net/base/network_change_notifier_mac.cc b/net/base/network_change_notifier_mac.cc index ca314a1..0265ef1 100644 --- a/net/base/network_change_notifier_mac.cc +++ b/net/base/network_change_notifier_mac.cc @@ -5,6 +5,15 @@ #include "net/base/network_change_notifier_mac.h" #include <netinet/in.h> +#include <resolv.h> + +#include "base/basictypes.h" +#include "base/threading/thread.h" +#include "net/dns/dns_config_watcher.h" + +#ifndef _PATH_RESCONF // Normally defined in <resolv.h> +#define _PATH_RESCONF "/etc/resolv.conf" +#endif namespace net { @@ -14,13 +23,38 @@ static bool CalculateReachability(SCNetworkConnectionFlags flags) { return reachable && !connection_required; } +class NetworkChangeNotifierMac::DnsWatcherThread : public base::Thread { + public: + DnsWatcherThread() : base::Thread("NetworkChangeNotifier") {} + + virtual ~DnsWatcherThread() { + Stop(); + } + + virtual void Init() OVERRIDE { + watcher_.Init(); + } + + virtual void CleanUp() OVERRIDE { + watcher_.CleanUp(); + } + + private: + internal::DnsConfigWatcher watcher_; + + DISALLOW_COPY_AND_ASSIGN(DnsWatcherThread); +}; + NetworkChangeNotifierMac::NetworkChangeNotifierMac() : online_state_(UNINITIALIZED), initial_state_cv_(&online_state_lock_), - forwarder_(this) { + forwarder_(this), + dns_watcher_thread_(new DnsWatcherThread()) { // Must be initialized after the rest of this object, as it may call back into // SetInitialState(). config_watcher_.reset(new NetworkConfigWatcherMac(&forwarder_)); + dns_watcher_thread_->StartWithOptions( + base::Thread::Options(MessageLoop::TYPE_IO, 0)); } NetworkChangeNotifierMac::~NetworkChangeNotifierMac() { diff --git a/net/base/network_change_notifier_mac.h b/net/base/network_change_notifier_mac.h index ec2bb1f..c95e952 100644 --- a/net/base/network_change_notifier_mac.h +++ b/net/base/network_change_notifier_mac.h @@ -24,7 +24,7 @@ class NetworkChangeNotifierMac: public NetworkChangeNotifier { NetworkChangeNotifierMac(); virtual ~NetworkChangeNotifierMac(); - // NetworkChangeNotifier implementation: + // NetworkChangeNotifier: virtual bool IsCurrentlyOffline() const OVERRIDE; private: @@ -34,6 +34,8 @@ class NetworkChangeNotifierMac: public NetworkChangeNotifier { ONLINE = 1 }; + class DnsWatcherThread; + // Forwarder just exists to keep the NetworkConfigWatcherMac API out of // NetworkChangeNotifierMac's public API. class Forwarder : public NetworkConfigWatcherMac::Delegate { @@ -83,6 +85,9 @@ class NetworkChangeNotifierMac: public NetworkChangeNotifier { Forwarder forwarder_; scoped_ptr<const NetworkConfigWatcherMac> config_watcher_; + // Thread on which we can run DnsConfigWatcher, which requires TYPE_IO. + scoped_ptr<DnsWatcherThread> dns_watcher_thread_; + DISALLOW_COPY_AND_ASSIGN(NetworkChangeNotifierMac); }; diff --git a/net/base/network_change_notifier_win.cc b/net/base/network_change_notifier_win.cc index eccacaa..928a688 100644 --- a/net/base/network_change_notifier_win.cc +++ b/net/base/network_change_notifier_win.cc @@ -10,11 +10,15 @@ #include "base/bind.h" #include "base/logging.h" #include "base/metrics/histogram.h" +#include "base/threading/thread.h" #include "base/time.h" #include "net/base/winsock_init.h" +#include "net/dns/dns_config_watcher.h" #pragma comment(lib, "iphlpapi.lib") +namespace net { + namespace { // Time between NotifyAddrChange retries, on failure. @@ -22,14 +26,39 @@ const int kWatchForAddressChangeRetryIntervalMs = 500; } // namespace -namespace net { +// Thread on which we can run DnsConfigWatcher, which requires AssertIOAllowed +// to open registry keys and to handle FilePathWatcher updates. +class NetworkChangeNotifierWin::DnsWatcherThread : public base::Thread { + public: + DnsWatcherThread() : base::Thread("NetworkChangeNotifier") {} + + virtual ~DnsWatcherThread() { + Stop(); + } + + virtual void Init() OVERRIDE { + watcher_.Init(); + } + + virtual void CleanUp() OVERRIDE { + watcher_.CleanUp(); + } + + private: + internal::DnsConfigWatcher watcher_; + + DISALLOW_COPY_AND_ASSIGN(DnsWatcherThread); +}; NetworkChangeNotifierWin::NetworkChangeNotifierWin() : is_watching_(false), sequential_failures_(0), - ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) { + ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)), + dns_watcher_thread_(new DnsWatcherThread()) { memset(&addr_overlapped_, 0, sizeof addr_overlapped_); addr_overlapped_.hEvent = WSACreateEvent(); + dns_watcher_thread_->StartWithOptions( + base::Thread::Options(MessageLoop::TYPE_IO, 0)); } NetworkChangeNotifierWin::~NetworkChangeNotifierWin() { diff --git a/net/base/network_change_notifier_win.h b/net/base/network_change_notifier_win.h index aee5304..d8c9a45 100644 --- a/net/base/network_change_notifier_win.h +++ b/net/base/network_change_notifier_win.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// 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. @@ -10,6 +10,7 @@ #include "base/basictypes.h" #include "base/compiler_specific.h" +#include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" #include "base/threading/non_thread_safe.h" #include "base/timer.h" @@ -47,6 +48,7 @@ class NET_EXPORT_PRIVATE NetworkChangeNotifierWin int sequential_failures() { return sequential_failures_; } private: + class DnsWatcherThread; friend class NetworkChangeNotifierWinTest; // NetworkChangeNotifier methods: @@ -89,6 +91,9 @@ class NET_EXPORT_PRIVATE NetworkChangeNotifierWin // Used for calling WatchForAddressChange again on failure. base::WeakPtrFactory<NetworkChangeNotifierWin> weak_factory_; + // Thread on which we can run DnsConfigWatcher. + scoped_ptr<DnsWatcherThread> dns_watcher_thread_; + DISALLOW_COPY_AND_ASSIGN(NetworkChangeNotifierWin); }; 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); }; diff --git a/net/net.gyp b/net/net.gyp index 8cd4ac6..7bf527f 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -364,6 +364,9 @@ 'dns/dns_config_service_posix.h', 'dns/dns_config_service_win.cc', 'dns/dns_config_service_win.h', + 'dns/dns_config_watcher.h', + 'dns/dns_config_watcher_posix.cc', + 'dns/dns_config_watcher_win.cc', 'dns/dns_hosts.cc', 'dns/dns_hosts.h', 'dns/dns_protocol.h', |