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