| Index: net/base/host_resolver.cc
|
| diff --git a/net/base/host_resolver.cc b/net/base/host_resolver.cc
|
| index 23a1c00940e75c1c9a5fcf36ad386789eb48b3ad..ec693eedf74a807ee7a9c79164130620d685e6d9 100644
|
| --- a/net/base/host_resolver.cc
|
| +++ b/net/base/host_resolver.cc
|
| @@ -21,6 +21,12 @@
|
| #include "net/base/address_list.h"
|
| #include "net/base/net_errors.h"
|
|
|
| +#if defined(OS_LINUX)
|
| +#include "base/singleton.h"
|
| +#include "base/thread_local_storage.h"
|
| +#include "base/time.h"
|
| +#endif
|
| +
|
| #if defined(OS_WIN)
|
| #include "net/base/winsock_init.h"
|
| #endif
|
| @@ -40,6 +46,70 @@ HostMapper* SetHostMapper(HostMapper* value) {
|
| return value;
|
| }
|
|
|
| +#if defined(OS_LINUX)
|
| +// On Linux 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.
|
| +
|
| +// Keep a timer per calling thread to rate limit the calling of res_ninit.
|
| +class DnsReloadTimer {
|
| + public:
|
| + DnsReloadTimer() {
|
| + tls_index_.Initialize(SlotReturnFunction);
|
| + }
|
| +
|
| + ~DnsReloadTimer() { }
|
| +
|
| + // 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:
|
| + // We use thread local storage to identify which base::TimeTicks to
|
| + // interact with.
|
| + static ThreadLocalStorage::Slot tls_index_ ;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(DnsReloadTimer);
|
| +};
|
| +
|
| +// A TLS slot to the TimeTicks for the current thread.
|
| +// static
|
| +ThreadLocalStorage::Slot DnsReloadTimer::tls_index_(base::LINKER_INITIALIZED);
|
| +
|
| +#endif // defined(OS_LINUX)
|
| +
|
| static int HostResolverProc(
|
| const std::string& host, const std::string& port, struct addrinfo** out) {
|
| struct addrinfo hints = {0};
|
| @@ -76,10 +146,14 @@ static int HostResolverProc(
|
|
|
| int err = getaddrinfo(host.c_str(), port.c_str(), &hints, out);
|
| #if defined(OS_LINUX)
|
| + net::DnsReloadTimer* dns_timer = Singleton<net::DnsReloadTimer>::get();
|
| // 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 && !res_init())
|
| - err = getaddrinfo(host.c_str(), port.c_str(), &hints, out);
|
| + if (err && dns_timer->Expired()) {
|
| + res_nclose(&_res);
|
| + if (!res_ninit(&_res))
|
| + err = getaddrinfo(host.c_str(), port.c_str(), &hints, out);
|
| + }
|
| #endif
|
|
|
| return err ? ERR_NAME_NOT_RESOLVED : OK;
|
|
|