summaryrefslogtreecommitdiffstats
path: root/net/base
diff options
context:
space:
mode:
authorthakis@chromium.org <thakis@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-09-06 03:42:34 +0000
committerthakis@chromium.org <thakis@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-09-06 03:42:34 +0000
commit46018c9d614cd47961cad5a39416687ebdd8d1cd (patch)
treea0caf8eba0e6a930177132a3f74d94d026986439 /net/base
parent0444c8ac9a5fbc500fd8f59782aea359b54d04d9 (diff)
downloadchromium_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.cc100
-rw-r--r--net/base/dns_reload_timer.h22
-rw-r--r--net/base/dns_reloader.cc122
-rw-r--r--net/base/dns_reloader.h24
-rw-r--r--net/base/dnsrr_resolver.cc18
-rw-r--r--net/base/host_resolver_impl.cc21
-rw-r--r--net/base/host_resolver_impl.h6
-rw-r--r--net/base/host_resolver_proc.cc18
-rw-r--r--net/base/network_change_notifier.cc26
-rw-r--r--net/base/network_change_notifier.h21
-rw-r--r--net/base/network_change_notifier_linux.cc58
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) {