OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 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/dns/async_host_resolver.h" | |
6 | |
7 #include <algorithm> | |
8 | |
9 #include "base/bind.h" | |
10 #include "base/logging.h" | |
11 #include "base/message_loop.h" | |
12 #include "base/rand_util.h" | |
13 #include "base/stl_util.h" | |
14 #include "base/values.h" | |
15 #include "net/base/address_list.h" | |
16 #include "net/base/dns_util.h" | |
17 #include "net/base/net_errors.h" | |
18 #include "net/dns/dns_protocol.h" | |
19 #include "net/dns/dns_response.h" | |
20 #include "net/dns/dns_session.h" | |
21 #include "net/socket/client_socket_factory.h" | |
22 | |
23 namespace net { | |
24 | |
25 namespace { | |
26 | |
27 // TODO(agayev): fix this when IPv6 support is added. | |
28 uint16 QueryTypeFromAddressFamily(AddressFamily address_family) { | |
29 return dns_protocol::kTypeA; | |
30 } | |
31 | |
32 class RequestParameters : public NetLog::EventParameters { | |
33 public: | |
34 RequestParameters(const HostResolver::RequestInfo& info, | |
35 const NetLog::Source& source) | |
36 : info_(info), source_(source) {} | |
37 | |
38 virtual Value* ToValue() const { | |
39 DictionaryValue* dict = new DictionaryValue(); | |
40 dict->SetString("hostname", info_.host_port_pair().ToString()); | |
41 dict->SetInteger("address_family", | |
42 static_cast<int>(info_.address_family())); | |
43 dict->SetBoolean("allow_cached_response", info_.allow_cached_response()); | |
44 dict->SetBoolean("is_speculative", info_.is_speculative()); | |
45 dict->SetInteger("priority", info_.priority()); | |
46 | |
47 if (source_.is_valid()) | |
48 dict->Set("source_dependency", source_.ToValue()); | |
49 | |
50 return dict; | |
51 } | |
52 | |
53 private: | |
54 const HostResolver::RequestInfo info_; | |
55 const NetLog::Source source_; | |
56 }; | |
57 | |
58 } // namespace | |
59 | |
60 HostResolver* CreateAsyncHostResolver(size_t max_concurrent_resolves, | |
61 const IPAddressNumber& dns_ip, | |
62 NetLog* net_log) { | |
63 size_t max_dns_requests = max_concurrent_resolves; | |
64 if (max_dns_requests == 0) | |
65 max_dns_requests = 20; | |
66 size_t max_pending_requests = max_dns_requests * 100; | |
67 DnsConfig config; | |
68 config.nameservers.push_back(IPEndPoint(dns_ip, 53)); | |
69 DnsSession* session = new DnsSession( | |
70 config, | |
71 ClientSocketFactory::GetDefaultFactory(), | |
72 base::Bind(&base::RandInt), | |
73 net_log); | |
74 HostResolver* resolver = new AsyncHostResolver( | |
75 max_dns_requests, | |
76 max_pending_requests, | |
77 HostCache::CreateDefaultCache(), | |
78 DnsTransactionFactory::CreateFactory(session), | |
79 net_log); | |
80 return resolver; | |
81 } | |
82 | |
83 //----------------------------------------------------------------------------- | |
84 // Every call to Resolve() results in Request object being created. Such a | |
85 // call may complete either synchronously or asynchronously or it may get | |
86 // cancelled, which can be either through specific CancelRequest call or by | |
87 // the destruction of AsyncHostResolver, which would destruct pending or | |
88 // in-progress requests, causing them to be cancelled. Synchronous | |
89 // resolution sets |callback_| to NULL, thus, if in the destructor we still | |
90 // have a non-NULL |callback_|, we are being cancelled. | |
91 class AsyncHostResolver::Request { | |
92 public: | |
93 Request(AsyncHostResolver* resolver, | |
94 const BoundNetLog& source_net_log, | |
95 const BoundNetLog& request_net_log, | |
96 const HostResolver::RequestInfo& info, | |
97 const CompletionCallback& callback, | |
98 AddressList* addresses) | |
99 : resolver_(resolver), | |
100 source_net_log_(source_net_log), | |
101 request_net_log_(request_net_log), | |
102 info_(info), | |
103 callback_(callback), | |
104 addresses_(addresses), | |
105 result_(ERR_UNEXPECTED) { | |
106 DCHECK(addresses_); | |
107 DCHECK(resolver_); | |
108 resolver_->OnStart(this); | |
109 key_ = Key(info.hostname(), | |
110 QueryTypeFromAddressFamily(info.address_family())); | |
111 } | |
112 | |
113 ~Request() { | |
114 if (!callback_.is_null()) | |
115 resolver_->OnCancel(this); | |
116 } | |
117 | |
118 int result() const { return result_; } | |
119 const Key& key() const { | |
120 DCHECK(IsValid()); | |
121 return key_; | |
122 } | |
123 const HostResolver::RequestInfo& info() const { return info_; } | |
124 RequestPriority priority() const { return info_.priority(); } | |
125 const BoundNetLog& source_net_log() const { return source_net_log_; } | |
126 const BoundNetLog& request_net_log() const { return request_net_log_; } | |
127 | |
128 bool ResolveAsIp() { | |
129 IPAddressNumber ip_number; | |
130 if (!ParseIPLiteralToNumber(info_.hostname(), &ip_number)) | |
131 return false; | |
132 | |
133 if (ip_number.size() != kIPv4AddressSize) { | |
134 result_ = ERR_NAME_NOT_RESOLVED; | |
135 } else { | |
136 *addresses_ = AddressList::CreateFromIPAddressWithCname( | |
137 ip_number, | |
138 info_.port(), | |
139 info_.host_resolver_flags() & HOST_RESOLVER_CANONNAME); | |
140 result_ = OK; | |
141 } | |
142 return true; | |
143 } | |
144 | |
145 bool ServeFromCache() { | |
146 HostCache* cache = resolver_->cache_.get(); | |
147 if (!cache || !info_.allow_cached_response()) | |
148 return false; | |
149 | |
150 HostCache::Key key(info_.hostname(), info_.address_family(), | |
151 info_.host_resolver_flags()); | |
152 const HostCache::Entry* cache_entry = cache->Lookup( | |
153 key, base::TimeTicks::Now()); | |
154 if (cache_entry) { | |
155 request_net_log_.AddEvent( | |
156 NetLog::TYPE_ASYNC_HOST_RESOLVER_CACHE_HIT, NULL); | |
157 DCHECK_EQ(OK, cache_entry->error); | |
158 result_ = cache_entry->error; | |
159 *addresses_ = | |
160 CreateAddressListUsingPort(cache_entry->addrlist, info_.port()); | |
161 return true; | |
162 } | |
163 return false; | |
164 } | |
165 | |
166 // Called when a request completes synchronously; we do not have an | |
167 // AddressList argument, since in case of a successful synchronous | |
168 // completion, either ResolveAsIp or ServerFromCache would set the | |
169 // |addresses_| and in case of an unsuccessful synchronous completion, we | |
170 // do not touch |addresses_|. | |
171 void OnSyncComplete(int result) { | |
172 callback_.Reset(); | |
173 resolver_->OnFinish(this, result); | |
174 } | |
175 | |
176 // Called when a request completes asynchronously. | |
177 void OnAsyncComplete(int result, const AddressList& addresses) { | |
178 if (result == OK) | |
179 *addresses_ = CreateAddressListUsingPort(addresses, info_.port()); | |
180 DCHECK_EQ(false, callback_.is_null()); | |
181 CompletionCallback callback = callback_; | |
182 callback_.Reset(); | |
183 resolver_->OnFinish(this, result); | |
184 callback.Run(result); | |
185 } | |
186 | |
187 // Returns true if request has a validly formed hostname. | |
188 bool IsValid() const { | |
189 return !info_.hostname().empty() && !key_.first.empty(); | |
190 } | |
191 | |
192 private: | |
193 AsyncHostResolver* resolver_; | |
194 BoundNetLog source_net_log_; | |
195 BoundNetLog request_net_log_; | |
196 const HostResolver::RequestInfo info_; | |
197 Key key_; | |
198 CompletionCallback callback_; | |
199 AddressList* addresses_; | |
200 int result_; | |
201 }; | |
202 | |
203 //----------------------------------------------------------------------------- | |
204 AsyncHostResolver::AsyncHostResolver(size_t max_dns_requests, | |
205 size_t max_pending_requests, | |
206 HostCache* cache, | |
207 scoped_ptr<DnsTransactionFactory> client, | |
208 NetLog* net_log) | |
209 : max_dns_transactions_(max_dns_requests), | |
210 max_pending_requests_(max_pending_requests), | |
211 cache_(cache), | |
212 client_(client.Pass()), | |
213 net_log_(net_log) { | |
214 } | |
215 | |
216 AsyncHostResolver::~AsyncHostResolver() { | |
217 // Destroy request lists. | |
218 for (KeyRequestListMap::iterator it = requestlist_map_.begin(); | |
219 it != requestlist_map_.end(); ++it) | |
220 STLDeleteElements(&it->second); | |
221 | |
222 // Destroy DNS transactions. | |
223 STLDeleteElements(&dns_transactions_); | |
224 | |
225 // Destroy pending requests. | |
226 for (size_t i = 0; i < arraysize(pending_requests_); ++i) | |
227 STLDeleteElements(&pending_requests_[i]); | |
228 } | |
229 | |
230 int AsyncHostResolver::Resolve(const RequestInfo& info, | |
231 AddressList* addresses, | |
232 const CompletionCallback& callback, | |
233 RequestHandle* out_req, | |
234 const BoundNetLog& source_net_log) { | |
235 DCHECK(addresses); | |
236 DCHECK_EQ(false, callback.is_null()); | |
237 scoped_ptr<Request> request( | |
238 CreateNewRequest(info, callback, addresses, source_net_log)); | |
239 | |
240 int rv = ERR_UNEXPECTED; | |
241 if (!request->IsValid()) | |
242 rv = ERR_NAME_NOT_RESOLVED; | |
243 else if (request->ResolveAsIp() || request->ServeFromCache()) | |
244 rv = request->result(); | |
245 else if (AttachToRequestList(request.get())) | |
246 rv = ERR_IO_PENDING; | |
247 else if (dns_transactions_.size() < max_dns_transactions_) | |
248 rv = StartNewDnsRequestFor(request.get()); | |
249 else | |
250 rv = Enqueue(request.get()); | |
251 | |
252 if (rv != ERR_IO_PENDING) { | |
253 request->OnSyncComplete(rv); | |
254 } else { | |
255 Request* req = request.release(); | |
256 if (out_req) | |
257 *out_req = reinterpret_cast<RequestHandle>(req); | |
258 } | |
259 return rv; | |
260 } | |
261 | |
262 int AsyncHostResolver::ResolveFromCache(const RequestInfo& info, | |
263 AddressList* addresses, | |
264 const BoundNetLog& source_net_log) { | |
265 scoped_ptr<Request> request( | |
266 CreateNewRequest(info, CompletionCallback(), addresses, source_net_log)); | |
267 int rv = ERR_UNEXPECTED; | |
268 if (!request->IsValid()) | |
269 rv = ERR_NAME_NOT_RESOLVED; | |
270 else if (request->ResolveAsIp() || request->ServeFromCache()) | |
271 rv = request->result(); | |
272 else | |
273 rv = ERR_DNS_CACHE_MISS; | |
274 request->OnSyncComplete(rv); | |
275 return rv; | |
276 } | |
277 | |
278 void AsyncHostResolver::OnStart(Request* request) { | |
279 DCHECK(request); | |
280 | |
281 request->source_net_log().BeginEvent( | |
282 NetLog::TYPE_ASYNC_HOST_RESOLVER, | |
283 make_scoped_refptr(new NetLogSourceParameter( | |
284 "source_dependency", request->request_net_log().source()))); | |
285 request->request_net_log().BeginEvent( | |
286 NetLog::TYPE_ASYNC_HOST_RESOLVER_REQUEST, | |
287 make_scoped_refptr(new RequestParameters( | |
288 request->info(), request->source_net_log().source()))); | |
289 } | |
290 | |
291 void AsyncHostResolver::OnFinish(Request* request, int result) { | |
292 DCHECK(request); | |
293 request->request_net_log().EndEventWithNetErrorCode( | |
294 NetLog::TYPE_ASYNC_HOST_RESOLVER_REQUEST, result); | |
295 request->source_net_log().EndEvent( | |
296 NetLog::TYPE_ASYNC_HOST_RESOLVER, NULL); | |
297 } | |
298 | |
299 void AsyncHostResolver::OnCancel(Request* request) { | |
300 DCHECK(request); | |
301 | |
302 request->request_net_log().AddEvent( | |
303 NetLog::TYPE_CANCELLED, NULL); | |
304 request->request_net_log().EndEvent( | |
305 NetLog::TYPE_ASYNC_HOST_RESOLVER_REQUEST, NULL); | |
306 request->source_net_log().EndEvent( | |
307 NetLog::TYPE_ASYNC_HOST_RESOLVER, NULL); | |
308 } | |
309 | |
310 void AsyncHostResolver::CancelRequest(RequestHandle req_handle) { | |
311 scoped_ptr<Request> request(reinterpret_cast<Request*>(req_handle)); | |
312 DCHECK(request.get()); | |
313 | |
314 KeyRequestListMap::iterator it = requestlist_map_.find(request->key()); | |
315 if (it != requestlist_map_.end()) | |
316 it->second.remove(request.get()); | |
317 else | |
318 pending_requests_[request->priority()].remove(request.get()); | |
319 } | |
320 | |
321 void AsyncHostResolver::SetDefaultAddressFamily( | |
322 AddressFamily address_family) { | |
323 NOTIMPLEMENTED(); | |
324 } | |
325 | |
326 AddressFamily AsyncHostResolver::GetDefaultAddressFamily() const { | |
327 return ADDRESS_FAMILY_IPV4; | |
328 } | |
329 | |
330 HostCache* AsyncHostResolver::GetHostCache() { | |
331 return cache_.get(); | |
332 } | |
333 | |
334 void AsyncHostResolver::OnDnsTransactionComplete( | |
335 DnsTransaction* transaction, | |
336 int result, | |
337 const DnsResponse* response) { | |
338 DCHECK(std::find(dns_transactions_.begin(), | |
339 dns_transactions_.end(), | |
340 transaction) != dns_transactions_.end()); | |
341 | |
342 // If by the time requests that caused |transaction| are cancelled, we do | |
343 // not have a port number to associate with the result, therefore, we | |
344 // assume the most common port, otherwise we use the port number of the | |
345 // first request. | |
346 KeyRequestListMap::iterator rit = requestlist_map_.find( | |
347 std::make_pair(transaction->GetHostname(), transaction->GetType())); | |
348 DCHECK(rit != requestlist_map_.end()); | |
349 RequestList& requests = rit->second; | |
350 int port = requests.empty() ? 80 : requests.front()->info().port(); | |
351 | |
352 // Extract AddressList and TTL out of DnsResponse. | |
353 AddressList addr_list; | |
354 uint32 ttl = kuint32max; | |
355 if (result == OK) { | |
356 IPAddressList ip_addresses; | |
357 DnsRecordParser parser = response->Parser(); | |
358 DnsResourceRecord record; | |
359 // TODO(szym): Add stricter checking of names, aliases and address lengths. | |
360 while (parser.ParseRecord(&record)) { | |
361 if (record.type == transaction->GetType() && | |
362 (record.rdata.size() == kIPv4AddressSize || | |
363 record.rdata.size() == kIPv6AddressSize)) { | |
364 ip_addresses.push_back(IPAddressNumber(record.rdata.begin(), | |
365 record.rdata.end())); | |
366 ttl = std::min(ttl, record.ttl); | |
367 } | |
368 } | |
369 if (!ip_addresses.empty()) | |
370 addr_list = AddressList::CreateFromIPAddressList(ip_addresses, port); | |
371 else | |
372 result = ERR_NAME_NOT_RESOLVED; | |
373 } | |
374 | |
375 // Run callback of every request that was depending on this DNS request, | |
376 // also notify observers. | |
377 for (RequestList::iterator it = requests.begin(); it != requests.end(); ++it) | |
378 (*it)->OnAsyncComplete(result, addr_list); | |
379 | |
380 // It is possible that the requests that caused |transaction| to be | |
381 // created are cancelled by the time |transaction| completes. In that | |
382 // case |requests| would be empty. We are knowingly throwing away the | |
383 // result of a DNS resolution in that case, because (a) if there are no | |
384 // requests, we do not have info to obtain a key from, (b) DnsTransaction | |
385 // does not have info(). | |
386 // TODO(szym): Should DnsTransaction ignore HostResolverFlags or use defaults? | |
387 if ((result == OK || result == ERR_NAME_NOT_RESOLVED) && cache_.get() && | |
388 !requests.empty()) { | |
389 Request* request = requests.front(); | |
390 HostResolver::RequestInfo info = request->info(); | |
391 HostCache::Key key( | |
392 info.hostname(), info.address_family(), info.host_resolver_flags()); | |
393 // Store negative results with TTL 0 to flush out the old entry. | |
394 cache_->Set(key, | |
395 result, | |
396 addr_list, | |
397 base::TimeTicks::Now(), | |
398 (result == OK) ? base::TimeDelta::FromSeconds(ttl) | |
399 : base::TimeDelta()); | |
400 } | |
401 | |
402 // Cleanup requests. | |
403 STLDeleteElements(&requests); | |
404 requestlist_map_.erase(rit); | |
405 | |
406 // Cleanup |transaction| and start a new one if there are pending requests. | |
407 dns_transactions_.remove(transaction); | |
408 delete transaction; | |
409 ProcessPending(); | |
410 } | |
411 | |
412 AsyncHostResolver::Request* AsyncHostResolver::CreateNewRequest( | |
413 const RequestInfo& info, | |
414 const CompletionCallback& callback, | |
415 AddressList* addresses, | |
416 const BoundNetLog& source_net_log) { | |
417 BoundNetLog request_net_log = BoundNetLog::Make(net_log_, | |
418 NetLog::SOURCE_ASYNC_HOST_RESOLVER_REQUEST); | |
419 return new Request( | |
420 this, source_net_log, request_net_log, info, callback, addresses); | |
421 } | |
422 | |
423 bool AsyncHostResolver::AttachToRequestList(Request* request) { | |
424 KeyRequestListMap::iterator it = requestlist_map_.find(request->key()); | |
425 if (it == requestlist_map_.end()) | |
426 return false; | |
427 it->second.push_back(request); | |
428 return true; | |
429 } | |
430 | |
431 int AsyncHostResolver::StartNewDnsRequestFor(Request* request) { | |
432 DCHECK(requestlist_map_.find(request->key()) == requestlist_map_.end()); | |
433 DCHECK(dns_transactions_.size() < max_dns_transactions_); | |
434 | |
435 request->request_net_log().AddEvent( | |
436 NetLog::TYPE_ASYNC_HOST_RESOLVER_CREATE_DNS_TRANSACTION, NULL); | |
437 | |
438 requestlist_map_[request->key()].push_back(request); | |
439 scoped_ptr<DnsTransaction> transaction(client_->CreateTransaction( | |
440 request->key().first, | |
441 request->key().second, | |
442 base::Bind(&AsyncHostResolver::OnDnsTransactionComplete, | |
443 base::Unretained(this)), | |
444 request->request_net_log())); | |
445 int rv = transaction->Start(); | |
446 if (rv == ERR_IO_PENDING) | |
447 dns_transactions_.push_back(transaction.release()); | |
448 return rv; | |
449 } | |
450 | |
451 int AsyncHostResolver::Enqueue(Request* request) { | |
452 Request* evicted_request = Insert(request); | |
453 int rv = ERR_HOST_RESOLVER_QUEUE_TOO_LARGE; | |
454 if (evicted_request == request) | |
455 return rv; | |
456 if (evicted_request != NULL) { | |
457 evicted_request->OnAsyncComplete(rv, AddressList()); | |
458 delete evicted_request; | |
459 } | |
460 return ERR_IO_PENDING; | |
461 } | |
462 | |
463 AsyncHostResolver::Request* AsyncHostResolver::Insert(Request* request) { | |
464 pending_requests_[request->priority()].push_back(request); | |
465 if (GetNumPending() > max_pending_requests_) { | |
466 Request* req = RemoveLowest(); | |
467 DCHECK(req); | |
468 return req; | |
469 } | |
470 return NULL; | |
471 } | |
472 | |
473 size_t AsyncHostResolver::GetNumPending() const { | |
474 size_t num_pending = 0; | |
475 for (size_t i = 0; i < arraysize(pending_requests_); ++i) | |
476 num_pending += pending_requests_[i].size(); | |
477 return num_pending; | |
478 } | |
479 | |
480 AsyncHostResolver::Request* AsyncHostResolver::RemoveLowest() { | |
481 for (int i = static_cast<int>(arraysize(pending_requests_)) - 1; | |
482 i >= 0; --i) { | |
483 RequestList& requests = pending_requests_[i]; | |
484 if (!requests.empty()) { | |
485 Request* request = requests.front(); | |
486 requests.pop_front(); | |
487 return request; | |
488 } | |
489 } | |
490 return NULL; | |
491 } | |
492 | |
493 AsyncHostResolver::Request* AsyncHostResolver::RemoveHighest() { | |
494 for (size_t i = 0; i < arraysize(pending_requests_) - 1; ++i) { | |
495 RequestList& requests = pending_requests_[i]; | |
496 if (!requests.empty()) { | |
497 Request* request = requests.front(); | |
498 requests.pop_front(); | |
499 return request; | |
500 } | |
501 } | |
502 return NULL; | |
503 } | |
504 | |
505 void AsyncHostResolver::ProcessPending() { | |
506 Request* request = RemoveHighest(); | |
507 if (!request) | |
508 return; | |
509 for (size_t i = 0; i < arraysize(pending_requests_); ++i) { | |
510 RequestList& requests = pending_requests_[i]; | |
511 RequestList::iterator it = requests.begin(); | |
512 while (it != requests.end()) { | |
513 if (request->key() == (*it)->key()) { | |
514 requestlist_map_[request->key()].push_back(*it); | |
515 it = requests.erase(it); | |
516 } else { | |
517 ++it; | |
518 } | |
519 } | |
520 } | |
521 StartNewDnsRequestFor(request); | |
522 } | |
523 | |
524 } // namespace net | |
OLD | NEW |