host_resolver_proc.cc revision 00d26a728db2814620f390b418a7d6325ce5aca6
1// Copyright (c) 2010 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "net/base/host_resolver_proc.h" 6 7#include "build/build_config.h" 8 9#if defined(OS_POSIX) && !defined(OS_MACOSX) 10#include <resolv.h> 11#endif 12 13#include <netinet/in.h> 14 15#include "base/logging.h" 16#include "base/time.h" 17#include "net/base/address_list.h" 18#include "net/base/net_errors.h" 19#include "net/base/sys_addrinfo.h" 20 21#if defined(OS_POSIX) && !defined(OS_MACOSX) 22#include "base/singleton.h" 23#include "base/thread_local_storage.h" 24#endif 25 26namespace net { 27 28HostResolverProc* HostResolverProc::default_proc_ = NULL; 29 30HostResolverProc::HostResolverProc(HostResolverProc* previous) { 31 SetPreviousProc(previous); 32 33 // Implicitly fall-back to the global default procedure. 34 if (!previous) 35 SetPreviousProc(default_proc_); 36} 37 38void HostResolverProc::SetPreviousProc(HostResolverProc* proc) { 39 HostResolverProc* current_previous = previous_proc_; 40 previous_proc_ = NULL; 41 // Now that we've guaranteed |this| is the last proc in a chain, we can 42 // detect potential cycles using GetLastProc(). 43 previous_proc_ = (GetLastProc(proc) == this) ? current_previous : proc; 44} 45 46void HostResolverProc::SetLastProc(HostResolverProc* proc) { 47 GetLastProc(this)->SetPreviousProc(proc); 48} 49 50// static 51HostResolverProc* HostResolverProc::GetLastProc(HostResolverProc* proc) { 52 if (proc == NULL) 53 return NULL; 54 HostResolverProc* last_proc = proc; 55 while (last_proc->previous_proc_ != NULL) 56 last_proc = last_proc->previous_proc_; 57 return last_proc; 58} 59 60// static 61HostResolverProc* HostResolverProc::SetDefault(HostResolverProc* proc) { 62 HostResolverProc* old = default_proc_; 63 default_proc_ = proc; 64 return old; 65} 66 67// static 68HostResolverProc* HostResolverProc::GetDefault() { 69 return default_proc_; 70} 71 72int HostResolverProc::ResolveUsingPrevious( 73 const std::string& host, 74 AddressFamily address_family, 75 HostResolverFlags host_resolver_flags, 76 AddressList* addrlist, 77 int* os_error) { 78 if (previous_proc_) { 79 return previous_proc_->Resolve(host, address_family, host_resolver_flags, 80 addrlist, os_error); 81 } 82 83 // Final fallback is the system resolver. 84 return SystemHostResolverProc(host, address_family, host_resolver_flags, 85 addrlist, os_error); 86} 87 88#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_OPENBSD) 89// On Linux/BSD, changes to /etc/resolv.conf can go unnoticed thus resulting 90// in DNS queries failing either because nameservers are unknown on startup 91// or because nameserver info has changed as a result of e.g. connecting to 92// a new network. Some distributions patch glibc to stat /etc/resolv.conf 93// to try to automatically detect such changes but these patches are not 94// universal and even patched systems such as Jaunty appear to need calls 95// to res_ninit to reload the nameserver information in different threads. 96// 97// We adopt the Mozilla solution here which is to call res_ninit when 98// lookups fail and to rate limit the reloading to once per second per 99// thread. 100// 101// OpenBSD does not have thread-safe res_ninit/res_nclose so we can't do 102// the same trick there. 103 104// Keep a timer per calling thread to rate limit the calling of res_ninit. 105class DnsReloadTimer { 106 public: 107 // Check if the timer for the calling thread has expired. When no 108 // timer exists for the calling thread, create one. 109 bool Expired() { 110 const base::TimeDelta kRetryTime = base::TimeDelta::FromSeconds(1); 111 base::TimeTicks now = base::TimeTicks::Now(); 112 base::TimeTicks* timer_ptr = 113 static_cast<base::TimeTicks*>(tls_index_.Get()); 114 115 if (!timer_ptr) { 116 timer_ptr = new base::TimeTicks(); 117 *timer_ptr = base::TimeTicks::Now(); 118 tls_index_.Set(timer_ptr); 119 // Return true to reload dns info on the first call for each thread. 120 return true; 121 } else if (now - *timer_ptr > kRetryTime) { 122 *timer_ptr = now; 123 return true; 124 } else { 125 return false; 126 } 127 } 128 129 // Free the allocated timer. 130 static void SlotReturnFunction(void* data) { 131 base::TimeTicks* tls_data = static_cast<base::TimeTicks*>(data); 132 delete tls_data; 133 } 134 135 private: 136 friend struct DefaultSingletonTraits<DnsReloadTimer>; 137 138 DnsReloadTimer() { 139 // During testing the DnsReloadTimer Singleton may be created and destroyed 140 // multiple times. Initialize the ThreadLocalStorage slot only once. 141 if (!tls_index_.initialized()) 142 tls_index_.Initialize(SlotReturnFunction); 143 } 144 145 ~DnsReloadTimer() { 146 } 147 148 // We use thread local storage to identify which base::TimeTicks to 149 // interact with. 150 static ThreadLocalStorage::Slot tls_index_ ; 151 152 DISALLOW_COPY_AND_ASSIGN(DnsReloadTimer); 153}; 154 155// A TLS slot to the TimeTicks for the current thread. 156// static 157ThreadLocalStorage::Slot DnsReloadTimer::tls_index_(base::LINKER_INITIALIZED); 158 159#endif // defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_OPENBSD) 160 161int SystemHostResolverProc(const std::string& host, 162 AddressFamily address_family, 163 HostResolverFlags host_resolver_flags, 164 AddressList* addrlist, 165 int* os_error) { 166 if (os_error) 167 *os_error = 0; 168 169 // The result of |getaddrinfo| for empty hosts is inconsistent across systems. 170 // On Windows it gives the default interface's address, whereas on Linux it 171 // gives an error. We will make it fail on all platforms for consistency. 172 if (host.empty()) 173 return ERR_NAME_NOT_RESOLVED; 174 175 struct addrinfo* ai = NULL; 176 struct addrinfo hints = {0}; 177 178 switch (address_family) { 179 case ADDRESS_FAMILY_IPV4: 180 hints.ai_family = AF_INET; 181 break; 182 case ADDRESS_FAMILY_IPV6: 183 hints.ai_family = AF_INET6; 184 break; 185 case ADDRESS_FAMILY_UNSPECIFIED: 186 hints.ai_family = AF_UNSPEC; 187 break; 188 default: 189 NOTREACHED(); 190 hints.ai_family = AF_UNSPEC; 191 } 192 193#if defined(OS_WIN) || defined(OS_OPENBSD) 194 // DO NOT USE AI_ADDRCONFIG ON WINDOWS. 195 // 196 // The following comment in <winsock2.h> is the best documentation I found 197 // on AI_ADDRCONFIG for Windows: 198 // Flags used in "hints" argument to getaddrinfo() 199 // - AI_ADDRCONFIG is supported starting with Vista 200 // - default is AI_ADDRCONFIG ON whether the flag is set or not 201 // because the performance penalty in not having ADDRCONFIG in 202 // the multi-protocol stack environment is severe; 203 // this defaulting may be disabled by specifying the AI_ALL flag, 204 // in that case AI_ADDRCONFIG must be EXPLICITLY specified to 205 // enable ADDRCONFIG behavior 206 // 207 // Not only is AI_ADDRCONFIG unnecessary, but it can be harmful. If the 208 // computer is not connected to a network, AI_ADDRCONFIG causes getaddrinfo 209 // to fail with WSANO_DATA (11004) for "localhost", probably because of the 210 // following note on AI_ADDRCONFIG in the MSDN getaddrinfo page: 211 // The IPv4 or IPv6 loopback address is not considered a valid global 212 // address. 213 // See http://crbug.com/5234. 214 // 215 // OpenBSD does not support it, either. 216 hints.ai_flags = 0; 217#else 218 hints.ai_flags = AI_ADDRCONFIG; 219#endif 220 221 if (host_resolver_flags & HOST_RESOLVER_CANONNAME) 222 hints.ai_flags |= AI_CANONNAME; 223 224 // Restrict result set to only this socket type to avoid duplicates. 225 hints.ai_socktype = SOCK_STREAM; 226 227 int err = getaddrinfo(host.c_str(), NULL, &hints, &ai); 228#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_OPENBSD) && !defined(ANDROID) 229 net::DnsReloadTimer* dns_timer = Singleton<net::DnsReloadTimer>::get(); 230 // If we fail, re-initialise the resolver just in case there have been any 231 // changes to /etc/resolv.conf and retry. See http://crbug.com/11380 for info. 232 if (err && dns_timer->Expired()) { 233 res_nclose(&_res); 234 if (!res_ninit(&_res)) 235 err = getaddrinfo(host.c_str(), NULL, &hints, &ai); 236 } 237#endif 238 239 if (err) { 240 if (os_error) { 241#if defined(OS_WIN) 242 *os_error = WSAGetLastError(); 243#else 244 *os_error = err; 245#endif 246 } 247 return ERR_NAME_NOT_RESOLVED; 248 } 249 250 addrlist->Adopt(ai); 251 return OK; 252} 253 254} // namespace net 255