diff options
Diffstat (limited to 'net/base/host_resolver_proc.cc')
-rw-r--r-- | net/base/host_resolver_proc.cc | 180 |
1 files changed, 180 insertions, 0 deletions
diff --git a/net/base/host_resolver_proc.cc b/net/base/host_resolver_proc.cc new file mode 100644 index 0000000..585a33e --- /dev/null +++ b/net/base/host_resolver_proc.cc @@ -0,0 +1,180 @@ +// Copyright (c) 2006-2008 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/host_resolver_proc.h" + +#include "build/build_config.h" + +#if defined(OS_WIN) +#include <ws2tcpip.h> +#include <wspiapi.h> // Needed for Win2k compat. +#elif defined(OS_POSIX) +#include <netdb.h> +#include <sys/socket.h> +#endif +#if defined(OS_LINUX) +#include <resolv.h> +#endif + +#include "base/logging.h" +#include "base/time.h" +#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" +#endif + +namespace net { + +HostResolverProc* HostResolverProc::default_proc_ = NULL; + +HostResolverProc::HostResolverProc(HostResolverProc* previous) { + set_previous_proc(previous); + + // Implicitly fall-back to the global default procedure. + if (!previous) + set_previous_proc(default_proc_); +} + +// static +HostResolverProc* HostResolverProc::SetDefault(HostResolverProc* proc) { + HostResolverProc* old = default_proc_; + default_proc_ = proc; + return old; +} + +// static +HostResolverProc* HostResolverProc::GetDefault() { + return default_proc_; +} + +int HostResolverProc::ResolveUsingPrevious(const std::string& host, + AddressList* addrlist) { + if (previous_proc_) + return previous_proc_->Resolve(host, addrlist); + + // Final fallback is the system resolver. + return SystemHostResolverProc(host, addrlist); +} + +#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) + +int SystemHostResolverProc(const std::string& host, AddressList* addrlist) { + struct addrinfo* ai = NULL; + struct addrinfo hints = {0}; + hints.ai_family = AF_UNSPEC; + +#if defined(OS_WIN) + // DO NOT USE AI_ADDRCONFIG ON WINDOWS. + // + // The following comment in <winsock2.h> is the best documentation I found + // on AI_ADDRCONFIG for Windows: + // Flags used in "hints" argument to getaddrinfo() + // - AI_ADDRCONFIG is supported starting with Vista + // - default is AI_ADDRCONFIG ON whether the flag is set or not + // because the performance penalty in not having ADDRCONFIG in + // the multi-protocol stack environment is severe; + // this defaulting may be disabled by specifying the AI_ALL flag, + // in that case AI_ADDRCONFIG must be EXPLICITLY specified to + // enable ADDRCONFIG behavior + // + // Not only is AI_ADDRCONFIG unnecessary, but it can be harmful. If the + // computer is not connected to a network, AI_ADDRCONFIG causes getaddrinfo + // to fail with WSANO_DATA (11004) for "localhost", probably because of the + // following note on AI_ADDRCONFIG in the MSDN getaddrinfo page: + // The IPv4 or IPv6 loopback address is not considered a valid global + // address. + // See http://crbug.com/5234. + hints.ai_flags = 0; +#else + hints.ai_flags = AI_ADDRCONFIG; +#endif + + // 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); +#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 && dns_timer->Expired()) { + res_nclose(&_res); + if (!res_ninit(&_res)) + err = getaddrinfo(host.c_str(), NULL, &hints, &ai); + } +#endif + + if (err) + return ERR_NAME_NOT_RESOLVED; + + addrlist->Adopt(ai); + return OK; +} + +} // namespace net |