blob: af9208787f30134079bed28fbbc2c45668327a74 [file] [log] [blame]
[email protected]b59ff372009-07-15 22:04:321// Copyright (c) 2006-2008 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_WIN)
10#include <ws2tcpip.h>
11#include <wspiapi.h> // Needed for Win2k compat.
12#elif defined(OS_POSIX)
13#include <netdb.h>
14#include <sys/socket.h>
15#endif
16#if defined(OS_LINUX)
17#include <resolv.h>
18#endif
19
20#include "base/logging.h"
21#include "base/time.h"
22#include "net/base/address_list.h"
23#include "net/base/net_errors.h"
24
25#if defined(OS_LINUX)
26#include "base/singleton.h"
27#include "base/thread_local_storage.h"
28#endif
29
30namespace net {
31
32HostResolverProc* HostResolverProc::default_proc_ = NULL;
33
34HostResolverProc::HostResolverProc(HostResolverProc* previous) {
35 set_previous_proc(previous);
36
37 // Implicitly fall-back to the global default procedure.
38 if (!previous)
39 set_previous_proc(default_proc_);
40}
41
42// static
43HostResolverProc* HostResolverProc::SetDefault(HostResolverProc* proc) {
44 HostResolverProc* old = default_proc_;
45 default_proc_ = proc;
46 return old;
47}
48
49// static
50HostResolverProc* HostResolverProc::GetDefault() {
51 return default_proc_;
52}
53
54int HostResolverProc::ResolveUsingPrevious(const std::string& host,
[email protected]123ab1e32009-10-21 19:12:5755 AddressFamily address_family,
[email protected]b59ff372009-07-15 22:04:3256 AddressList* addrlist) {
57 if (previous_proc_)
[email protected]123ab1e32009-10-21 19:12:5758 return previous_proc_->Resolve(host, address_family, addrlist);
[email protected]b59ff372009-07-15 22:04:3259
60 // Final fallback is the system resolver.
[email protected]123ab1e32009-10-21 19:12:5761 return SystemHostResolverProc(host, address_family, addrlist);
[email protected]b59ff372009-07-15 22:04:3262}
63
64#if defined(OS_LINUX)
65// On Linux changes to /etc/resolv.conf can go unnoticed thus resulting in
66// DNS queries failing either because nameservers are unknown on startup
67// or because nameserver info has changed as a result of e.g. connecting to
68// a new network. Some distributions patch glibc to stat /etc/resolv.conf
69// to try to automatically detect such changes but these patches are not
70// universal and even patched systems such as Jaunty appear to need calls
71// to res_ninit to reload the nameserver information in different threads.
72//
73// We adopt the Mozilla solution here which is to call res_ninit when
74// lookups fail and to rate limit the reloading to once per second per
75// thread.
76
77// Keep a timer per calling thread to rate limit the calling of res_ninit.
78class DnsReloadTimer {
79 public:
80 DnsReloadTimer() {
81 tls_index_.Initialize(SlotReturnFunction);
82 }
83
84 ~DnsReloadTimer() { }
85
86 // Check if the timer for the calling thread has expired. When no
87 // timer exists for the calling thread, create one.
88 bool Expired() {
89 const base::TimeDelta kRetryTime = base::TimeDelta::FromSeconds(1);
90 base::TimeTicks now = base::TimeTicks::Now();
91 base::TimeTicks* timer_ptr =
92 static_cast<base::TimeTicks*>(tls_index_.Get());
93
94 if (!timer_ptr) {
95 timer_ptr = new base::TimeTicks();
96 *timer_ptr = base::TimeTicks::Now();
97 tls_index_.Set(timer_ptr);
98 // Return true to reload dns info on the first call for each thread.
99 return true;
100 } else if (now - *timer_ptr > kRetryTime) {
101 *timer_ptr = now;
102 return true;
103 } else {
104 return false;
105 }
106 }
107
108 // Free the allocated timer.
109 static void SlotReturnFunction(void* data) {
110 base::TimeTicks* tls_data = static_cast<base::TimeTicks*>(data);
111 delete tls_data;
112 }
113
114 private:
115 // We use thread local storage to identify which base::TimeTicks to
116 // interact with.
117 static ThreadLocalStorage::Slot tls_index_ ;
118
119 DISALLOW_COPY_AND_ASSIGN(DnsReloadTimer);
120};
121
122// A TLS slot to the TimeTicks for the current thread.
123// static
124ThreadLocalStorage::Slot DnsReloadTimer::tls_index_(base::LINKER_INITIALIZED);
125
126#endif // defined(OS_LINUX)
127
[email protected]123ab1e32009-10-21 19:12:57128int SystemHostResolverProc(const std::string& host,
129 AddressFamily address_family,
130 AddressList* addrlist) {
[email protected]41547862009-08-17 20:47:02131 // The result of |getaddrinfo| for empty hosts is inconsistent across systems.
132 // On Windows it gives the default interface's address, whereas on Linux it
133 // gives an error. We will make it fail on all platforms for consistency.
134 if (host.empty())
135 return ERR_NAME_NOT_RESOLVED;
136
[email protected]b59ff372009-07-15 22:04:32137 struct addrinfo* ai = NULL;
138 struct addrinfo hints = {0};
[email protected]123ab1e32009-10-21 19:12:57139
140 switch (address_family) {
141 case ADDRESS_FAMILY_IPV4_ONLY:
142 hints.ai_family = AF_INET;
143 break;
144 default:
145 hints.ai_family = AF_UNSPEC;
146 }
[email protected]b59ff372009-07-15 22:04:32147
148#if defined(OS_WIN)
149 // DO NOT USE AI_ADDRCONFIG ON WINDOWS.
150 //
151 // The following comment in <winsock2.h> is the best documentation I found
152 // on AI_ADDRCONFIG for Windows:
153 // Flags used in "hints" argument to getaddrinfo()
154 // - AI_ADDRCONFIG is supported starting with Vista
155 // - default is AI_ADDRCONFIG ON whether the flag is set or not
156 // because the performance penalty in not having ADDRCONFIG in
157 // the multi-protocol stack environment is severe;
158 // this defaulting may be disabled by specifying the AI_ALL flag,
159 // in that case AI_ADDRCONFIG must be EXPLICITLY specified to
160 // enable ADDRCONFIG behavior
161 //
162 // Not only is AI_ADDRCONFIG unnecessary, but it can be harmful. If the
163 // computer is not connected to a network, AI_ADDRCONFIG causes getaddrinfo
164 // to fail with WSANO_DATA (11004) for "localhost", probably because of the
165 // following note on AI_ADDRCONFIG in the MSDN getaddrinfo page:
166 // The IPv4 or IPv6 loopback address is not considered a valid global
167 // address.
168 // See http://crbug.com/5234.
169 hints.ai_flags = 0;
[email protected]b59ff372009-07-15 22:04:32170#else
171 hints.ai_flags = AI_ADDRCONFIG;
172#endif
173
174 // Restrict result set to only this socket type to avoid duplicates.
175 hints.ai_socktype = SOCK_STREAM;
176
177 int err = getaddrinfo(host.c_str(), NULL, &hints, &ai);
178#if defined(OS_LINUX)
179 net::DnsReloadTimer* dns_timer = Singleton<net::DnsReloadTimer>::get();
180 // If we fail, re-initialise the resolver just in case there have been any
181 // changes to /etc/resolv.conf and retry. See http://crbug.com/11380 for info.
182 if (err && dns_timer->Expired()) {
183 res_nclose(&_res);
184 if (!res_ninit(&_res))
185 err = getaddrinfo(host.c_str(), NULL, &hints, &ai);
186 }
187#endif
188
189 if (err)
190 return ERR_NAME_NOT_RESOLVED;
191
192 addrlist->Adopt(ai);
193 return OK;
194}
195
196} // namespace net