diff options
author | thakis@chromium.org <thakis@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-09-06 03:42:34 +0000 |
---|---|---|
committer | thakis@chromium.org <thakis@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-09-06 03:42:34 +0000 |
commit | 46018c9d614cd47961cad5a39416687ebdd8d1cd (patch) | |
tree | a0caf8eba0e6a930177132a3f74d94d026986439 /net/base | |
parent | 0444c8ac9a5fbc500fd8f59782aea359b54d04d9 (diff) | |
download | chromium_src-46018c9d614cd47961cad5a39416687ebdd8d1cd.zip chromium_src-46018c9d614cd47961cad5a39416687ebdd8d1cd.tar.gz chromium_src-46018c9d614cd47961cad5a39416687ebdd8d1cd.tar.bz2 |
Revert 99677 (didn't help) - Revert 99666 (sync tests started failing on mac10.6:
http://build.chromium.org/p/chromium/builders/Mac10.6%20Sync/builds/9198
- Linux: Monitor resolv.conf for changes and use that to reload resolver.
BUG=67734
TEST=manual testing by poking at resolv.conf
Review URL: http://codereview.chromium.org/6903061
TBR=craig.schlenter@chromium.org
Review URL: http://codereview.chromium.org/7833030
TBR=thakis@chromium.org
Review URL: http://codereview.chromium.org/7737034
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@99693 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/base')
-rw-r--r-- | net/base/dns_reload_timer.cc | 100 | ||||
-rw-r--r-- | net/base/dns_reload_timer.h | 22 | ||||
-rw-r--r-- | net/base/dns_reloader.cc | 122 | ||||
-rw-r--r-- | net/base/dns_reloader.h | 24 | ||||
-rw-r--r-- | net/base/dnsrr_resolver.cc | 18 | ||||
-rw-r--r-- | net/base/host_resolver_impl.cc | 21 | ||||
-rw-r--r-- | net/base/host_resolver_impl.h | 6 | ||||
-rw-r--r-- | net/base/host_resolver_proc.cc | 18 | ||||
-rw-r--r-- | net/base/network_change_notifier.cc | 26 | ||||
-rw-r--r-- | net/base/network_change_notifier.h | 21 | ||||
-rw-r--r-- | net/base/network_change_notifier_linux.cc | 58 |
11 files changed, 284 insertions, 152 deletions
diff --git a/net/base/dns_reload_timer.cc b/net/base/dns_reload_timer.cc deleted file mode 100644 index 89bda59..0000000 --- a/net/base/dns_reload_timer.cc +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright (c) 2010 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/base/dns_reload_timer.h" - -#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_OPENBSD) -#include "base/lazy_instance.h" -#include "base/threading/thread_local_storage.h" -#include "base/time.h" - -namespace { - -// On Linux/BSD, changes to /etc/resolv.conf can go unnoticed thus resulting -// in DNS queries failing either because nameservers are unknown on startup -// or because nameserver info has changed as a result of e.g. connecting to -// a new network. Some distributions patch glibc to stat /etc/resolv.conf -// to try to automatically detect such changes but these patches are not -// universal and even patched systems such as Jaunty appear to need calls -// to res_ninit to reload the nameserver information in different threads. -// -// We adopt the Mozilla solution here which is to call res_ninit when -// lookups fail and to rate limit the reloading to once per second per -// thread. -// -// OpenBSD does not have thread-safe res_ninit/res_nclose so we can't do -// the same trick there. - -// Keep a timer per calling thread to rate limit the calling of res_ninit. -class DnsReloadTimer { - public: - // Check if the timer for the calling thread has expired. When no - // timer exists for the calling thread, create one. - bool Expired() { - const base::TimeDelta kRetryTime = base::TimeDelta::FromSeconds(1); - base::TimeTicks now = base::TimeTicks::Now(); - base::TimeTicks* timer_ptr = - static_cast<base::TimeTicks*>(tls_index_.Get()); - - if (!timer_ptr) { - timer_ptr = new base::TimeTicks(); - *timer_ptr = base::TimeTicks::Now(); - tls_index_.Set(timer_ptr); - // Return true to reload dns info on the first call for each thread. - return true; - } else if (now - *timer_ptr > kRetryTime) { - *timer_ptr = now; - return true; - } else { - return false; - } - } - - // Free the allocated timer. - static void SlotReturnFunction(void* data) { - base::TimeTicks* tls_data = static_cast<base::TimeTicks*>(data); - delete tls_data; - } - - private: - friend struct base::DefaultLazyInstanceTraits<DnsReloadTimer>; - - DnsReloadTimer() { - // During testing the DnsReloadTimer Singleton may be created and destroyed - // multiple times. Initialize the ThreadLocalStorage slot only once. - if (!tls_index_.initialized()) - tls_index_.Initialize(SlotReturnFunction); - } - - ~DnsReloadTimer() { - } - - // We use thread local storage to identify which base::TimeTicks to - // interact with. - static base::ThreadLocalStorage::Slot tls_index_ ; - - DISALLOW_COPY_AND_ASSIGN(DnsReloadTimer); -}; - -// A TLS slot to the TimeTicks for the current thread. -// static -base::ThreadLocalStorage::Slot DnsReloadTimer::tls_index_( - base::LINKER_INITIALIZED); - -base::LazyInstance<DnsReloadTimer, - base::LeakyLazyInstanceTraits<DnsReloadTimer> > - g_dns_reload_timer(base::LINKER_INITIALIZED); - -} // namespace - -namespace net { - -bool DnsReloadTimerHasExpired() { - DnsReloadTimer* dns_timer = g_dns_reload_timer.Pointer(); - return dns_timer->Expired(); -} - -} // namespace net - -#endif // defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_OPENBSD) diff --git a/net/base/dns_reload_timer.h b/net/base/dns_reload_timer.h deleted file mode 100644 index fdd90a7..0000000 --- a/net/base/dns_reload_timer.h +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) 2009 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_BASE_DNS_RELOAD_TIMER_H_ -#define NET_BASE_DNS_RELOAD_TIMER_H_ -#pragma once - -#include "build/build_config.h" - -#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_OPENBSD) -namespace net { - -// DnsReloadTimerExpired tests the thread local DNS reload timer and, if it has -// expired, returns true and resets the timer. See comments in -// host_resolver_proc.cc for details. -bool DnsReloadTimerHasExpired(); - -} // namespace net -#endif - -#endif // NET_BASE_DNS_RELOAD_TIMER_H_ diff --git a/net/base/dns_reloader.cc b/net/base/dns_reloader.cc new file mode 100644 index 0000000..d45c145 --- /dev/null +++ b/net/base/dns_reloader.cc @@ -0,0 +1,122 @@ +// Copyright (c) 2011 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/base/dns_reloader.h" + +#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_OPENBSD) + +#include <resolv.h> + +#include "base/basictypes.h" +#include "base/lazy_instance.h" +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/synchronization/lock.h" +#include "base/threading/thread_local_storage.h" +#include "net/base/network_change_notifier.h" + +namespace { + +// On Linux/BSD, changes to /etc/resolv.conf can go unnoticed thus resulting +// in DNS queries failing either because nameservers are unknown on startup +// or because nameserver info has changed as a result of e.g. connecting to +// a new network. Some distributions patch glibc to stat /etc/resolv.conf +// to try to automatically detect such changes but these patches are not +// universal and even patched systems such as Jaunty appear to need calls +// to res_ninit to reload the nameserver information in different threads. +// +// To fix this, on systems with FilePathWatcher support, we use +// NetworkChangeNotifier::DNSObserver to monitor /etc/resolv.conf to +// enable us to respond to DNS changes and reload the resolver state. +// +// OpenBSD does not have thread-safe res_ninit/res_nclose so we can't do +// the same trick there and most *BSD's don't yet have support for +// FilePathWatcher (but perhaps the new kqueue mac code just needs to be +// ported to *BSD to support that). + +class DnsReloader : public net::NetworkChangeNotifier::DNSObserver { + public: + struct ReloadState { + int resolver_generation; + }; + + // NetworkChangeNotifier::OnDNSChanged methods: + virtual void OnDNSChanged() OVERRIDE { + DCHECK_EQ(MessageLoop::current()->type(), MessageLoop::TYPE_IO); + base::AutoLock l(lock_); + resolver_generation_++; + } + + void MaybeReload() { + ReloadState* reload_state = static_cast<ReloadState*>(tls_index_.Get()); + base::AutoLock l(lock_); + + if (!reload_state) { + reload_state = new ReloadState(); + reload_state->resolver_generation = resolver_generation_; + res_ninit(&_res); + tls_index_.Set(reload_state); + } else if (reload_state->resolver_generation != resolver_generation_) { + reload_state->resolver_generation = resolver_generation_; + // It is safe to call res_nclose here since we know res_ninit will have + // been called above. + res_nclose(&_res); + res_ninit(&_res); + } + } + + // Free the allocated state. + static void SlotReturnFunction(void* data) { + ReloadState* reload_state = static_cast<ReloadState*>(data); + if (reload_state) + res_nclose(&_res); + delete reload_state; + } + + private: + DnsReloader() : resolver_generation_(0) { + tls_index_.Initialize(SlotReturnFunction); + net::NetworkChangeNotifier::AddDNSObserver(this); + } + + ~DnsReloader() { + NOTREACHED(); // LeakyLazyInstance is not destructed. + } + + base::Lock lock_; // Protects resolver_generation_. + int resolver_generation_; + friend struct base::DefaultLazyInstanceTraits<DnsReloader>; + + // We use thread local storage to identify which ReloadState to interact with. + static base::ThreadLocalStorage::Slot tls_index_ ; + + DISALLOW_COPY_AND_ASSIGN(DnsReloader); +}; + +// A TLS slot to the ReloadState for the current thread. +// static +base::ThreadLocalStorage::Slot DnsReloader::tls_index_( + base::LINKER_INITIALIZED); + +base::LazyInstance<DnsReloader, + base::LeakyLazyInstanceTraits<DnsReloader> > + g_dns_reloader(base::LINKER_INITIALIZED); + +} // namespace + +namespace net { + +void EnsureDnsReloaderInit() { + DnsReloader* t ALLOW_UNUSED = g_dns_reloader.Pointer(); +} + +void DnsReloaderMaybeReload() { + // This routine can be called by any of the DNS worker threads. + DnsReloader* dns_reloader = g_dns_reloader.Pointer(); + dns_reloader->MaybeReload(); +} + +} // namespace net + +#endif // defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_OPENBSD) diff --git a/net/base/dns_reloader.h b/net/base/dns_reloader.h new file mode 100644 index 0000000..899fbf3 --- /dev/null +++ b/net/base/dns_reloader.h @@ -0,0 +1,24 @@ +// Copyright (c) 2011 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_BASE_DNS_RELOADER_H_ +#define NET_BASE_DNS_RELOADER_H_ +#pragma once + +#include "build/build_config.h" + +#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_OPENBSD) +namespace net { + +// Call on the network thread before calling DnsReloaderMaybeReload() anywhere. +void EnsureDnsReloaderInit(); + +// Call on the worker thread before doing a DNS lookup to reload the +// resolver for that thread by doing res_ninit() if required. +void DnsReloaderMaybeReload(); + +} // namespace net +#endif // defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_OPENBSD) + +#endif // NET_BASE_DNS_RELOADER_H_ diff --git a/net/base/dnsrr_resolver.cc b/net/base/dnsrr_resolver.cc index 2cfb0cf..e0d43d9 100644 --- a/net/base/dnsrr_resolver.cc +++ b/net/base/dnsrr_resolver.cc @@ -20,7 +20,7 @@ #include "base/synchronization/lock.h" #include "base/task.h" #include "base/threading/worker_pool.h" -#include "net/base/dns_reload_timer.h" +#include "net/base/dns_reloader.h" #include "net/base/dns_util.h" #include "net/base/net_errors.h" @@ -184,26 +184,18 @@ class RRResolverWorker { } bool r = true; +#if defined(OS_MACOSX) || defined(OS_OPENBSD) if ((_res.options & RES_INIT) == 0) { if (res_ninit(&_res) != 0) r = false; } +#else + DnsReloaderMaybeReload(); +#endif if (r) { unsigned long saved_options = _res.options; r = Do(); - -#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_OPENBSD) - if (!r && DnsReloadTimerHasExpired()) { - // When there's no network connection, _res may not be initialized by - // getaddrinfo. Therefore, we call res_nclose only when there are ns - // entries. - if (_res.nscount > 0) - res_nclose(&_res); - if (res_ninit(&_res) == 0) - r = Do(); - } -#endif _res.options = saved_options; } diff --git a/net/base/host_resolver_impl.cc b/net/base/host_resolver_impl.cc index 840d263..7d6e3e2 100644 --- a/net/base/host_resolver_impl.cc +++ b/net/base/host_resolver_impl.cc @@ -30,6 +30,7 @@ #include "base/values.h" #include "net/base/address_list.h" #include "net/base/address_list_net_log_param.h" +#include "net/base/dns_reloader.h" #include "net/base/host_port_pair.h" #include "net/base/host_resolver_proc.h" #include "net/base/net_errors.h" @@ -1090,6 +1091,10 @@ HostResolverImpl::HostResolverImpl( additional_resolver_flags_ |= HOST_RESOLVER_LOOPBACK_ONLY; #endif NetworkChangeNotifier::AddIPAddressObserver(this); +#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_OPENBSD) + EnsureDnsReloaderInit(); + NetworkChangeNotifier::AddDNSObserver(this); +#endif } HostResolverImpl::~HostResolverImpl() { @@ -1104,6 +1109,9 @@ HostResolverImpl::~HostResolverImpl() { cur_completing_job_->Cancel(); NetworkChangeNotifier::RemoveIPAddressObserver(this); +#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_OPENBSD) + NetworkChangeNotifier::RemoveDNSObserver(this); +#endif // Delete the job pools. for (size_t i = 0u; i < arraysize(job_pools_); ++i) @@ -1644,4 +1652,17 @@ void HostResolverImpl::OnIPAddressChanged() { // |this| may be deleted inside AbortAllInProgressJobs(). } +void HostResolverImpl::OnDNSChanged() { + // If the DNS server has changed, existing cached info could be wrong so we + // have to drop our internal cache :( Note that OS level DNS caches, such + // as NSCD's cache should be dropped automatically by the OS when + // resolv.conf changes so we don't need to do anything to clear that cache. + if (cache_.get()) + cache_->clear(); + // Existing jobs will have been sent to the original server so they need to + // be aborted. TODO(Craig): Should these jobs be restarted? + AbortAllInProgressJobs(); + // |this| may be deleted inside AbortAllInProgressJobs(). +} + } // namespace net diff --git a/net/base/host_resolver_impl.h b/net/base/host_resolver_impl.h index 30772df..4c4581e 100644 --- a/net/base/host_resolver_impl.h +++ b/net/base/host_resolver_impl.h @@ -62,7 +62,8 @@ namespace net { class NET_EXPORT HostResolverImpl : public HostResolver, NON_EXPORTED_BASE(public base::NonThreadSafe), - public NetworkChangeNotifier::IPAddressObserver { + public NetworkChangeNotifier::IPAddressObserver, + public NetworkChangeNotifier::DNSObserver { public: // The index into |job_pools_| for the various job pools. Pools with a higher // index have lower priority. @@ -304,6 +305,9 @@ class NET_EXPORT HostResolverImpl retry_factor_ = retry_factor; } + // NetworkChangeNotifier::OnDNSChanged methods: + virtual void OnDNSChanged(); + // Cache of host resolution results. scoped_ptr<HostCache> cache_; diff --git a/net/base/host_resolver_proc.cc b/net/base/host_resolver_proc.cc index 755f119..331675a 100644 --- a/net/base/host_resolver_proc.cc +++ b/net/base/host_resolver_proc.cc @@ -8,7 +8,7 @@ #include "base/logging.h" #include "net/base/address_list.h" -#include "net/base/dns_reload_timer.h" +#include "net/base/dns_reloader.h" #include "net/base/net_errors.h" #include "net/base/sys_addrinfo.h" @@ -183,22 +183,12 @@ int SystemHostResolverProc(const std::string& host, // Restrict result set to only this socket type to avoid duplicates. hints.ai_socktype = SOCK_STREAM; - int err = getaddrinfo(host.c_str(), NULL, &hints, &ai); - bool should_retry = false; #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_OPENBSD) && \ !defined(OS_ANDROID) - // If we fail, re-initialise the resolver just in case there have been any - // changes to /etc/resolv.conf and retry. See http://crbug.com/11380 for info. - if (err && DnsReloadTimerHasExpired()) { - // When there's no network connection, _res may not be initialized by - // getaddrinfo. Therefore, we call res_nclose only when there are ns - // entries. - if (_res.nscount > 0) - res_nclose(&_res); - if (!res_ninit(&_res)) - should_retry = true; - } + DnsReloaderMaybeReload(); #endif + int err = getaddrinfo(host.c_str(), NULL, &hints, &ai); + bool should_retry = false; // If the lookup was restricted (either by address family, or address // detection), and the results where all localhost of a single family, // maybe we should retry. There were several bugs related to these diff --git a/net/base/network_change_notifier.cc b/net/base/network_change_notifier.cc index 2eb59ad..29b983e 100644 --- a/net/base/network_change_notifier.cc +++ b/net/base/network_change_notifier.cc @@ -90,6 +90,13 @@ void NetworkChangeNotifier::AddOnlineStateObserver( } } +void NetworkChangeNotifier::AddDNSObserver(DNSObserver* observer) { + if (g_network_change_notifier) { + g_network_change_notifier->resolver_state_observer_list_->AddObserver( + observer); + } +} + void NetworkChangeNotifier::RemoveIPAddressObserver( IPAddressObserver* observer) { if (g_network_change_notifier) { @@ -106,13 +113,23 @@ void NetworkChangeNotifier::RemoveOnlineStateObserver( } } +void NetworkChangeNotifier::RemoveDNSObserver(DNSObserver* observer) { + if (g_network_change_notifier) { + g_network_change_notifier->resolver_state_observer_list_->RemoveObserver( + observer); + } +} + NetworkChangeNotifier::NetworkChangeNotifier() : ip_address_observer_list_( new ObserverListThreadSafe<IPAddressObserver>( ObserverListBase<IPAddressObserver>::NOTIFY_EXISTING_ONLY)), online_state_observer_list_( new ObserverListThreadSafe<OnlineStateObserver>( - ObserverListBase<OnlineStateObserver>::NOTIFY_EXISTING_ONLY)) { + ObserverListBase<OnlineStateObserver>::NOTIFY_EXISTING_ONLY)), + resolver_state_observer_list_( + new ObserverListThreadSafe<DNSObserver>( + ObserverListBase<DNSObserver>::NOTIFY_EXISTING_ONLY)) { DCHECK(!g_network_change_notifier); g_network_change_notifier = this; } @@ -124,6 +141,13 @@ void NetworkChangeNotifier::NotifyObserversOfIPAddressChange() { } } +void NetworkChangeNotifier::NotifyObserversOfDNSChange() { + if (g_network_change_notifier) { + g_network_change_notifier->resolver_state_observer_list_->Notify( + &DNSObserver::OnDNSChanged); + } +} + 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 dad71c3..39b9e05 100644 --- a/net/base/network_change_notifier.h +++ b/net/base/network_change_notifier.h @@ -50,6 +50,22 @@ class NET_EXPORT NetworkChangeNotifier { DISALLOW_COPY_AND_ASSIGN(OnlineStateObserver); }; + class NET_EXPORT DNSObserver { + public: + virtual ~DNSObserver() {} + + // Will be called when the DNS resolver of the system may have changed. + // This is only used on Linux currently and watches /etc/resolv.conf + // and /etc/hosts + virtual void OnDNSChanged() = 0; + + protected: + DNSObserver() {} + + private: + DISALLOW_COPY_AND_ASSIGN(DNSObserver); + }; + virtual ~NetworkChangeNotifier(); // See the description of NetworkChangeNotifier::IsOffline(). @@ -89,6 +105,7 @@ class NET_EXPORT NetworkChangeNotifier { // thread), in which case it will simply do nothing. static void AddIPAddressObserver(IPAddressObserver* observer); static void AddOnlineStateObserver(OnlineStateObserver* observer); + static void AddDNSObserver(DNSObserver* observer); // Unregisters |observer| from receiving notifications. This must be called // on the same thread on which AddObserver() was called. Like AddObserver(), @@ -99,6 +116,7 @@ class NET_EXPORT NetworkChangeNotifier { // there's no reason to use the API in this risky way, so don't do it. static void RemoveIPAddressObserver(IPAddressObserver* observer); static void RemoveOnlineStateObserver(OnlineStateObserver* observer); + static void RemoveDNSObserver(DNSObserver* observer); // Allow unit tests to trigger notifications. static void NotifyObserversOfIPAddressChangeForTests() { @@ -113,12 +131,15 @@ class NET_EXPORT NetworkChangeNotifier { // tests. static void NotifyObserversOfIPAddressChange(); static void NotifyObserversOfOnlineStateChange(); + static void NotifyObserversOfDNSChange(); private: const scoped_refptr<ObserverListThreadSafe<IPAddressObserver> > ip_address_observer_list_; const scoped_refptr<ObserverListThreadSafe<OnlineStateObserver> > online_state_observer_list_; + const scoped_refptr<ObserverListThreadSafe<DNSObserver> > + resolver_state_observer_list_; DISALLOW_COPY_AND_ASSIGN(NetworkChangeNotifier); }; diff --git a/net/base/network_change_notifier_linux.cc b/net/base/network_change_notifier_linux.cc index 3937de0..45ee139 100644 --- a/net/base/network_change_notifier_linux.cc +++ b/net/base/network_change_notifier_linux.cc @@ -7,19 +7,48 @@ #include <errno.h> #include <sys/socket.h> +#include "base/bind.h" +#include "base/callback_old.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/task.h" #include "base/threading/thread.h" #include "net/base/net_errors.h" #include "net/base/network_change_notifier_netlink_linux.h" +using ::base::files::FilePathWatcher; + namespace net { namespace { const int kInvalidSocket = -1; +class DNSWatchDelegate : public FilePathWatcher::Delegate { + public: + explicit DNSWatchDelegate(Callback0::Type* callback) + : callback_(callback) {} + virtual ~DNSWatchDelegate() {} + // FilePathWatcher::Delegate interface + virtual void OnFilePathChanged(const FilePath& path) OVERRIDE; + virtual void OnFilePathError(const FilePath& path) OVERRIDE; + private: + scoped_ptr<Callback0::Type> callback_; + DISALLOW_COPY_AND_ASSIGN(DNSWatchDelegate); +}; + +void DNSWatchDelegate::OnFilePathChanged(const FilePath& path) { + // Calls NetworkChangeNotifier::NotifyObserversOfDNSChange(). + if (callback_.get()) + callback_->Run(); +} + +void DNSWatchDelegate::OnFilePathError(const FilePath& path) { + LOG(ERROR) << "DNSWatchDelegate::OnFilePathError for " << path.value(); +} + } // namespace class NetworkChangeNotifierLinux::Thread @@ -42,6 +71,10 @@ class NetworkChangeNotifierLinux::Thread NetworkChangeNotifier::NotifyObserversOfIPAddressChange(); } + void NotifyObserversOfDNSChange() { + NetworkChangeNotifier::NotifyObserversOfDNSChange(); + } + // Starts listening for netlink messages. Also handles the messages if there // are any available on the netlink socket. void ListenForNotifications(); @@ -58,17 +91,36 @@ class NetworkChangeNotifierLinux::Thread // Technically only needed for ChromeOS, but it's ugly to #ifdef out. ScopedRunnableMethodFactory<Thread> method_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> file_watcher_delegate_; + DISALLOW_COPY_AND_ASSIGN(Thread); }; NetworkChangeNotifierLinux::Thread::Thread() : base::Thread("NetworkChangeNotifier"), netlink_fd_(kInvalidSocket), - ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {} + ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) { +} NetworkChangeNotifierLinux::Thread::~Thread() {} void NetworkChangeNotifierLinux::Thread::Init() { + resolv_file_watcher_.reset(new FilePathWatcher); + hosts_file_watcher_.reset(new FilePathWatcher); + file_watcher_delegate_ = new DNSWatchDelegate(NewCallback(this, + &NetworkChangeNotifierLinux::Thread::NotifyObserversOfDNSChange)); + if (!resolv_file_watcher_->Watch( + FilePath(FILE_PATH_LITERAL("/etc/resolv.conf")), + file_watcher_delegate_.get())) { + LOG(ERROR) << "Failed to setup watch for /etc/resolv.conf"; + } + if (!hosts_file_watcher_->Watch(FilePath(FILE_PATH_LITERAL("/etc/hosts")), + file_watcher_delegate_.get())) { + LOG(ERROR) << "Failed to setup watch for /etc/hosts"; + } netlink_fd_ = InitializeNetlinkSocket(); if (netlink_fd_ < 0) { netlink_fd_ = kInvalidSocket; @@ -84,6 +136,10 @@ 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(); } void NetworkChangeNotifierLinux::Thread::OnFileCanReadWithoutBlocking(int fd) { |