| 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/host_resolver_impl.h" | |
| 6 | |
| 7 #if defined(OS_WIN) | |
| 8 #include <Winsock2.h> | |
| 9 #elif defined(OS_POSIX) | |
| 10 #include <netdb.h> | |
| 11 #endif | |
| 12 | |
| 13 #include <cmath> | |
| 14 #include <utility> | |
| 15 #include <vector> | |
| 16 | |
| 17 #include "base/basictypes.h" | |
| 18 #include "base/bind.h" | |
| 19 #include "base/bind_helpers.h" | |
| 20 #include "base/callback.h" | |
| 21 #include "base/compiler_specific.h" | |
| 22 #include "base/debug/debugger.h" | |
| 23 #include "base/debug/stack_trace.h" | |
| 24 #include "base/message_loop/message_loop_proxy.h" | |
| 25 #include "base/metrics/field_trial.h" | |
| 26 #include "base/metrics/histogram.h" | |
| 27 #include "base/profiler/scoped_tracker.h" | |
| 28 #include "base/stl_util.h" | |
| 29 #include "base/strings/string_util.h" | |
| 30 #include "base/strings/utf_string_conversions.h" | |
| 31 #include "base/threading/worker_pool.h" | |
| 32 #include "base/time/time.h" | |
| 33 #include "base/values.h" | |
| 34 #include "net/base/address_family.h" | |
| 35 #include "net/base/address_list.h" | |
| 36 #include "net/base/dns_reloader.h" | |
| 37 #include "net/base/dns_util.h" | |
| 38 #include "net/base/host_port_pair.h" | |
| 39 #include "net/base/ip_endpoint.h" | |
| 40 #include "net/base/net_errors.h" | |
| 41 #include "net/base/net_log.h" | |
| 42 #include "net/base/net_util.h" | |
| 43 #include "net/dns/address_sorter.h" | |
| 44 #include "net/dns/dns_client.h" | |
| 45 #include "net/dns/dns_config_service.h" | |
| 46 #include "net/dns/dns_protocol.h" | |
| 47 #include "net/dns/dns_response.h" | |
| 48 #include "net/dns/dns_transaction.h" | |
| 49 #include "net/dns/host_resolver_proc.h" | |
| 50 #include "net/socket/client_socket_factory.h" | |
| 51 #include "net/udp/datagram_client_socket.h" | |
| 52 #include "url/url_canon_ip.h" | |
| 53 | |
| 54 #if defined(OS_WIN) | |
| 55 #include "net/base/winsock_init.h" | |
| 56 #endif | |
| 57 | |
| 58 namespace net { | |
| 59 | |
| 60 namespace { | |
| 61 | |
| 62 // Limit the size of hostnames that will be resolved to combat issues in | |
| 63 // some platform's resolvers. | |
| 64 const size_t kMaxHostLength = 4096; | |
| 65 | |
| 66 // Default TTL for successful resolutions with ProcTask. | |
| 67 const unsigned kCacheEntryTTLSeconds = 60; | |
| 68 | |
| 69 // Default TTL for unsuccessful resolutions with ProcTask. | |
| 70 const unsigned kNegativeCacheEntryTTLSeconds = 0; | |
| 71 | |
| 72 // Minimum TTL for successful resolutions with DnsTask. | |
| 73 const unsigned kMinimumTTLSeconds = kCacheEntryTTLSeconds; | |
| 74 | |
| 75 // We use a separate histogram name for each platform to facilitate the | |
| 76 // display of error codes by their symbolic name (since each platform has | |
| 77 // different mappings). | |
| 78 const char kOSErrorsForGetAddrinfoHistogramName[] = | |
| 79 #if defined(OS_WIN) | |
| 80 "Net.OSErrorsForGetAddrinfo_Win"; | |
| 81 #elif defined(OS_MACOSX) | |
| 82 "Net.OSErrorsForGetAddrinfo_Mac"; | |
| 83 #elif defined(OS_LINUX) | |
| 84 "Net.OSErrorsForGetAddrinfo_Linux"; | |
| 85 #else | |
| 86 "Net.OSErrorsForGetAddrinfo"; | |
| 87 #endif | |
| 88 | |
| 89 // Gets a list of the likely error codes that getaddrinfo() can return | |
| 90 // (non-exhaustive). These are the error codes that we will track via | |
| 91 // a histogram. | |
| 92 std::vector<int> GetAllGetAddrinfoOSErrors() { | |
| 93 int os_errors[] = { | |
| 94 #if defined(OS_POSIX) | |
| 95 #if !defined(OS_FREEBSD) | |
| 96 #if !defined(OS_ANDROID) | |
| 97 // EAI_ADDRFAMILY has been declared obsolete in Android's and | |
| 98 // FreeBSD's netdb.h. | |
| 99 EAI_ADDRFAMILY, | |
| 100 #endif | |
| 101 // EAI_NODATA has been declared obsolete in FreeBSD's netdb.h. | |
| 102 EAI_NODATA, | |
| 103 #endif | |
| 104 EAI_AGAIN, | |
| 105 EAI_BADFLAGS, | |
| 106 EAI_FAIL, | |
| 107 EAI_FAMILY, | |
| 108 EAI_MEMORY, | |
| 109 EAI_NONAME, | |
| 110 EAI_SERVICE, | |
| 111 EAI_SOCKTYPE, | |
| 112 EAI_SYSTEM, | |
| 113 #elif defined(OS_WIN) | |
| 114 // See: http://msdn.microsoft.com/en-us/library/ms738520(VS.85).aspx | |
| 115 WSA_NOT_ENOUGH_MEMORY, | |
| 116 WSAEAFNOSUPPORT, | |
| 117 WSAEINVAL, | |
| 118 WSAESOCKTNOSUPPORT, | |
| 119 WSAHOST_NOT_FOUND, | |
| 120 WSANO_DATA, | |
| 121 WSANO_RECOVERY, | |
| 122 WSANOTINITIALISED, | |
| 123 WSATRY_AGAIN, | |
| 124 WSATYPE_NOT_FOUND, | |
| 125 // The following are not in doc, but might be to appearing in results :-(. | |
| 126 WSA_INVALID_HANDLE, | |
| 127 #endif | |
| 128 }; | |
| 129 | |
| 130 // Ensure all errors are positive, as histogram only tracks positive values. | |
| 131 for (size_t i = 0; i < arraysize(os_errors); ++i) { | |
| 132 os_errors[i] = std::abs(os_errors[i]); | |
| 133 } | |
| 134 | |
| 135 return base::CustomHistogram::ArrayToCustomRanges(os_errors, | |
| 136 arraysize(os_errors)); | |
| 137 } | |
| 138 | |
| 139 enum DnsResolveStatus { | |
| 140 RESOLVE_STATUS_DNS_SUCCESS = 0, | |
| 141 RESOLVE_STATUS_PROC_SUCCESS, | |
| 142 RESOLVE_STATUS_FAIL, | |
| 143 RESOLVE_STATUS_SUSPECT_NETBIOS, | |
| 144 RESOLVE_STATUS_MAX | |
| 145 }; | |
| 146 | |
| 147 void UmaAsyncDnsResolveStatus(DnsResolveStatus result) { | |
| 148 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.ResolveStatus", | |
| 149 result, | |
| 150 RESOLVE_STATUS_MAX); | |
| 151 } | |
| 152 | |
| 153 bool ResemblesNetBIOSName(const std::string& hostname) { | |
| 154 return (hostname.size() < 16) && (hostname.find('.') == std::string::npos); | |
| 155 } | |
| 156 | |
| 157 // True if |hostname| ends with either ".local" or ".local.". | |
| 158 bool ResemblesMulticastDNSName(const std::string& hostname) { | |
| 159 DCHECK(!hostname.empty()); | |
| 160 const char kSuffix[] = ".local."; | |
| 161 const size_t kSuffixLen = sizeof(kSuffix) - 1; | |
| 162 const size_t kSuffixLenTrimmed = kSuffixLen - 1; | |
| 163 if (hostname[hostname.size() - 1] == '.') { | |
| 164 return hostname.size() > kSuffixLen && | |
| 165 !hostname.compare(hostname.size() - kSuffixLen, kSuffixLen, kSuffix); | |
| 166 } | |
| 167 return hostname.size() > kSuffixLenTrimmed && | |
| 168 !hostname.compare(hostname.size() - kSuffixLenTrimmed, kSuffixLenTrimmed, | |
| 169 kSuffix, kSuffixLenTrimmed); | |
| 170 } | |
| 171 | |
| 172 // Attempts to connect a UDP socket to |dest|:53. | |
| 173 bool IsGloballyReachable(const IPAddressNumber& dest, | |
| 174 const BoundNetLog& net_log) { | |
| 175 scoped_ptr<DatagramClientSocket> socket( | |
| 176 ClientSocketFactory::GetDefaultFactory()->CreateDatagramClientSocket( | |
| 177 DatagramSocket::DEFAULT_BIND, | |
| 178 RandIntCallback(), | |
| 179 net_log.net_log(), | |
| 180 net_log.source())); | |
| 181 int rv = socket->Connect(IPEndPoint(dest, 53)); | |
| 182 if (rv != OK) | |
| 183 return false; | |
| 184 IPEndPoint endpoint; | |
| 185 rv = socket->GetLocalAddress(&endpoint); | |
| 186 if (rv != OK) | |
| 187 return false; | |
| 188 DCHECK_EQ(ADDRESS_FAMILY_IPV6, endpoint.GetFamily()); | |
| 189 const IPAddressNumber& address = endpoint.address(); | |
| 190 bool is_link_local = (address[0] == 0xFE) && ((address[1] & 0xC0) == 0x80); | |
| 191 if (is_link_local) | |
| 192 return false; | |
| 193 const uint8 kTeredoPrefix[] = { 0x20, 0x01, 0, 0 }; | |
| 194 bool is_teredo = std::equal(kTeredoPrefix, | |
| 195 kTeredoPrefix + arraysize(kTeredoPrefix), | |
| 196 address.begin()); | |
| 197 if (is_teredo) | |
| 198 return false; | |
| 199 return true; | |
| 200 } | |
| 201 | |
| 202 // Provide a common macro to simplify code and readability. We must use a | |
| 203 // macro as the underlying HISTOGRAM macro creates static variables. | |
| 204 #define DNS_HISTOGRAM(name, time) UMA_HISTOGRAM_CUSTOM_TIMES(name, time, \ | |
| 205 base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromHours(1), 100) | |
| 206 | |
| 207 // A macro to simplify code and readability. | |
| 208 #define DNS_HISTOGRAM_BY_PRIORITY(basename, priority, time) \ | |
| 209 do { \ | |
| 210 switch (priority) { \ | |
| 211 case HIGHEST: DNS_HISTOGRAM(basename "_HIGHEST", time); break; \ | |
| 212 case MEDIUM: DNS_HISTOGRAM(basename "_MEDIUM", time); break; \ | |
| 213 case LOW: DNS_HISTOGRAM(basename "_LOW", time); break; \ | |
| 214 case LOWEST: DNS_HISTOGRAM(basename "_LOWEST", time); break; \ | |
| 215 case IDLE: DNS_HISTOGRAM(basename "_IDLE", time); break; \ | |
| 216 default: NOTREACHED(); break; \ | |
| 217 } \ | |
| 218 DNS_HISTOGRAM(basename, time); \ | |
| 219 } while (0) | |
| 220 | |
| 221 // Record time from Request creation until a valid DNS response. | |
| 222 void RecordTotalTime(bool had_dns_config, | |
| 223 bool speculative, | |
| 224 base::TimeDelta duration) { | |
| 225 if (had_dns_config) { | |
| 226 if (speculative) { | |
| 227 DNS_HISTOGRAM("AsyncDNS.TotalTime_speculative", duration); | |
| 228 } else { | |
| 229 DNS_HISTOGRAM("AsyncDNS.TotalTime", duration); | |
| 230 } | |
| 231 } else { | |
| 232 if (speculative) { | |
| 233 DNS_HISTOGRAM("DNS.TotalTime_speculative", duration); | |
| 234 } else { | |
| 235 DNS_HISTOGRAM("DNS.TotalTime", duration); | |
| 236 } | |
| 237 } | |
| 238 } | |
| 239 | |
| 240 void RecordTTL(base::TimeDelta ttl) { | |
| 241 UMA_HISTOGRAM_CUSTOM_TIMES("AsyncDNS.TTL", ttl, | |
| 242 base::TimeDelta::FromSeconds(1), | |
| 243 base::TimeDelta::FromDays(1), 100); | |
| 244 } | |
| 245 | |
| 246 bool ConfigureAsyncDnsNoFallbackFieldTrial() { | |
| 247 const bool kDefault = false; | |
| 248 | |
| 249 // Configure the AsyncDns field trial as follows: | |
| 250 // groups AsyncDnsNoFallbackA and AsyncDnsNoFallbackB: return true, | |
| 251 // groups AsyncDnsA and AsyncDnsB: return false, | |
| 252 // groups SystemDnsA and SystemDnsB: return false, | |
| 253 // otherwise (trial absent): return default. | |
| 254 std::string group_name = base::FieldTrialList::FindFullName("AsyncDns"); | |
| 255 if (!group_name.empty()) | |
| 256 return StartsWithASCII(group_name, "AsyncDnsNoFallback", false); | |
| 257 return kDefault; | |
| 258 } | |
| 259 | |
| 260 //----------------------------------------------------------------------------- | |
| 261 | |
| 262 AddressList EnsurePortOnAddressList(const AddressList& list, uint16 port) { | |
| 263 if (list.empty() || list.front().port() == port) | |
| 264 return list; | |
| 265 return AddressList::CopyWithPort(list, port); | |
| 266 } | |
| 267 | |
| 268 // Returns true if |addresses| contains only IPv4 loopback addresses. | |
| 269 bool IsAllIPv4Loopback(const AddressList& addresses) { | |
| 270 for (unsigned i = 0; i < addresses.size(); ++i) { | |
| 271 const IPAddressNumber& address = addresses[i].address(); | |
| 272 switch (addresses[i].GetFamily()) { | |
| 273 case ADDRESS_FAMILY_IPV4: | |
| 274 if (address[0] != 127) | |
| 275 return false; | |
| 276 break; | |
| 277 case ADDRESS_FAMILY_IPV6: | |
| 278 return false; | |
| 279 default: | |
| 280 NOTREACHED(); | |
| 281 return false; | |
| 282 } | |
| 283 } | |
| 284 return true; | |
| 285 } | |
| 286 | |
| 287 // Creates NetLog parameters when the resolve failed. | |
| 288 base::Value* NetLogProcTaskFailedCallback(uint32 attempt_number, | |
| 289 int net_error, | |
| 290 int os_error, | |
| 291 NetLog::LogLevel /* log_level */) { | |
| 292 base::DictionaryValue* dict = new base::DictionaryValue(); | |
| 293 if (attempt_number) | |
| 294 dict->SetInteger("attempt_number", attempt_number); | |
| 295 | |
| 296 dict->SetInteger("net_error", net_error); | |
| 297 | |
| 298 if (os_error) { | |
| 299 dict->SetInteger("os_error", os_error); | |
| 300 #if defined(OS_POSIX) | |
| 301 dict->SetString("os_error_string", gai_strerror(os_error)); | |
| 302 #elif defined(OS_WIN) | |
| 303 // Map the error code to a human-readable string. | |
| 304 LPWSTR error_string = NULL; | |
| 305 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, | |
| 306 0, // Use the internal message table. | |
| 307 os_error, | |
| 308 0, // Use default language. | |
| 309 (LPWSTR)&error_string, | |
| 310 0, // Buffer size. | |
| 311 0); // Arguments (unused). | |
| 312 dict->SetString("os_error_string", base::WideToUTF8(error_string)); | |
| 313 LocalFree(error_string); | |
| 314 #endif | |
| 315 } | |
| 316 | |
| 317 return dict; | |
| 318 } | |
| 319 | |
| 320 // Creates NetLog parameters when the DnsTask failed. | |
| 321 base::Value* NetLogDnsTaskFailedCallback(int net_error, | |
| 322 int dns_error, | |
| 323 NetLog::LogLevel /* log_level */) { | |
| 324 base::DictionaryValue* dict = new base::DictionaryValue(); | |
| 325 dict->SetInteger("net_error", net_error); | |
| 326 if (dns_error) | |
| 327 dict->SetInteger("dns_error", dns_error); | |
| 328 return dict; | |
| 329 }; | |
| 330 | |
| 331 // Creates NetLog parameters containing the information in a RequestInfo object, | |
| 332 // along with the associated NetLog::Source. | |
| 333 base::Value* NetLogRequestInfoCallback(const HostResolver::RequestInfo* info, | |
| 334 NetLog::LogLevel /* log_level */) { | |
| 335 base::DictionaryValue* dict = new base::DictionaryValue(); | |
| 336 | |
| 337 dict->SetString("host", info->host_port_pair().ToString()); | |
| 338 dict->SetInteger("address_family", | |
| 339 static_cast<int>(info->address_family())); | |
| 340 dict->SetBoolean("allow_cached_response", info->allow_cached_response()); | |
| 341 dict->SetBoolean("is_speculative", info->is_speculative()); | |
| 342 return dict; | |
| 343 } | |
| 344 | |
| 345 // Creates NetLog parameters for the creation of a HostResolverImpl::Job. | |
| 346 base::Value* NetLogJobCreationCallback(const NetLog::Source& source, | |
| 347 const std::string* host, | |
| 348 NetLog::LogLevel /* log_level */) { | |
| 349 base::DictionaryValue* dict = new base::DictionaryValue(); | |
| 350 source.AddToEventParameters(dict); | |
| 351 dict->SetString("host", *host); | |
| 352 return dict; | |
| 353 } | |
| 354 | |
| 355 // Creates NetLog parameters for HOST_RESOLVER_IMPL_JOB_ATTACH/DETACH events. | |
| 356 base::Value* NetLogJobAttachCallback(const NetLog::Source& source, | |
| 357 RequestPriority priority, | |
| 358 NetLog::LogLevel /* log_level */) { | |
| 359 base::DictionaryValue* dict = new base::DictionaryValue(); | |
| 360 source.AddToEventParameters(dict); | |
| 361 dict->SetString("priority", RequestPriorityToString(priority)); | |
| 362 return dict; | |
| 363 } | |
| 364 | |
| 365 // Creates NetLog parameters for the DNS_CONFIG_CHANGED event. | |
| 366 base::Value* NetLogDnsConfigCallback(const DnsConfig* config, | |
| 367 NetLog::LogLevel /* log_level */) { | |
| 368 return config->ToValue(); | |
| 369 } | |
| 370 | |
| 371 // The logging routines are defined here because some requests are resolved | |
| 372 // without a Request object. | |
| 373 | |
| 374 // Logs when a request has just been started. | |
| 375 void LogStartRequest(const BoundNetLog& source_net_log, | |
| 376 const HostResolver::RequestInfo& info) { | |
| 377 source_net_log.BeginEvent( | |
| 378 NetLog::TYPE_HOST_RESOLVER_IMPL_REQUEST, | |
| 379 base::Bind(&NetLogRequestInfoCallback, &info)); | |
| 380 } | |
| 381 | |
| 382 // Logs when a request has just completed (before its callback is run). | |
| 383 void LogFinishRequest(const BoundNetLog& source_net_log, | |
| 384 const HostResolver::RequestInfo& info, | |
| 385 int net_error) { | |
| 386 source_net_log.EndEventWithNetErrorCode( | |
| 387 NetLog::TYPE_HOST_RESOLVER_IMPL_REQUEST, net_error); | |
| 388 } | |
| 389 | |
| 390 // Logs when a request has been cancelled. | |
| 391 void LogCancelRequest(const BoundNetLog& source_net_log, | |
| 392 const HostResolverImpl::RequestInfo& info) { | |
| 393 source_net_log.AddEvent(NetLog::TYPE_CANCELLED); | |
| 394 source_net_log.EndEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_REQUEST); | |
| 395 } | |
| 396 | |
| 397 //----------------------------------------------------------------------------- | |
| 398 | |
| 399 // Keeps track of the highest priority. | |
| 400 class PriorityTracker { | |
| 401 public: | |
| 402 explicit PriorityTracker(RequestPriority initial_priority) | |
| 403 : highest_priority_(initial_priority), total_count_(0) { | |
| 404 memset(counts_, 0, sizeof(counts_)); | |
| 405 } | |
| 406 | |
| 407 RequestPriority highest_priority() const { | |
| 408 return highest_priority_; | |
| 409 } | |
| 410 | |
| 411 size_t total_count() const { | |
| 412 return total_count_; | |
| 413 } | |
| 414 | |
| 415 void Add(RequestPriority req_priority) { | |
| 416 ++total_count_; | |
| 417 ++counts_[req_priority]; | |
| 418 if (highest_priority_ < req_priority) | |
| 419 highest_priority_ = req_priority; | |
| 420 } | |
| 421 | |
| 422 void Remove(RequestPriority req_priority) { | |
| 423 DCHECK_GT(total_count_, 0u); | |
| 424 DCHECK_GT(counts_[req_priority], 0u); | |
| 425 --total_count_; | |
| 426 --counts_[req_priority]; | |
| 427 size_t i; | |
| 428 for (i = highest_priority_; i > MINIMUM_PRIORITY && !counts_[i]; --i); | |
| 429 highest_priority_ = static_cast<RequestPriority>(i); | |
| 430 | |
| 431 // In absence of requests, default to MINIMUM_PRIORITY. | |
| 432 if (total_count_ == 0) | |
| 433 DCHECK_EQ(MINIMUM_PRIORITY, highest_priority_); | |
| 434 } | |
| 435 | |
| 436 private: | |
| 437 RequestPriority highest_priority_; | |
| 438 size_t total_count_; | |
| 439 size_t counts_[NUM_PRIORITIES]; | |
| 440 }; | |
| 441 | |
| 442 } // namespace | |
| 443 | |
| 444 //----------------------------------------------------------------------------- | |
| 445 | |
| 446 const unsigned HostResolverImpl::kMaximumDnsFailures = 16; | |
| 447 | |
| 448 // Holds the data for a request that could not be completed synchronously. | |
| 449 // It is owned by a Job. Canceled Requests are only marked as canceled rather | |
| 450 // than removed from the Job's |requests_| list. | |
| 451 class HostResolverImpl::Request { | |
| 452 public: | |
| 453 Request(const BoundNetLog& source_net_log, | |
| 454 const RequestInfo& info, | |
| 455 RequestPriority priority, | |
| 456 const CompletionCallback& callback, | |
| 457 AddressList* addresses) | |
| 458 : source_net_log_(source_net_log), | |
| 459 info_(info), | |
| 460 priority_(priority), | |
| 461 job_(NULL), | |
| 462 callback_(callback), | |
| 463 addresses_(addresses), | |
| 464 request_time_(base::TimeTicks::Now()) {} | |
| 465 | |
| 466 // Mark the request as canceled. | |
| 467 void MarkAsCanceled() { | |
| 468 job_ = NULL; | |
| 469 addresses_ = NULL; | |
| 470 callback_.Reset(); | |
| 471 } | |
| 472 | |
| 473 bool was_canceled() const { | |
| 474 return callback_.is_null(); | |
| 475 } | |
| 476 | |
| 477 void set_job(Job* job) { | |
| 478 DCHECK(job); | |
| 479 // Identify which job the request is waiting on. | |
| 480 job_ = job; | |
| 481 } | |
| 482 | |
| 483 // Prepare final AddressList and call completion callback. | |
| 484 void OnComplete(int error, const AddressList& addr_list) { | |
| 485 // TODO(vadimt): Remove ScopedTracker below once crbug.com/436634 is fixed. | |
| 486 tracked_objects::ScopedTracker tracking_profile( | |
| 487 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 488 "436634 HostResolverImpl::Request::OnComplete")); | |
| 489 | |
| 490 DCHECK(!was_canceled()); | |
| 491 if (error == OK) | |
| 492 *addresses_ = EnsurePortOnAddressList(addr_list, info_.port()); | |
| 493 CompletionCallback callback = callback_; | |
| 494 MarkAsCanceled(); | |
| 495 callback.Run(error); | |
| 496 } | |
| 497 | |
| 498 Job* job() const { | |
| 499 return job_; | |
| 500 } | |
| 501 | |
| 502 // NetLog for the source, passed in HostResolver::Resolve. | |
| 503 const BoundNetLog& source_net_log() { | |
| 504 return source_net_log_; | |
| 505 } | |
| 506 | |
| 507 const RequestInfo& info() const { | |
| 508 return info_; | |
| 509 } | |
| 510 | |
| 511 RequestPriority priority() const { return priority_; } | |
| 512 | |
| 513 base::TimeTicks request_time() const { return request_time_; } | |
| 514 | |
| 515 private: | |
| 516 const BoundNetLog source_net_log_; | |
| 517 | |
| 518 // The request info that started the request. | |
| 519 const RequestInfo info_; | |
| 520 | |
| 521 // TODO(akalin): Support reprioritization. | |
| 522 const RequestPriority priority_; | |
| 523 | |
| 524 // The resolve job that this request is dependent on. | |
| 525 Job* job_; | |
| 526 | |
| 527 // The user's callback to invoke when the request completes. | |
| 528 CompletionCallback callback_; | |
| 529 | |
| 530 // The address list to save result into. | |
| 531 AddressList* addresses_; | |
| 532 | |
| 533 const base::TimeTicks request_time_; | |
| 534 | |
| 535 DISALLOW_COPY_AND_ASSIGN(Request); | |
| 536 }; | |
| 537 | |
| 538 //------------------------------------------------------------------------------ | |
| 539 | |
| 540 // Calls HostResolverProc on the WorkerPool. Performs retries if necessary. | |
| 541 // | |
| 542 // Whenever we try to resolve the host, we post a delayed task to check if host | |
| 543 // resolution (OnLookupComplete) is completed or not. If the original attempt | |
| 544 // hasn't completed, then we start another attempt for host resolution. We take | |
| 545 // the results from the first attempt that finishes and ignore the results from | |
| 546 // all other attempts. | |
| 547 // | |
| 548 // TODO(szym): Move to separate source file for testing and mocking. | |
| 549 // | |
| 550 class HostResolverImpl::ProcTask | |
| 551 : public base::RefCountedThreadSafe<HostResolverImpl::ProcTask> { | |
| 552 public: | |
| 553 typedef base::Callback<void(int net_error, | |
| 554 const AddressList& addr_list)> Callback; | |
| 555 | |
| 556 ProcTask(const Key& key, | |
| 557 const ProcTaskParams& params, | |
| 558 const Callback& callback, | |
| 559 const BoundNetLog& job_net_log) | |
| 560 : key_(key), | |
| 561 params_(params), | |
| 562 callback_(callback), | |
| 563 origin_loop_(base::MessageLoopProxy::current()), | |
| 564 attempt_number_(0), | |
| 565 completed_attempt_number_(0), | |
| 566 completed_attempt_error_(ERR_UNEXPECTED), | |
| 567 had_non_speculative_request_(false), | |
| 568 net_log_(job_net_log) { | |
| 569 if (!params_.resolver_proc.get()) | |
| 570 params_.resolver_proc = HostResolverProc::GetDefault(); | |
| 571 // If default is unset, use the system proc. | |
| 572 if (!params_.resolver_proc.get()) | |
| 573 params_.resolver_proc = new SystemHostResolverProc(); | |
| 574 } | |
| 575 | |
| 576 void Start() { | |
| 577 DCHECK(origin_loop_->BelongsToCurrentThread()); | |
| 578 net_log_.BeginEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_PROC_TASK); | |
| 579 StartLookupAttempt(); | |
| 580 } | |
| 581 | |
| 582 // Cancels this ProcTask. It will be orphaned. Any outstanding resolve | |
| 583 // attempts running on worker threads will continue running. Only once all the | |
| 584 // attempts complete will the final reference to this ProcTask be released. | |
| 585 void Cancel() { | |
| 586 DCHECK(origin_loop_->BelongsToCurrentThread()); | |
| 587 | |
| 588 if (was_canceled() || was_completed()) | |
| 589 return; | |
| 590 | |
| 591 callback_.Reset(); | |
| 592 net_log_.EndEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_PROC_TASK); | |
| 593 } | |
| 594 | |
| 595 void set_had_non_speculative_request() { | |
| 596 DCHECK(origin_loop_->BelongsToCurrentThread()); | |
| 597 had_non_speculative_request_ = true; | |
| 598 } | |
| 599 | |
| 600 bool was_canceled() const { | |
| 601 DCHECK(origin_loop_->BelongsToCurrentThread()); | |
| 602 return callback_.is_null(); | |
| 603 } | |
| 604 | |
| 605 bool was_completed() const { | |
| 606 DCHECK(origin_loop_->BelongsToCurrentThread()); | |
| 607 return completed_attempt_number_ > 0; | |
| 608 } | |
| 609 | |
| 610 private: | |
| 611 friend class base::RefCountedThreadSafe<ProcTask>; | |
| 612 ~ProcTask() {} | |
| 613 | |
| 614 void StartLookupAttempt() { | |
| 615 DCHECK(origin_loop_->BelongsToCurrentThread()); | |
| 616 base::TimeTicks start_time = base::TimeTicks::Now(); | |
| 617 ++attempt_number_; | |
| 618 // Dispatch the lookup attempt to a worker thread. | |
| 619 if (!base::WorkerPool::PostTask( | |
| 620 FROM_HERE, | |
| 621 base::Bind(&ProcTask::DoLookup, this, start_time, attempt_number_), | |
| 622 true)) { | |
| 623 NOTREACHED(); | |
| 624 | |
| 625 // Since we could be running within Resolve() right now, we can't just | |
| 626 // call OnLookupComplete(). Instead we must wait until Resolve() has | |
| 627 // returned (IO_PENDING). | |
| 628 origin_loop_->PostTask( | |
| 629 FROM_HERE, | |
| 630 base::Bind(&ProcTask::OnLookupComplete, this, AddressList(), | |
| 631 start_time, attempt_number_, ERR_UNEXPECTED, 0)); | |
| 632 return; | |
| 633 } | |
| 634 | |
| 635 net_log_.AddEvent( | |
| 636 NetLog::TYPE_HOST_RESOLVER_IMPL_ATTEMPT_STARTED, | |
| 637 NetLog::IntegerCallback("attempt_number", attempt_number_)); | |
| 638 | |
| 639 // If we don't get the results within a given time, RetryIfNotComplete | |
| 640 // will start a new attempt on a different worker thread if none of our | |
| 641 // outstanding attempts have completed yet. | |
| 642 if (attempt_number_ <= params_.max_retry_attempts) { | |
| 643 origin_loop_->PostDelayedTask( | |
| 644 FROM_HERE, | |
| 645 base::Bind(&ProcTask::RetryIfNotComplete, this), | |
| 646 params_.unresponsive_delay); | |
| 647 } | |
| 648 } | |
| 649 | |
| 650 // WARNING: This code runs inside a worker pool. The shutdown code cannot | |
| 651 // wait for it to finish, so we must be very careful here about using other | |
| 652 // objects (like MessageLoops, Singletons, etc). During shutdown these objects | |
| 653 // may no longer exist. Multiple DoLookups() could be running in parallel, so | |
| 654 // any state inside of |this| must not mutate . | |
| 655 void DoLookup(const base::TimeTicks& start_time, | |
| 656 const uint32 attempt_number) { | |
| 657 AddressList results; | |
| 658 int os_error = 0; | |
| 659 // Running on the worker thread | |
| 660 int error = params_.resolver_proc->Resolve(key_.hostname, | |
| 661 key_.address_family, | |
| 662 key_.host_resolver_flags, | |
| 663 &results, | |
| 664 &os_error); | |
| 665 | |
| 666 origin_loop_->PostTask( | |
| 667 FROM_HERE, | |
| 668 base::Bind(&ProcTask::OnLookupComplete, this, results, start_time, | |
| 669 attempt_number, error, os_error)); | |
| 670 } | |
| 671 | |
| 672 // Makes next attempt if DoLookup() has not finished (runs on origin thread). | |
| 673 void RetryIfNotComplete() { | |
| 674 DCHECK(origin_loop_->BelongsToCurrentThread()); | |
| 675 | |
| 676 if (was_completed() || was_canceled()) | |
| 677 return; | |
| 678 | |
| 679 params_.unresponsive_delay *= params_.retry_factor; | |
| 680 StartLookupAttempt(); | |
| 681 } | |
| 682 | |
| 683 // Callback for when DoLookup() completes (runs on origin thread). | |
| 684 void OnLookupComplete(const AddressList& results, | |
| 685 const base::TimeTicks& start_time, | |
| 686 const uint32 attempt_number, | |
| 687 int error, | |
| 688 const int os_error) { | |
| 689 // TODO(vadimt): Remove ScopedTracker below once crbug.com/436634 is fixed. | |
| 690 tracked_objects::ScopedTracker tracking_profile1( | |
| 691 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 692 "436634 HostResolverImpl::ProcTask::OnLookupComplete1")); | |
| 693 | |
| 694 DCHECK(origin_loop_->BelongsToCurrentThread()); | |
| 695 // If results are empty, we should return an error. | |
| 696 bool empty_list_on_ok = (error == OK && results.empty()); | |
| 697 UMA_HISTOGRAM_BOOLEAN("DNS.EmptyAddressListAndNoError", empty_list_on_ok); | |
| 698 if (empty_list_on_ok) | |
| 699 error = ERR_NAME_NOT_RESOLVED; | |
| 700 | |
| 701 bool was_retry_attempt = attempt_number > 1; | |
| 702 | |
| 703 // Ideally the following code would be part of host_resolver_proc.cc, | |
| 704 // however it isn't safe to call NetworkChangeNotifier from worker threads. | |
| 705 // So we do it here on the IO thread instead. | |
| 706 if (error != OK && NetworkChangeNotifier::IsOffline()) | |
| 707 error = ERR_INTERNET_DISCONNECTED; | |
| 708 | |
| 709 // If this is the first attempt that is finishing later, then record data | |
| 710 // for the first attempt. Won't contaminate with retry attempt's data. | |
| 711 if (!was_retry_attempt) | |
| 712 RecordPerformanceHistograms(start_time, error, os_error); | |
| 713 | |
| 714 RecordAttemptHistograms(start_time, attempt_number, error, os_error); | |
| 715 | |
| 716 if (was_canceled()) | |
| 717 return; | |
| 718 | |
| 719 NetLog::ParametersCallback net_log_callback; | |
| 720 if (error != OK) { | |
| 721 net_log_callback = base::Bind(&NetLogProcTaskFailedCallback, | |
| 722 attempt_number, | |
| 723 error, | |
| 724 os_error); | |
| 725 } else { | |
| 726 net_log_callback = NetLog::IntegerCallback("attempt_number", | |
| 727 attempt_number); | |
| 728 } | |
| 729 net_log_.AddEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_ATTEMPT_FINISHED, | |
| 730 net_log_callback); | |
| 731 | |
| 732 if (was_completed()) | |
| 733 return; | |
| 734 | |
| 735 // Copy the results from the first worker thread that resolves the host. | |
| 736 results_ = results; | |
| 737 completed_attempt_number_ = attempt_number; | |
| 738 completed_attempt_error_ = error; | |
| 739 | |
| 740 if (was_retry_attempt) { | |
| 741 // If retry attempt finishes before 1st attempt, then get stats on how | |
| 742 // much time is saved by having spawned an extra attempt. | |
| 743 retry_attempt_finished_time_ = base::TimeTicks::Now(); | |
| 744 } | |
| 745 | |
| 746 if (error != OK) { | |
| 747 net_log_callback = base::Bind(&NetLogProcTaskFailedCallback, | |
| 748 0, error, os_error); | |
| 749 } else { | |
| 750 net_log_callback = results_.CreateNetLogCallback(); | |
| 751 } | |
| 752 net_log_.EndEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_PROC_TASK, | |
| 753 net_log_callback); | |
| 754 | |
| 755 // TODO(vadimt): Remove ScopedTracker below once crbug.com/436634 is fixed. | |
| 756 tracked_objects::ScopedTracker tracking_profile2( | |
| 757 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 758 "436634 HostResolverImpl::ProcTask::OnLookupComplete2")); | |
| 759 | |
| 760 callback_.Run(error, results_); | |
| 761 } | |
| 762 | |
| 763 void RecordPerformanceHistograms(const base::TimeTicks& start_time, | |
| 764 const int error, | |
| 765 const int os_error) const { | |
| 766 DCHECK(origin_loop_->BelongsToCurrentThread()); | |
| 767 enum Category { // Used in UMA_HISTOGRAM_ENUMERATION. | |
| 768 RESOLVE_SUCCESS, | |
| 769 RESOLVE_FAIL, | |
| 770 RESOLVE_SPECULATIVE_SUCCESS, | |
| 771 RESOLVE_SPECULATIVE_FAIL, | |
| 772 RESOLVE_MAX, // Bounding value. | |
| 773 }; | |
| 774 int category = RESOLVE_MAX; // Illegal value for later DCHECK only. | |
| 775 | |
| 776 base::TimeDelta duration = base::TimeTicks::Now() - start_time; | |
| 777 if (error == OK) { | |
| 778 if (had_non_speculative_request_) { | |
| 779 category = RESOLVE_SUCCESS; | |
| 780 DNS_HISTOGRAM("DNS.ResolveSuccess", duration); | |
| 781 } else { | |
| 782 category = RESOLVE_SPECULATIVE_SUCCESS; | |
| 783 DNS_HISTOGRAM("DNS.ResolveSpeculativeSuccess", duration); | |
| 784 } | |
| 785 | |
| 786 // Log DNS lookups based on |address_family|. This will help us determine | |
| 787 // if IPv4 or IPv4/6 lookups are faster or slower. | |
| 788 switch(key_.address_family) { | |
| 789 case ADDRESS_FAMILY_IPV4: | |
| 790 DNS_HISTOGRAM("DNS.ResolveSuccess_FAMILY_IPV4", duration); | |
| 791 break; | |
| 792 case ADDRESS_FAMILY_IPV6: | |
| 793 DNS_HISTOGRAM("DNS.ResolveSuccess_FAMILY_IPV6", duration); | |
| 794 break; | |
| 795 case ADDRESS_FAMILY_UNSPECIFIED: | |
| 796 DNS_HISTOGRAM("DNS.ResolveSuccess_FAMILY_UNSPEC", duration); | |
| 797 break; | |
| 798 } | |
| 799 } else { | |
| 800 if (had_non_speculative_request_) { | |
| 801 category = RESOLVE_FAIL; | |
| 802 DNS_HISTOGRAM("DNS.ResolveFail", duration); | |
| 803 } else { | |
| 804 category = RESOLVE_SPECULATIVE_FAIL; | |
| 805 DNS_HISTOGRAM("DNS.ResolveSpeculativeFail", duration); | |
| 806 } | |
| 807 // Log DNS lookups based on |address_family|. This will help us determine | |
| 808 // if IPv4 or IPv4/6 lookups are faster or slower. | |
| 809 switch(key_.address_family) { | |
| 810 case ADDRESS_FAMILY_IPV4: | |
| 811 DNS_HISTOGRAM("DNS.ResolveFail_FAMILY_IPV4", duration); | |
| 812 break; | |
| 813 case ADDRESS_FAMILY_IPV6: | |
| 814 DNS_HISTOGRAM("DNS.ResolveFail_FAMILY_IPV6", duration); | |
| 815 break; | |
| 816 case ADDRESS_FAMILY_UNSPECIFIED: | |
| 817 DNS_HISTOGRAM("DNS.ResolveFail_FAMILY_UNSPEC", duration); | |
| 818 break; | |
| 819 } | |
| 820 UMA_HISTOGRAM_CUSTOM_ENUMERATION(kOSErrorsForGetAddrinfoHistogramName, | |
| 821 std::abs(os_error), | |
| 822 GetAllGetAddrinfoOSErrors()); | |
| 823 } | |
| 824 DCHECK_LT(category, static_cast<int>(RESOLVE_MAX)); // Be sure it was set. | |
| 825 | |
| 826 UMA_HISTOGRAM_ENUMERATION("DNS.ResolveCategory", category, RESOLVE_MAX); | |
| 827 } | |
| 828 | |
| 829 void RecordAttemptHistograms(const base::TimeTicks& start_time, | |
| 830 const uint32 attempt_number, | |
| 831 const int error, | |
| 832 const int os_error) const { | |
| 833 DCHECK(origin_loop_->BelongsToCurrentThread()); | |
| 834 bool first_attempt_to_complete = | |
| 835 completed_attempt_number_ == attempt_number; | |
| 836 bool is_first_attempt = (attempt_number == 1); | |
| 837 | |
| 838 if (first_attempt_to_complete) { | |
| 839 // If this was first attempt to complete, then record the resolution | |
| 840 // status of the attempt. | |
| 841 if (completed_attempt_error_ == OK) { | |
| 842 UMA_HISTOGRAM_ENUMERATION( | |
| 843 "DNS.AttemptFirstSuccess", attempt_number, 100); | |
| 844 } else { | |
| 845 UMA_HISTOGRAM_ENUMERATION( | |
| 846 "DNS.AttemptFirstFailure", attempt_number, 100); | |
| 847 } | |
| 848 } | |
| 849 | |
| 850 if (error == OK) | |
| 851 UMA_HISTOGRAM_ENUMERATION("DNS.AttemptSuccess", attempt_number, 100); | |
| 852 else | |
| 853 UMA_HISTOGRAM_ENUMERATION("DNS.AttemptFailure", attempt_number, 100); | |
| 854 | |
| 855 // If first attempt didn't finish before retry attempt, then calculate stats | |
| 856 // on how much time is saved by having spawned an extra attempt. | |
| 857 if (!first_attempt_to_complete && is_first_attempt && !was_canceled()) { | |
| 858 DNS_HISTOGRAM("DNS.AttemptTimeSavedByRetry", | |
| 859 base::TimeTicks::Now() - retry_attempt_finished_time_); | |
| 860 } | |
| 861 | |
| 862 if (was_canceled() || !first_attempt_to_complete) { | |
| 863 // Count those attempts which completed after the job was already canceled | |
| 864 // OR after the job was already completed by an earlier attempt (so in | |
| 865 // effect). | |
| 866 UMA_HISTOGRAM_ENUMERATION("DNS.AttemptDiscarded", attempt_number, 100); | |
| 867 | |
| 868 // Record if job is canceled. | |
| 869 if (was_canceled()) | |
| 870 UMA_HISTOGRAM_ENUMERATION("DNS.AttemptCancelled", attempt_number, 100); | |
| 871 } | |
| 872 | |
| 873 base::TimeDelta duration = base::TimeTicks::Now() - start_time; | |
| 874 if (error == OK) | |
| 875 DNS_HISTOGRAM("DNS.AttemptSuccessDuration", duration); | |
| 876 else | |
| 877 DNS_HISTOGRAM("DNS.AttemptFailDuration", duration); | |
| 878 } | |
| 879 | |
| 880 // Set on the origin thread, read on the worker thread. | |
| 881 Key key_; | |
| 882 | |
| 883 // Holds an owning reference to the HostResolverProc that we are going to use. | |
| 884 // This may not be the current resolver procedure by the time we call | |
| 885 // ResolveAddrInfo, but that's OK... we'll use it anyways, and the owning | |
| 886 // reference ensures that it remains valid until we are done. | |
| 887 ProcTaskParams params_; | |
| 888 | |
| 889 // The listener to the results of this ProcTask. | |
| 890 Callback callback_; | |
| 891 | |
| 892 // Used to post ourselves onto the origin thread. | |
| 893 scoped_refptr<base::MessageLoopProxy> origin_loop_; | |
| 894 | |
| 895 // Keeps track of the number of attempts we have made so far to resolve the | |
| 896 // host. Whenever we start an attempt to resolve the host, we increase this | |
| 897 // number. | |
| 898 uint32 attempt_number_; | |
| 899 | |
| 900 // The index of the attempt which finished first (or 0 if the job is still in | |
| 901 // progress). | |
| 902 uint32 completed_attempt_number_; | |
| 903 | |
| 904 // The result (a net error code) from the first attempt to complete. | |
| 905 int completed_attempt_error_; | |
| 906 | |
| 907 // The time when retry attempt was finished. | |
| 908 base::TimeTicks retry_attempt_finished_time_; | |
| 909 | |
| 910 // True if a non-speculative request was ever attached to this job | |
| 911 // (regardless of whether or not it was later canceled. | |
| 912 // This boolean is used for histogramming the duration of jobs used to | |
| 913 // service non-speculative requests. | |
| 914 bool had_non_speculative_request_; | |
| 915 | |
| 916 AddressList results_; | |
| 917 | |
| 918 BoundNetLog net_log_; | |
| 919 | |
| 920 DISALLOW_COPY_AND_ASSIGN(ProcTask); | |
| 921 }; | |
| 922 | |
| 923 //----------------------------------------------------------------------------- | |
| 924 | |
| 925 // Wraps a call to HaveOnlyLoopbackAddresses to be executed on the WorkerPool as | |
| 926 // it takes 40-100ms and should not block initialization. | |
| 927 class HostResolverImpl::LoopbackProbeJob { | |
| 928 public: | |
| 929 explicit LoopbackProbeJob(const base::WeakPtr<HostResolverImpl>& resolver) | |
| 930 : resolver_(resolver), | |
| 931 result_(false) { | |
| 932 DCHECK(resolver.get()); | |
| 933 const bool kIsSlow = true; | |
| 934 base::WorkerPool::PostTaskAndReply( | |
| 935 FROM_HERE, | |
| 936 base::Bind(&LoopbackProbeJob::DoProbe, base::Unretained(this)), | |
| 937 base::Bind(&LoopbackProbeJob::OnProbeComplete, base::Owned(this)), | |
| 938 kIsSlow); | |
| 939 } | |
| 940 | |
| 941 virtual ~LoopbackProbeJob() {} | |
| 942 | |
| 943 private: | |
| 944 // Runs on worker thread. | |
| 945 void DoProbe() { | |
| 946 result_ = HaveOnlyLoopbackAddresses(); | |
| 947 } | |
| 948 | |
| 949 void OnProbeComplete() { | |
| 950 if (!resolver_.get()) | |
| 951 return; | |
| 952 resolver_->SetHaveOnlyLoopbackAddresses(result_); | |
| 953 } | |
| 954 | |
| 955 // Used/set only on origin thread. | |
| 956 base::WeakPtr<HostResolverImpl> resolver_; | |
| 957 | |
| 958 bool result_; | |
| 959 | |
| 960 DISALLOW_COPY_AND_ASSIGN(LoopbackProbeJob); | |
| 961 }; | |
| 962 | |
| 963 //----------------------------------------------------------------------------- | |
| 964 | |
| 965 // Resolves the hostname using DnsTransaction. | |
| 966 // TODO(szym): This could be moved to separate source file as well. | |
| 967 class HostResolverImpl::DnsTask : public base::SupportsWeakPtr<DnsTask> { | |
| 968 public: | |
| 969 class Delegate { | |
| 970 public: | |
| 971 virtual void OnDnsTaskComplete(base::TimeTicks start_time, | |
| 972 int net_error, | |
| 973 const AddressList& addr_list, | |
| 974 base::TimeDelta ttl) = 0; | |
| 975 | |
| 976 // Called when the first of two jobs succeeds. If the first completed | |
| 977 // transaction fails, this is not called. Also not called when the DnsTask | |
| 978 // only needs to run one transaction. | |
| 979 virtual void OnFirstDnsTransactionComplete() = 0; | |
| 980 | |
| 981 protected: | |
| 982 Delegate() {} | |
| 983 virtual ~Delegate() {} | |
| 984 }; | |
| 985 | |
| 986 DnsTask(DnsClient* client, | |
| 987 const Key& key, | |
| 988 Delegate* delegate, | |
| 989 const BoundNetLog& job_net_log) | |
| 990 : client_(client), | |
| 991 key_(key), | |
| 992 delegate_(delegate), | |
| 993 net_log_(job_net_log), | |
| 994 num_completed_transactions_(0), | |
| 995 task_start_time_(base::TimeTicks::Now()) { | |
| 996 DCHECK(client); | |
| 997 DCHECK(delegate_); | |
| 998 } | |
| 999 | |
| 1000 bool needs_two_transactions() const { | |
| 1001 return key_.address_family == ADDRESS_FAMILY_UNSPECIFIED; | |
| 1002 } | |
| 1003 | |
| 1004 bool needs_another_transaction() const { | |
| 1005 return needs_two_transactions() && !transaction_aaaa_; | |
| 1006 } | |
| 1007 | |
| 1008 void StartFirstTransaction() { | |
| 1009 DCHECK_EQ(0u, num_completed_transactions_); | |
| 1010 net_log_.BeginEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_DNS_TASK); | |
| 1011 if (key_.address_family == ADDRESS_FAMILY_IPV6) { | |
| 1012 StartAAAA(); | |
| 1013 } else { | |
| 1014 StartA(); | |
| 1015 } | |
| 1016 } | |
| 1017 | |
| 1018 void StartSecondTransaction() { | |
| 1019 DCHECK(needs_two_transactions()); | |
| 1020 StartAAAA(); | |
| 1021 } | |
| 1022 | |
| 1023 private: | |
| 1024 void StartA() { | |
| 1025 DCHECK(!transaction_a_); | |
| 1026 DCHECK_NE(ADDRESS_FAMILY_IPV6, key_.address_family); | |
| 1027 transaction_a_ = CreateTransaction(ADDRESS_FAMILY_IPV4); | |
| 1028 transaction_a_->Start(); | |
| 1029 } | |
| 1030 | |
| 1031 void StartAAAA() { | |
| 1032 DCHECK(!transaction_aaaa_); | |
| 1033 DCHECK_NE(ADDRESS_FAMILY_IPV4, key_.address_family); | |
| 1034 transaction_aaaa_ = CreateTransaction(ADDRESS_FAMILY_IPV6); | |
| 1035 transaction_aaaa_->Start(); | |
| 1036 } | |
| 1037 | |
| 1038 scoped_ptr<DnsTransaction> CreateTransaction(AddressFamily family) { | |
| 1039 DCHECK_NE(ADDRESS_FAMILY_UNSPECIFIED, family); | |
| 1040 return client_->GetTransactionFactory()->CreateTransaction( | |
| 1041 key_.hostname, | |
| 1042 family == ADDRESS_FAMILY_IPV6 ? dns_protocol::kTypeAAAA : | |
| 1043 dns_protocol::kTypeA, | |
| 1044 base::Bind(&DnsTask::OnTransactionComplete, base::Unretained(this), | |
| 1045 base::TimeTicks::Now()), | |
| 1046 net_log_); | |
| 1047 } | |
| 1048 | |
| 1049 void OnTransactionComplete(const base::TimeTicks& start_time, | |
| 1050 DnsTransaction* transaction, | |
| 1051 int net_error, | |
| 1052 const DnsResponse* response) { | |
| 1053 DCHECK(transaction); | |
| 1054 base::TimeDelta duration = base::TimeTicks::Now() - start_time; | |
| 1055 if (net_error != OK) { | |
| 1056 DNS_HISTOGRAM("AsyncDNS.TransactionFailure", duration); | |
| 1057 OnFailure(net_error, DnsResponse::DNS_PARSE_OK); | |
| 1058 return; | |
| 1059 } | |
| 1060 | |
| 1061 DNS_HISTOGRAM("AsyncDNS.TransactionSuccess", duration); | |
| 1062 switch (transaction->GetType()) { | |
| 1063 case dns_protocol::kTypeA: | |
| 1064 DNS_HISTOGRAM("AsyncDNS.TransactionSuccess_A", duration); | |
| 1065 break; | |
| 1066 case dns_protocol::kTypeAAAA: | |
| 1067 DNS_HISTOGRAM("AsyncDNS.TransactionSuccess_AAAA", duration); | |
| 1068 break; | |
| 1069 } | |
| 1070 | |
| 1071 AddressList addr_list; | |
| 1072 base::TimeDelta ttl; | |
| 1073 DnsResponse::Result result = response->ParseToAddressList(&addr_list, &ttl); | |
| 1074 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.ParseToAddressList", | |
| 1075 result, | |
| 1076 DnsResponse::DNS_PARSE_RESULT_MAX); | |
| 1077 if (result != DnsResponse::DNS_PARSE_OK) { | |
| 1078 // Fail even if the other query succeeds. | |
| 1079 OnFailure(ERR_DNS_MALFORMED_RESPONSE, result); | |
| 1080 return; | |
| 1081 } | |
| 1082 | |
| 1083 ++num_completed_transactions_; | |
| 1084 if (num_completed_transactions_ == 1) { | |
| 1085 ttl_ = ttl; | |
| 1086 } else { | |
| 1087 ttl_ = std::min(ttl_, ttl); | |
| 1088 } | |
| 1089 | |
| 1090 if (transaction->GetType() == dns_protocol::kTypeA) { | |
| 1091 DCHECK_EQ(transaction_a_.get(), transaction); | |
| 1092 // Place IPv4 addresses after IPv6. | |
| 1093 addr_list_.insert(addr_list_.end(), addr_list.begin(), addr_list.end()); | |
| 1094 } else { | |
| 1095 DCHECK_EQ(transaction_aaaa_.get(), transaction); | |
| 1096 // Place IPv6 addresses before IPv4. | |
| 1097 addr_list_.insert(addr_list_.begin(), addr_list.begin(), addr_list.end()); | |
| 1098 } | |
| 1099 | |
| 1100 if (needs_two_transactions() && num_completed_transactions_ == 1) { | |
| 1101 // No need to repeat the suffix search. | |
| 1102 key_.hostname = transaction->GetHostname(); | |
| 1103 delegate_->OnFirstDnsTransactionComplete(); | |
| 1104 return; | |
| 1105 } | |
| 1106 | |
| 1107 if (addr_list_.empty()) { | |
| 1108 // TODO(szym): Don't fallback to ProcTask in this case. | |
| 1109 OnFailure(ERR_NAME_NOT_RESOLVED, DnsResponse::DNS_PARSE_OK); | |
| 1110 return; | |
| 1111 } | |
| 1112 | |
| 1113 // If there are multiple addresses, and at least one is IPv6, need to sort | |
| 1114 // them. Note that IPv6 addresses are always put before IPv4 ones, so it's | |
| 1115 // sufficient to just check the family of the first address. | |
| 1116 if (addr_list_.size() > 1 && | |
| 1117 addr_list_[0].GetFamily() == ADDRESS_FAMILY_IPV6) { | |
| 1118 // Sort addresses if needed. Sort could complete synchronously. | |
| 1119 client_->GetAddressSorter()->Sort( | |
| 1120 addr_list_, | |
| 1121 base::Bind(&DnsTask::OnSortComplete, | |
| 1122 AsWeakPtr(), | |
| 1123 base::TimeTicks::Now())); | |
| 1124 } else { | |
| 1125 OnSuccess(addr_list_); | |
| 1126 } | |
| 1127 } | |
| 1128 | |
| 1129 void OnSortComplete(base::TimeTicks start_time, | |
| 1130 bool success, | |
| 1131 const AddressList& addr_list) { | |
| 1132 if (!success) { | |
| 1133 DNS_HISTOGRAM("AsyncDNS.SortFailure", | |
| 1134 base::TimeTicks::Now() - start_time); | |
| 1135 OnFailure(ERR_DNS_SORT_ERROR, DnsResponse::DNS_PARSE_OK); | |
| 1136 return; | |
| 1137 } | |
| 1138 | |
| 1139 DNS_HISTOGRAM("AsyncDNS.SortSuccess", | |
| 1140 base::TimeTicks::Now() - start_time); | |
| 1141 | |
| 1142 // AddressSorter prunes unusable destinations. | |
| 1143 if (addr_list.empty()) { | |
| 1144 LOG(WARNING) << "Address list empty after RFC3484 sort"; | |
| 1145 OnFailure(ERR_NAME_NOT_RESOLVED, DnsResponse::DNS_PARSE_OK); | |
| 1146 return; | |
| 1147 } | |
| 1148 | |
| 1149 OnSuccess(addr_list); | |
| 1150 } | |
| 1151 | |
| 1152 void OnFailure(int net_error, DnsResponse::Result result) { | |
| 1153 DCHECK_NE(OK, net_error); | |
| 1154 net_log_.EndEvent( | |
| 1155 NetLog::TYPE_HOST_RESOLVER_IMPL_DNS_TASK, | |
| 1156 base::Bind(&NetLogDnsTaskFailedCallback, net_error, result)); | |
| 1157 delegate_->OnDnsTaskComplete(task_start_time_, net_error, AddressList(), | |
| 1158 base::TimeDelta()); | |
| 1159 } | |
| 1160 | |
| 1161 void OnSuccess(const AddressList& addr_list) { | |
| 1162 net_log_.EndEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_DNS_TASK, | |
| 1163 addr_list.CreateNetLogCallback()); | |
| 1164 delegate_->OnDnsTaskComplete(task_start_time_, OK, addr_list, ttl_); | |
| 1165 } | |
| 1166 | |
| 1167 DnsClient* client_; | |
| 1168 Key key_; | |
| 1169 | |
| 1170 // The listener to the results of this DnsTask. | |
| 1171 Delegate* delegate_; | |
| 1172 const BoundNetLog net_log_; | |
| 1173 | |
| 1174 scoped_ptr<DnsTransaction> transaction_a_; | |
| 1175 scoped_ptr<DnsTransaction> transaction_aaaa_; | |
| 1176 | |
| 1177 unsigned num_completed_transactions_; | |
| 1178 | |
| 1179 // These are updated as each transaction completes. | |
| 1180 base::TimeDelta ttl_; | |
| 1181 // IPv6 addresses must appear first in the list. | |
| 1182 AddressList addr_list_; | |
| 1183 | |
| 1184 base::TimeTicks task_start_time_; | |
| 1185 | |
| 1186 DISALLOW_COPY_AND_ASSIGN(DnsTask); | |
| 1187 }; | |
| 1188 | |
| 1189 //----------------------------------------------------------------------------- | |
| 1190 | |
| 1191 // Aggregates all Requests for the same Key. Dispatched via PriorityDispatch. | |
| 1192 class HostResolverImpl::Job : public PrioritizedDispatcher::Job, | |
| 1193 public HostResolverImpl::DnsTask::Delegate { | |
| 1194 public: | |
| 1195 // Creates new job for |key| where |request_net_log| is bound to the | |
| 1196 // request that spawned it. | |
| 1197 Job(const base::WeakPtr<HostResolverImpl>& resolver, | |
| 1198 const Key& key, | |
| 1199 RequestPriority priority, | |
| 1200 const BoundNetLog& source_net_log) | |
| 1201 : resolver_(resolver), | |
| 1202 key_(key), | |
| 1203 priority_tracker_(priority), | |
| 1204 had_non_speculative_request_(false), | |
| 1205 had_dns_config_(false), | |
| 1206 num_occupied_job_slots_(0), | |
| 1207 dns_task_error_(OK), | |
| 1208 creation_time_(base::TimeTicks::Now()), | |
| 1209 priority_change_time_(creation_time_), | |
| 1210 net_log_(BoundNetLog::Make(source_net_log.net_log(), | |
| 1211 NetLog::SOURCE_HOST_RESOLVER_IMPL_JOB)) { | |
| 1212 source_net_log.AddEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_CREATE_JOB); | |
| 1213 | |
| 1214 net_log_.BeginEvent( | |
| 1215 NetLog::TYPE_HOST_RESOLVER_IMPL_JOB, | |
| 1216 base::Bind(&NetLogJobCreationCallback, | |
| 1217 source_net_log.source(), | |
| 1218 &key_.hostname)); | |
| 1219 } | |
| 1220 | |
| 1221 ~Job() override { | |
| 1222 if (is_running()) { | |
| 1223 // |resolver_| was destroyed with this Job still in flight. | |
| 1224 // Clean-up, record in the log, but don't run any callbacks. | |
| 1225 if (is_proc_running()) { | |
| 1226 proc_task_->Cancel(); | |
| 1227 proc_task_ = NULL; | |
| 1228 } | |
| 1229 // Clean up now for nice NetLog. | |
| 1230 KillDnsTask(); | |
| 1231 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HOST_RESOLVER_IMPL_JOB, | |
| 1232 ERR_ABORTED); | |
| 1233 } else if (is_queued()) { | |
| 1234 // |resolver_| was destroyed without running this Job. | |
| 1235 // TODO(szym): is there any benefit in having this distinction? | |
| 1236 net_log_.AddEvent(NetLog::TYPE_CANCELLED); | |
| 1237 net_log_.EndEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_JOB); | |
| 1238 } | |
| 1239 // else CompleteRequests logged EndEvent. | |
| 1240 | |
| 1241 // Log any remaining Requests as cancelled. | |
| 1242 for (RequestsList::const_iterator it = requests_.begin(); | |
| 1243 it != requests_.end(); ++it) { | |
| 1244 Request* req = *it; | |
| 1245 if (req->was_canceled()) | |
| 1246 continue; | |
| 1247 DCHECK_EQ(this, req->job()); | |
| 1248 LogCancelRequest(req->source_net_log(), req->info()); | |
| 1249 } | |
| 1250 } | |
| 1251 | |
| 1252 // Add this job to the dispatcher. If "at_head" is true, adds at the front | |
| 1253 // of the queue. | |
| 1254 void Schedule(bool at_head) { | |
| 1255 DCHECK(!is_queued()); | |
| 1256 PrioritizedDispatcher::Handle handle; | |
| 1257 if (!at_head) { | |
| 1258 handle = resolver_->dispatcher_->Add(this, priority()); | |
| 1259 } else { | |
| 1260 handle = resolver_->dispatcher_->AddAtHead(this, priority()); | |
| 1261 } | |
| 1262 // The dispatcher could have started |this| in the above call to Add, which | |
| 1263 // could have called Schedule again. In that case |handle| will be null, | |
| 1264 // but |handle_| may have been set by the other nested call to Schedule. | |
| 1265 if (!handle.is_null()) { | |
| 1266 DCHECK(handle_.is_null()); | |
| 1267 handle_ = handle; | |
| 1268 } | |
| 1269 } | |
| 1270 | |
| 1271 void AddRequest(scoped_ptr<Request> req) { | |
| 1272 DCHECK_EQ(key_.hostname, req->info().hostname()); | |
| 1273 | |
| 1274 req->set_job(this); | |
| 1275 priority_tracker_.Add(req->priority()); | |
| 1276 | |
| 1277 req->source_net_log().AddEvent( | |
| 1278 NetLog::TYPE_HOST_RESOLVER_IMPL_JOB_ATTACH, | |
| 1279 net_log_.source().ToEventParametersCallback()); | |
| 1280 | |
| 1281 net_log_.AddEvent( | |
| 1282 NetLog::TYPE_HOST_RESOLVER_IMPL_JOB_REQUEST_ATTACH, | |
| 1283 base::Bind(&NetLogJobAttachCallback, | |
| 1284 req->source_net_log().source(), | |
| 1285 priority())); | |
| 1286 | |
| 1287 // TODO(szym): Check if this is still needed. | |
| 1288 if (!req->info().is_speculative()) { | |
| 1289 had_non_speculative_request_ = true; | |
| 1290 if (proc_task_.get()) | |
| 1291 proc_task_->set_had_non_speculative_request(); | |
| 1292 } | |
| 1293 | |
| 1294 requests_.push_back(req.release()); | |
| 1295 | |
| 1296 UpdatePriority(); | |
| 1297 } | |
| 1298 | |
| 1299 // Marks |req| as cancelled. If it was the last active Request, also finishes | |
| 1300 // this Job, marking it as cancelled, and deletes it. | |
| 1301 void CancelRequest(Request* req) { | |
| 1302 DCHECK_EQ(key_.hostname, req->info().hostname()); | |
| 1303 DCHECK(!req->was_canceled()); | |
| 1304 | |
| 1305 // Don't remove it from |requests_| just mark it canceled. | |
| 1306 req->MarkAsCanceled(); | |
| 1307 LogCancelRequest(req->source_net_log(), req->info()); | |
| 1308 | |
| 1309 priority_tracker_.Remove(req->priority()); | |
| 1310 net_log_.AddEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_JOB_REQUEST_DETACH, | |
| 1311 base::Bind(&NetLogJobAttachCallback, | |
| 1312 req->source_net_log().source(), | |
| 1313 priority())); | |
| 1314 | |
| 1315 if (num_active_requests() > 0) { | |
| 1316 UpdatePriority(); | |
| 1317 } else { | |
| 1318 // If we were called from a Request's callback within CompleteRequests, | |
| 1319 // that Request could not have been cancelled, so num_active_requests() | |
| 1320 // could not be 0. Therefore, we are not in CompleteRequests(). | |
| 1321 CompleteRequestsWithError(OK /* cancelled */); | |
| 1322 } | |
| 1323 } | |
| 1324 | |
| 1325 // Called from AbortAllInProgressJobs. Completes all requests and destroys | |
| 1326 // the job. This currently assumes the abort is due to a network change. | |
| 1327 void Abort() { | |
| 1328 DCHECK(is_running()); | |
| 1329 CompleteRequestsWithError(ERR_NETWORK_CHANGED); | |
| 1330 } | |
| 1331 | |
| 1332 // If DnsTask present, abort it and fall back to ProcTask. | |
| 1333 void AbortDnsTask() { | |
| 1334 if (dns_task_) { | |
| 1335 KillDnsTask(); | |
| 1336 dns_task_error_ = OK; | |
| 1337 StartProcTask(); | |
| 1338 } | |
| 1339 } | |
| 1340 | |
| 1341 // Called by HostResolverImpl when this job is evicted due to queue overflow. | |
| 1342 // Completes all requests and destroys the job. | |
| 1343 void OnEvicted() { | |
| 1344 DCHECK(!is_running()); | |
| 1345 DCHECK(is_queued()); | |
| 1346 handle_.Reset(); | |
| 1347 | |
| 1348 net_log_.AddEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_JOB_EVICTED); | |
| 1349 | |
| 1350 // This signals to CompleteRequests that this job never ran. | |
| 1351 CompleteRequestsWithError(ERR_HOST_RESOLVER_QUEUE_TOO_LARGE); | |
| 1352 } | |
| 1353 | |
| 1354 // Attempts to serve the job from HOSTS. Returns true if succeeded and | |
| 1355 // this Job was destroyed. | |
| 1356 bool ServeFromHosts() { | |
| 1357 DCHECK_GT(num_active_requests(), 0u); | |
| 1358 AddressList addr_list; | |
| 1359 if (resolver_->ServeFromHosts(key(), | |
| 1360 requests_.front()->info(), | |
| 1361 &addr_list)) { | |
| 1362 // This will destroy the Job. | |
| 1363 CompleteRequests( | |
| 1364 HostCache::Entry(OK, MakeAddressListForRequest(addr_list)), | |
| 1365 base::TimeDelta()); | |
| 1366 return true; | |
| 1367 } | |
| 1368 return false; | |
| 1369 } | |
| 1370 | |
| 1371 const Key key() const { | |
| 1372 return key_; | |
| 1373 } | |
| 1374 | |
| 1375 bool is_queued() const { | |
| 1376 return !handle_.is_null(); | |
| 1377 } | |
| 1378 | |
| 1379 bool is_running() const { | |
| 1380 return is_dns_running() || is_proc_running(); | |
| 1381 } | |
| 1382 | |
| 1383 private: | |
| 1384 void KillDnsTask() { | |
| 1385 if (dns_task_) { | |
| 1386 ReduceToOneJobSlot(); | |
| 1387 dns_task_.reset(); | |
| 1388 } | |
| 1389 } | |
| 1390 | |
| 1391 // Reduce the number of job slots occupied and queued in the dispatcher | |
| 1392 // to one. If the second Job slot is queued in the dispatcher, cancels the | |
| 1393 // queued job. Otherwise, the second Job has been started by the | |
| 1394 // PrioritizedDispatcher, so signals it is complete. | |
| 1395 void ReduceToOneJobSlot() { | |
| 1396 DCHECK_GE(num_occupied_job_slots_, 1u); | |
| 1397 if (is_queued()) { | |
| 1398 resolver_->dispatcher_->Cancel(handle_); | |
| 1399 handle_.Reset(); | |
| 1400 } else if (num_occupied_job_slots_ > 1) { | |
| 1401 resolver_->dispatcher_->OnJobFinished(); | |
| 1402 --num_occupied_job_slots_; | |
| 1403 } | |
| 1404 DCHECK_EQ(1u, num_occupied_job_slots_); | |
| 1405 } | |
| 1406 | |
| 1407 void UpdatePriority() { | |
| 1408 if (is_queued()) { | |
| 1409 if (priority() != static_cast<RequestPriority>(handle_.priority())) | |
| 1410 priority_change_time_ = base::TimeTicks::Now(); | |
| 1411 handle_ = resolver_->dispatcher_->ChangePriority(handle_, priority()); | |
| 1412 } | |
| 1413 } | |
| 1414 | |
| 1415 AddressList MakeAddressListForRequest(const AddressList& list) const { | |
| 1416 if (requests_.empty()) | |
| 1417 return list; | |
| 1418 return AddressList::CopyWithPort(list, requests_.front()->info().port()); | |
| 1419 } | |
| 1420 | |
| 1421 // PriorityDispatch::Job: | |
| 1422 void Start() override { | |
| 1423 DCHECK_LE(num_occupied_job_slots_, 1u); | |
| 1424 | |
| 1425 handle_.Reset(); | |
| 1426 ++num_occupied_job_slots_; | |
| 1427 | |
| 1428 if (num_occupied_job_slots_ == 2) { | |
| 1429 StartSecondDnsTransaction(); | |
| 1430 return; | |
| 1431 } | |
| 1432 | |
| 1433 DCHECK(!is_running()); | |
| 1434 | |
| 1435 net_log_.AddEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_JOB_STARTED); | |
| 1436 | |
| 1437 had_dns_config_ = resolver_->HaveDnsConfig(); | |
| 1438 | |
| 1439 base::TimeTicks now = base::TimeTicks::Now(); | |
| 1440 base::TimeDelta queue_time = now - creation_time_; | |
| 1441 base::TimeDelta queue_time_after_change = now - priority_change_time_; | |
| 1442 | |
| 1443 if (had_dns_config_) { | |
| 1444 DNS_HISTOGRAM_BY_PRIORITY("AsyncDNS.JobQueueTime", priority(), | |
| 1445 queue_time); | |
| 1446 DNS_HISTOGRAM_BY_PRIORITY("AsyncDNS.JobQueueTimeAfterChange", priority(), | |
| 1447 queue_time_after_change); | |
| 1448 } else { | |
| 1449 DNS_HISTOGRAM_BY_PRIORITY("DNS.JobQueueTime", priority(), queue_time); | |
| 1450 DNS_HISTOGRAM_BY_PRIORITY("DNS.JobQueueTimeAfterChange", priority(), | |
| 1451 queue_time_after_change); | |
| 1452 } | |
| 1453 | |
| 1454 bool system_only = | |
| 1455 (key_.host_resolver_flags & HOST_RESOLVER_SYSTEM_ONLY) != 0; | |
| 1456 | |
| 1457 // Caution: Job::Start must not complete synchronously. | |
| 1458 if (!system_only && had_dns_config_ && | |
| 1459 !ResemblesMulticastDNSName(key_.hostname)) { | |
| 1460 StartDnsTask(); | |
| 1461 } else { | |
| 1462 StartProcTask(); | |
| 1463 } | |
| 1464 } | |
| 1465 | |
| 1466 // TODO(szym): Since DnsTransaction does not consume threads, we can increase | |
| 1467 // the limits on |dispatcher_|. But in order to keep the number of WorkerPool | |
| 1468 // threads low, we will need to use an "inner" PrioritizedDispatcher with | |
| 1469 // tighter limits. | |
| 1470 void StartProcTask() { | |
| 1471 DCHECK(!is_dns_running()); | |
| 1472 proc_task_ = new ProcTask( | |
| 1473 key_, | |
| 1474 resolver_->proc_params_, | |
| 1475 base::Bind(&Job::OnProcTaskComplete, base::Unretained(this), | |
| 1476 base::TimeTicks::Now()), | |
| 1477 net_log_); | |
| 1478 | |
| 1479 if (had_non_speculative_request_) | |
| 1480 proc_task_->set_had_non_speculative_request(); | |
| 1481 // Start() could be called from within Resolve(), hence it must NOT directly | |
| 1482 // call OnProcTaskComplete, for example, on synchronous failure. | |
| 1483 proc_task_->Start(); | |
| 1484 } | |
| 1485 | |
| 1486 // Called by ProcTask when it completes. | |
| 1487 void OnProcTaskComplete(base::TimeTicks start_time, | |
| 1488 int net_error, | |
| 1489 const AddressList& addr_list) { | |
| 1490 // TODO(vadimt): Remove ScopedTracker below once crbug.com/436634 is fixed. | |
| 1491 tracked_objects::ScopedTracker tracking_profile( | |
| 1492 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 1493 "436634 HostResolverImpl::Job::OnProcTaskComplete")); | |
| 1494 | |
| 1495 DCHECK(is_proc_running()); | |
| 1496 | |
| 1497 if (!resolver_->resolved_known_ipv6_hostname_ && | |
| 1498 net_error == OK && | |
| 1499 key_.address_family == ADDRESS_FAMILY_UNSPECIFIED) { | |
| 1500 if (key_.hostname == "www.google.com") { | |
| 1501 resolver_->resolved_known_ipv6_hostname_ = true; | |
| 1502 bool got_ipv6_address = false; | |
| 1503 for (size_t i = 0; i < addr_list.size(); ++i) { | |
| 1504 if (addr_list[i].GetFamily() == ADDRESS_FAMILY_IPV6) { | |
| 1505 got_ipv6_address = true; | |
| 1506 break; | |
| 1507 } | |
| 1508 } | |
| 1509 UMA_HISTOGRAM_BOOLEAN("Net.UnspecResolvedIPv6", got_ipv6_address); | |
| 1510 } | |
| 1511 } | |
| 1512 | |
| 1513 if (dns_task_error_ != OK) { | |
| 1514 base::TimeDelta duration = base::TimeTicks::Now() - start_time; | |
| 1515 if (net_error == OK) { | |
| 1516 DNS_HISTOGRAM("AsyncDNS.FallbackSuccess", duration); | |
| 1517 if ((dns_task_error_ == ERR_NAME_NOT_RESOLVED) && | |
| 1518 ResemblesNetBIOSName(key_.hostname)) { | |
| 1519 UmaAsyncDnsResolveStatus(RESOLVE_STATUS_SUSPECT_NETBIOS); | |
| 1520 } else { | |
| 1521 UmaAsyncDnsResolveStatus(RESOLVE_STATUS_PROC_SUCCESS); | |
| 1522 } | |
| 1523 UMA_HISTOGRAM_CUSTOM_ENUMERATION("AsyncDNS.ResolveError", | |
| 1524 std::abs(dns_task_error_), | |
| 1525 GetAllErrorCodesForUma()); | |
| 1526 resolver_->OnDnsTaskResolve(dns_task_error_); | |
| 1527 } else { | |
| 1528 DNS_HISTOGRAM("AsyncDNS.FallbackFail", duration); | |
| 1529 UmaAsyncDnsResolveStatus(RESOLVE_STATUS_FAIL); | |
| 1530 } | |
| 1531 } | |
| 1532 | |
| 1533 base::TimeDelta ttl = | |
| 1534 base::TimeDelta::FromSeconds(kNegativeCacheEntryTTLSeconds); | |
| 1535 if (net_error == OK) | |
| 1536 ttl = base::TimeDelta::FromSeconds(kCacheEntryTTLSeconds); | |
| 1537 | |
| 1538 // Don't store the |ttl| in cache since it's not obtained from the server. | |
| 1539 CompleteRequests( | |
| 1540 HostCache::Entry(net_error, MakeAddressListForRequest(addr_list)), | |
| 1541 ttl); | |
| 1542 } | |
| 1543 | |
| 1544 void StartDnsTask() { | |
| 1545 DCHECK(resolver_->HaveDnsConfig()); | |
| 1546 dns_task_.reset(new DnsTask(resolver_->dns_client_.get(), key_, this, | |
| 1547 net_log_)); | |
| 1548 | |
| 1549 dns_task_->StartFirstTransaction(); | |
| 1550 // Schedule a second transaction, if needed. | |
| 1551 if (dns_task_->needs_two_transactions()) | |
| 1552 Schedule(true); | |
| 1553 } | |
| 1554 | |
| 1555 void StartSecondDnsTransaction() { | |
| 1556 DCHECK(dns_task_->needs_two_transactions()); | |
| 1557 dns_task_->StartSecondTransaction(); | |
| 1558 } | |
| 1559 | |
| 1560 // Called if DnsTask fails. It is posted from StartDnsTask, so Job may be | |
| 1561 // deleted before this callback. In this case dns_task is deleted as well, | |
| 1562 // so we use it as indicator whether Job is still valid. | |
| 1563 void OnDnsTaskFailure(const base::WeakPtr<DnsTask>& dns_task, | |
| 1564 base::TimeDelta duration, | |
| 1565 int net_error) { | |
| 1566 DNS_HISTOGRAM("AsyncDNS.ResolveFail", duration); | |
| 1567 | |
| 1568 if (dns_task == NULL) | |
| 1569 return; | |
| 1570 | |
| 1571 dns_task_error_ = net_error; | |
| 1572 | |
| 1573 // TODO(szym): Run ServeFromHosts now if nsswitch.conf says so. | |
| 1574 // http://crbug.com/117655 | |
| 1575 | |
| 1576 // TODO(szym): Some net errors indicate lack of connectivity. Starting | |
| 1577 // ProcTask in that case is a waste of time. | |
| 1578 if (resolver_->fallback_to_proctask_) { | |
| 1579 KillDnsTask(); | |
| 1580 StartProcTask(); | |
| 1581 } else { | |
| 1582 UmaAsyncDnsResolveStatus(RESOLVE_STATUS_FAIL); | |
| 1583 CompleteRequestsWithError(net_error); | |
| 1584 } | |
| 1585 } | |
| 1586 | |
| 1587 | |
| 1588 // HostResolverImpl::DnsTask::Delegate implementation: | |
| 1589 | |
| 1590 void OnDnsTaskComplete(base::TimeTicks start_time, | |
| 1591 int net_error, | |
| 1592 const AddressList& addr_list, | |
| 1593 base::TimeDelta ttl) override { | |
| 1594 DCHECK(is_dns_running()); | |
| 1595 | |
| 1596 base::TimeDelta duration = base::TimeTicks::Now() - start_time; | |
| 1597 if (net_error != OK) { | |
| 1598 OnDnsTaskFailure(dns_task_->AsWeakPtr(), duration, net_error); | |
| 1599 return; | |
| 1600 } | |
| 1601 DNS_HISTOGRAM("AsyncDNS.ResolveSuccess", duration); | |
| 1602 // Log DNS lookups based on |address_family|. | |
| 1603 switch(key_.address_family) { | |
| 1604 case ADDRESS_FAMILY_IPV4: | |
| 1605 DNS_HISTOGRAM("AsyncDNS.ResolveSuccess_FAMILY_IPV4", duration); | |
| 1606 break; | |
| 1607 case ADDRESS_FAMILY_IPV6: | |
| 1608 DNS_HISTOGRAM("AsyncDNS.ResolveSuccess_FAMILY_IPV6", duration); | |
| 1609 break; | |
| 1610 case ADDRESS_FAMILY_UNSPECIFIED: | |
| 1611 DNS_HISTOGRAM("AsyncDNS.ResolveSuccess_FAMILY_UNSPEC", duration); | |
| 1612 break; | |
| 1613 } | |
| 1614 | |
| 1615 UmaAsyncDnsResolveStatus(RESOLVE_STATUS_DNS_SUCCESS); | |
| 1616 RecordTTL(ttl); | |
| 1617 | |
| 1618 resolver_->OnDnsTaskResolve(OK); | |
| 1619 | |
| 1620 base::TimeDelta bounded_ttl = | |
| 1621 std::max(ttl, base::TimeDelta::FromSeconds(kMinimumTTLSeconds)); | |
| 1622 | |
| 1623 CompleteRequests( | |
| 1624 HostCache::Entry(net_error, MakeAddressListForRequest(addr_list), ttl), | |
| 1625 bounded_ttl); | |
| 1626 } | |
| 1627 | |
| 1628 void OnFirstDnsTransactionComplete() override { | |
| 1629 DCHECK(dns_task_->needs_two_transactions()); | |
| 1630 DCHECK_EQ(dns_task_->needs_another_transaction(), is_queued()); | |
| 1631 // No longer need to occupy two dispatcher slots. | |
| 1632 ReduceToOneJobSlot(); | |
| 1633 | |
| 1634 // We already have a job slot at the dispatcher, so if the second | |
| 1635 // transaction hasn't started, reuse it now instead of waiting in the queue | |
| 1636 // for the second slot. | |
| 1637 if (dns_task_->needs_another_transaction()) | |
| 1638 dns_task_->StartSecondTransaction(); | |
| 1639 } | |
| 1640 | |
| 1641 // Performs Job's last rites. Completes all Requests. Deletes this. | |
| 1642 void CompleteRequests(const HostCache::Entry& entry, | |
| 1643 base::TimeDelta ttl) { | |
| 1644 // TODO(vadimt): Remove ScopedTracker below once crbug.com/436634 is fixed. | |
| 1645 tracked_objects::ScopedTracker tracking_profile1( | |
| 1646 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 1647 "436634 HostResolverImpl::Job::CompleteRequests1")); | |
| 1648 | |
| 1649 CHECK(resolver_.get()); | |
| 1650 | |
| 1651 // This job must be removed from resolver's |jobs_| now to make room for a | |
| 1652 // new job with the same key in case one of the OnComplete callbacks decides | |
| 1653 // to spawn one. Consequently, the job deletes itself when CompleteRequests | |
| 1654 // is done. | |
| 1655 scoped_ptr<Job> self_deleter(this); | |
| 1656 | |
| 1657 resolver_->RemoveJob(this); | |
| 1658 | |
| 1659 if (is_running()) { | |
| 1660 if (is_proc_running()) { | |
| 1661 DCHECK(!is_queued()); | |
| 1662 proc_task_->Cancel(); | |
| 1663 proc_task_ = NULL; | |
| 1664 } | |
| 1665 KillDnsTask(); | |
| 1666 | |
| 1667 // Signal dispatcher that a slot has opened. | |
| 1668 resolver_->dispatcher_->OnJobFinished(); | |
| 1669 } else if (is_queued()) { | |
| 1670 resolver_->dispatcher_->Cancel(handle_); | |
| 1671 handle_.Reset(); | |
| 1672 } | |
| 1673 | |
| 1674 if (num_active_requests() == 0) { | |
| 1675 net_log_.AddEvent(NetLog::TYPE_CANCELLED); | |
| 1676 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HOST_RESOLVER_IMPL_JOB, | |
| 1677 OK); | |
| 1678 return; | |
| 1679 } | |
| 1680 | |
| 1681 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HOST_RESOLVER_IMPL_JOB, | |
| 1682 entry.error); | |
| 1683 | |
| 1684 DCHECK(!requests_.empty()); | |
| 1685 | |
| 1686 if (entry.error == OK) { | |
| 1687 // Record this histogram here, when we know the system has a valid DNS | |
| 1688 // configuration. | |
| 1689 UMA_HISTOGRAM_BOOLEAN("AsyncDNS.HaveDnsConfig", | |
| 1690 resolver_->received_dns_config_); | |
| 1691 } | |
| 1692 | |
| 1693 bool did_complete = (entry.error != ERR_NETWORK_CHANGED) && | |
| 1694 (entry.error != ERR_HOST_RESOLVER_QUEUE_TOO_LARGE); | |
| 1695 if (did_complete) | |
| 1696 resolver_->CacheResult(key_, entry, ttl); | |
| 1697 | |
| 1698 // TODO(vadimt): Remove ScopedTracker below once crbug.com/436634 is fixed. | |
| 1699 tracked_objects::ScopedTracker tracking_profile2( | |
| 1700 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 1701 "436634 HostResolverImpl::Job::CompleteRequests2")); | |
| 1702 | |
| 1703 // Complete all of the requests that were attached to the job. | |
| 1704 for (RequestsList::const_iterator it = requests_.begin(); | |
| 1705 it != requests_.end(); ++it) { | |
| 1706 Request* req = *it; | |
| 1707 | |
| 1708 if (req->was_canceled()) | |
| 1709 continue; | |
| 1710 | |
| 1711 DCHECK_EQ(this, req->job()); | |
| 1712 // Update the net log and notify registered observers. | |
| 1713 LogFinishRequest(req->source_net_log(), req->info(), entry.error); | |
| 1714 if (did_complete) { | |
| 1715 // Record effective total time from creation to completion. | |
| 1716 RecordTotalTime(had_dns_config_, req->info().is_speculative(), | |
| 1717 base::TimeTicks::Now() - req->request_time()); | |
| 1718 } | |
| 1719 req->OnComplete(entry.error, entry.addrlist); | |
| 1720 | |
| 1721 // Check if the resolver was destroyed as a result of running the | |
| 1722 // callback. If it was, we could continue, but we choose to bail. | |
| 1723 if (!resolver_.get()) | |
| 1724 return; | |
| 1725 } | |
| 1726 } | |
| 1727 | |
| 1728 // Convenience wrapper for CompleteRequests in case of failure. | |
| 1729 void CompleteRequestsWithError(int net_error) { | |
| 1730 CompleteRequests(HostCache::Entry(net_error, AddressList()), | |
| 1731 base::TimeDelta()); | |
| 1732 } | |
| 1733 | |
| 1734 RequestPriority priority() const { | |
| 1735 return priority_tracker_.highest_priority(); | |
| 1736 } | |
| 1737 | |
| 1738 // Number of non-canceled requests in |requests_|. | |
| 1739 size_t num_active_requests() const { | |
| 1740 return priority_tracker_.total_count(); | |
| 1741 } | |
| 1742 | |
| 1743 bool is_dns_running() const { | |
| 1744 return dns_task_.get() != NULL; | |
| 1745 } | |
| 1746 | |
| 1747 bool is_proc_running() const { | |
| 1748 return proc_task_.get() != NULL; | |
| 1749 } | |
| 1750 | |
| 1751 base::WeakPtr<HostResolverImpl> resolver_; | |
| 1752 | |
| 1753 Key key_; | |
| 1754 | |
| 1755 // Tracks the highest priority across |requests_|. | |
| 1756 PriorityTracker priority_tracker_; | |
| 1757 | |
| 1758 bool had_non_speculative_request_; | |
| 1759 | |
| 1760 // Distinguishes measurements taken while DnsClient was fully configured. | |
| 1761 bool had_dns_config_; | |
| 1762 | |
| 1763 // Number of slots occupied by this Job in resolver's PrioritizedDispatcher. | |
| 1764 unsigned num_occupied_job_slots_; | |
| 1765 | |
| 1766 // Result of DnsTask. | |
| 1767 int dns_task_error_; | |
| 1768 | |
| 1769 const base::TimeTicks creation_time_; | |
| 1770 base::TimeTicks priority_change_time_; | |
| 1771 | |
| 1772 BoundNetLog net_log_; | |
| 1773 | |
| 1774 // Resolves the host using a HostResolverProc. | |
| 1775 scoped_refptr<ProcTask> proc_task_; | |
| 1776 | |
| 1777 // Resolves the host using a DnsTransaction. | |
| 1778 scoped_ptr<DnsTask> dns_task_; | |
| 1779 | |
| 1780 // All Requests waiting for the result of this Job. Some can be canceled. | |
| 1781 RequestsList requests_; | |
| 1782 | |
| 1783 // A handle used in |HostResolverImpl::dispatcher_|. | |
| 1784 PrioritizedDispatcher::Handle handle_; | |
| 1785 }; | |
| 1786 | |
| 1787 //----------------------------------------------------------------------------- | |
| 1788 | |
| 1789 HostResolverImpl::ProcTaskParams::ProcTaskParams( | |
| 1790 HostResolverProc* resolver_proc, | |
| 1791 size_t max_retry_attempts) | |
| 1792 : resolver_proc(resolver_proc), | |
| 1793 max_retry_attempts(max_retry_attempts), | |
| 1794 unresponsive_delay(base::TimeDelta::FromMilliseconds(6000)), | |
| 1795 retry_factor(2) { | |
| 1796 // Maximum of 4 retry attempts for host resolution. | |
| 1797 static const size_t kDefaultMaxRetryAttempts = 4u; | |
| 1798 if (max_retry_attempts == HostResolver::kDefaultRetryAttempts) | |
| 1799 max_retry_attempts = kDefaultMaxRetryAttempts; | |
| 1800 } | |
| 1801 | |
| 1802 HostResolverImpl::ProcTaskParams::~ProcTaskParams() {} | |
| 1803 | |
| 1804 HostResolverImpl::HostResolverImpl(const Options& options, NetLog* net_log) | |
| 1805 : max_queued_jobs_(0), | |
| 1806 proc_params_(NULL, options.max_retry_attempts), | |
| 1807 net_log_(net_log), | |
| 1808 default_address_family_(ADDRESS_FAMILY_UNSPECIFIED), | |
| 1809 received_dns_config_(false), | |
| 1810 num_dns_failures_(0), | |
| 1811 probe_ipv6_support_(true), | |
| 1812 use_local_ipv6_(false), | |
| 1813 resolved_known_ipv6_hostname_(false), | |
| 1814 additional_resolver_flags_(0), | |
| 1815 fallback_to_proctask_(true), | |
| 1816 weak_ptr_factory_(this), | |
| 1817 probe_weak_ptr_factory_(this) { | |
| 1818 if (options.enable_caching) | |
| 1819 cache_ = HostCache::CreateDefaultCache(); | |
| 1820 | |
| 1821 PrioritizedDispatcher::Limits job_limits = options.GetDispatcherLimits(); | |
| 1822 dispatcher_.reset(new PrioritizedDispatcher(job_limits)); | |
| 1823 max_queued_jobs_ = job_limits.total_jobs * 100u; | |
| 1824 | |
| 1825 DCHECK_GE(dispatcher_->num_priorities(), static_cast<size_t>(NUM_PRIORITIES)); | |
| 1826 | |
| 1827 #if defined(OS_WIN) | |
| 1828 EnsureWinsockInit(); | |
| 1829 #endif | |
| 1830 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) | |
| 1831 new LoopbackProbeJob(weak_ptr_factory_.GetWeakPtr()); | |
| 1832 #endif | |
| 1833 NetworkChangeNotifier::AddIPAddressObserver(this); | |
| 1834 NetworkChangeNotifier::AddDNSObserver(this); | |
| 1835 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_OPENBSD) && \ | |
| 1836 !defined(OS_ANDROID) | |
| 1837 EnsureDnsReloaderInit(); | |
| 1838 #endif | |
| 1839 | |
| 1840 { | |
| 1841 DnsConfig dns_config; | |
| 1842 NetworkChangeNotifier::GetDnsConfig(&dns_config); | |
| 1843 received_dns_config_ = dns_config.IsValid(); | |
| 1844 // Conservatively assume local IPv6 is needed when DnsConfig is not valid. | |
| 1845 use_local_ipv6_ = !dns_config.IsValid() || dns_config.use_local_ipv6; | |
| 1846 } | |
| 1847 | |
| 1848 fallback_to_proctask_ = !ConfigureAsyncDnsNoFallbackFieldTrial(); | |
| 1849 } | |
| 1850 | |
| 1851 HostResolverImpl::~HostResolverImpl() { | |
| 1852 // Prevent the dispatcher from starting new jobs. | |
| 1853 dispatcher_->SetLimitsToZero(); | |
| 1854 // It's now safe for Jobs to call KillDsnTask on destruction, because | |
| 1855 // OnJobComplete will not start any new jobs. | |
| 1856 STLDeleteValues(&jobs_); | |
| 1857 | |
| 1858 NetworkChangeNotifier::RemoveIPAddressObserver(this); | |
| 1859 NetworkChangeNotifier::RemoveDNSObserver(this); | |
| 1860 } | |
| 1861 | |
| 1862 void HostResolverImpl::SetMaxQueuedJobs(size_t value) { | |
| 1863 DCHECK_EQ(0u, dispatcher_->num_queued_jobs()); | |
| 1864 DCHECK_GT(value, 0u); | |
| 1865 max_queued_jobs_ = value; | |
| 1866 } | |
| 1867 | |
| 1868 int HostResolverImpl::Resolve(const RequestInfo& info, | |
| 1869 RequestPriority priority, | |
| 1870 AddressList* addresses, | |
| 1871 const CompletionCallback& callback, | |
| 1872 RequestHandle* out_req, | |
| 1873 const BoundNetLog& source_net_log) { | |
| 1874 DCHECK(addresses); | |
| 1875 DCHECK(CalledOnValidThread()); | |
| 1876 DCHECK_EQ(false, callback.is_null()); | |
| 1877 | |
| 1878 // Check that the caller supplied a valid hostname to resolve. | |
| 1879 std::string labeled_hostname; | |
| 1880 if (!DNSDomainFromDot(info.hostname(), &labeled_hostname)) | |
| 1881 return ERR_NAME_NOT_RESOLVED; | |
| 1882 | |
| 1883 LogStartRequest(source_net_log, info); | |
| 1884 | |
| 1885 // Build a key that identifies the request in the cache and in the | |
| 1886 // outstanding jobs map. | |
| 1887 Key key = GetEffectiveKeyForRequest(info, source_net_log); | |
| 1888 | |
| 1889 int rv = ResolveHelper(key, info, addresses, source_net_log); | |
| 1890 if (rv != ERR_DNS_CACHE_MISS) { | |
| 1891 LogFinishRequest(source_net_log, info, rv); | |
| 1892 RecordTotalTime(HaveDnsConfig(), info.is_speculative(), base::TimeDelta()); | |
| 1893 return rv; | |
| 1894 } | |
| 1895 | |
| 1896 // Next we need to attach our request to a "job". This job is responsible for | |
| 1897 // calling "getaddrinfo(hostname)" on a worker thread. | |
| 1898 | |
| 1899 JobMap::iterator jobit = jobs_.find(key); | |
| 1900 Job* job; | |
| 1901 if (jobit == jobs_.end()) { | |
| 1902 job = | |
| 1903 new Job(weak_ptr_factory_.GetWeakPtr(), key, priority, source_net_log); | |
| 1904 job->Schedule(false); | |
| 1905 | |
| 1906 // Check for queue overflow. | |
| 1907 if (dispatcher_->num_queued_jobs() > max_queued_jobs_) { | |
| 1908 Job* evicted = static_cast<Job*>(dispatcher_->EvictOldestLowest()); | |
| 1909 DCHECK(evicted); | |
| 1910 evicted->OnEvicted(); // Deletes |evicted|. | |
| 1911 if (evicted == job) { | |
| 1912 rv = ERR_HOST_RESOLVER_QUEUE_TOO_LARGE; | |
| 1913 LogFinishRequest(source_net_log, info, rv); | |
| 1914 return rv; | |
| 1915 } | |
| 1916 } | |
| 1917 jobs_.insert(jobit, std::make_pair(key, job)); | |
| 1918 } else { | |
| 1919 job = jobit->second; | |
| 1920 } | |
| 1921 | |
| 1922 // Can't complete synchronously. Create and attach request. | |
| 1923 scoped_ptr<Request> req(new Request( | |
| 1924 source_net_log, info, priority, callback, addresses)); | |
| 1925 if (out_req) | |
| 1926 *out_req = reinterpret_cast<RequestHandle>(req.get()); | |
| 1927 | |
| 1928 job->AddRequest(req.Pass()); | |
| 1929 // Completion happens during Job::CompleteRequests(). | |
| 1930 return ERR_IO_PENDING; | |
| 1931 } | |
| 1932 | |
| 1933 int HostResolverImpl::ResolveHelper(const Key& key, | |
| 1934 const RequestInfo& info, | |
| 1935 AddressList* addresses, | |
| 1936 const BoundNetLog& source_net_log) { | |
| 1937 // The result of |getaddrinfo| for empty hosts is inconsistent across systems. | |
| 1938 // On Windows it gives the default interface's address, whereas on Linux it | |
| 1939 // gives an error. We will make it fail on all platforms for consistency. | |
| 1940 if (info.hostname().empty() || info.hostname().size() > kMaxHostLength) | |
| 1941 return ERR_NAME_NOT_RESOLVED; | |
| 1942 | |
| 1943 int net_error = ERR_UNEXPECTED; | |
| 1944 if (ResolveAsIP(key, info, &net_error, addresses)) | |
| 1945 return net_error; | |
| 1946 if (ServeFromCache(key, info, &net_error, addresses)) { | |
| 1947 source_net_log.AddEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_CACHE_HIT); | |
| 1948 return net_error; | |
| 1949 } | |
| 1950 // TODO(szym): Do not do this if nsswitch.conf instructs not to. | |
| 1951 // http://crbug.com/117655 | |
| 1952 if (ServeFromHosts(key, info, addresses)) { | |
| 1953 source_net_log.AddEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_HOSTS_HIT); | |
| 1954 return OK; | |
| 1955 } | |
| 1956 return ERR_DNS_CACHE_MISS; | |
| 1957 } | |
| 1958 | |
| 1959 int HostResolverImpl::ResolveFromCache(const RequestInfo& info, | |
| 1960 AddressList* addresses, | |
| 1961 const BoundNetLog& source_net_log) { | |
| 1962 DCHECK(CalledOnValidThread()); | |
| 1963 DCHECK(addresses); | |
| 1964 | |
| 1965 // Update the net log and notify registered observers. | |
| 1966 LogStartRequest(source_net_log, info); | |
| 1967 | |
| 1968 Key key = GetEffectiveKeyForRequest(info, source_net_log); | |
| 1969 | |
| 1970 int rv = ResolveHelper(key, info, addresses, source_net_log); | |
| 1971 LogFinishRequest(source_net_log, info, rv); | |
| 1972 return rv; | |
| 1973 } | |
| 1974 | |
| 1975 void HostResolverImpl::CancelRequest(RequestHandle req_handle) { | |
| 1976 DCHECK(CalledOnValidThread()); | |
| 1977 Request* req = reinterpret_cast<Request*>(req_handle); | |
| 1978 DCHECK(req); | |
| 1979 Job* job = req->job(); | |
| 1980 DCHECK(job); | |
| 1981 job->CancelRequest(req); | |
| 1982 } | |
| 1983 | |
| 1984 void HostResolverImpl::SetDefaultAddressFamily(AddressFamily address_family) { | |
| 1985 DCHECK(CalledOnValidThread()); | |
| 1986 default_address_family_ = address_family; | |
| 1987 probe_ipv6_support_ = false; | |
| 1988 } | |
| 1989 | |
| 1990 AddressFamily HostResolverImpl::GetDefaultAddressFamily() const { | |
| 1991 return default_address_family_; | |
| 1992 } | |
| 1993 | |
| 1994 void HostResolverImpl::SetDnsClientEnabled(bool enabled) { | |
| 1995 DCHECK(CalledOnValidThread()); | |
| 1996 #if defined(ENABLE_BUILT_IN_DNS) | |
| 1997 if (enabled && !dns_client_) { | |
| 1998 SetDnsClient(DnsClient::CreateClient(net_log_)); | |
| 1999 } else if (!enabled && dns_client_) { | |
| 2000 SetDnsClient(scoped_ptr<DnsClient>()); | |
| 2001 } | |
| 2002 #endif | |
| 2003 } | |
| 2004 | |
| 2005 HostCache* HostResolverImpl::GetHostCache() { | |
| 2006 return cache_.get(); | |
| 2007 } | |
| 2008 | |
| 2009 base::Value* HostResolverImpl::GetDnsConfigAsValue() const { | |
| 2010 // Check if async DNS is disabled. | |
| 2011 if (!dns_client_.get()) | |
| 2012 return NULL; | |
| 2013 | |
| 2014 // Check if async DNS is enabled, but we currently have no configuration | |
| 2015 // for it. | |
| 2016 const DnsConfig* dns_config = dns_client_->GetConfig(); | |
| 2017 if (dns_config == NULL) | |
| 2018 return new base::DictionaryValue(); | |
| 2019 | |
| 2020 return dns_config->ToValue(); | |
| 2021 } | |
| 2022 | |
| 2023 bool HostResolverImpl::ResolveAsIP(const Key& key, | |
| 2024 const RequestInfo& info, | |
| 2025 int* net_error, | |
| 2026 AddressList* addresses) { | |
| 2027 DCHECK(addresses); | |
| 2028 DCHECK(net_error); | |
| 2029 IPAddressNumber ip_number; | |
| 2030 if (!ParseIPLiteralToNumber(key.hostname, &ip_number)) | |
| 2031 return false; | |
| 2032 | |
| 2033 DCHECK_EQ(key.host_resolver_flags & | |
| 2034 ~(HOST_RESOLVER_CANONNAME | HOST_RESOLVER_LOOPBACK_ONLY | | |
| 2035 HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6), | |
| 2036 0) << " Unhandled flag"; | |
| 2037 | |
| 2038 *net_error = OK; | |
| 2039 AddressFamily family = GetAddressFamily(ip_number); | |
| 2040 if (family == ADDRESS_FAMILY_IPV6 && | |
| 2041 !probe_ipv6_support_ && | |
| 2042 default_address_family_ == ADDRESS_FAMILY_IPV4) { | |
| 2043 // Don't return IPv6 addresses if default address family is set to IPv4, | |
| 2044 // and probes are disabled. | |
| 2045 *net_error = ERR_NAME_NOT_RESOLVED; | |
| 2046 } else if (key.address_family != ADDRESS_FAMILY_UNSPECIFIED && | |
| 2047 key.address_family != family) { | |
| 2048 // Don't return IPv6 addresses for IPv4 queries, and vice versa. | |
| 2049 *net_error = ERR_NAME_NOT_RESOLVED; | |
| 2050 } else { | |
| 2051 *addresses = AddressList::CreateFromIPAddress(ip_number, info.port()); | |
| 2052 if (key.host_resolver_flags & HOST_RESOLVER_CANONNAME) | |
| 2053 addresses->SetDefaultCanonicalName(); | |
| 2054 } | |
| 2055 return true; | |
| 2056 } | |
| 2057 | |
| 2058 bool HostResolverImpl::ServeFromCache(const Key& key, | |
| 2059 const RequestInfo& info, | |
| 2060 int* net_error, | |
| 2061 AddressList* addresses) { | |
| 2062 DCHECK(addresses); | |
| 2063 DCHECK(net_error); | |
| 2064 if (!info.allow_cached_response() || !cache_.get()) | |
| 2065 return false; | |
| 2066 | |
| 2067 const HostCache::Entry* cache_entry = cache_->Lookup( | |
| 2068 key, base::TimeTicks::Now()); | |
| 2069 if (!cache_entry) | |
| 2070 return false; | |
| 2071 | |
| 2072 *net_error = cache_entry->error; | |
| 2073 if (*net_error == OK) { | |
| 2074 if (cache_entry->has_ttl()) | |
| 2075 RecordTTL(cache_entry->ttl); | |
| 2076 *addresses = EnsurePortOnAddressList(cache_entry->addrlist, info.port()); | |
| 2077 } | |
| 2078 return true; | |
| 2079 } | |
| 2080 | |
| 2081 bool HostResolverImpl::ServeFromHosts(const Key& key, | |
| 2082 const RequestInfo& info, | |
| 2083 AddressList* addresses) { | |
| 2084 DCHECK(addresses); | |
| 2085 if (!HaveDnsConfig()) | |
| 2086 return false; | |
| 2087 addresses->clear(); | |
| 2088 | |
| 2089 // HOSTS lookups are case-insensitive. | |
| 2090 std::string hostname = base::StringToLowerASCII(key.hostname); | |
| 2091 | |
| 2092 const DnsHosts& hosts = dns_client_->GetConfig()->hosts; | |
| 2093 | |
| 2094 // If |address_family| is ADDRESS_FAMILY_UNSPECIFIED other implementations | |
| 2095 // (glibc and c-ares) return the first matching line. We have more | |
| 2096 // flexibility, but lose implicit ordering. | |
| 2097 // We prefer IPv6 because "happy eyeballs" will fall back to IPv4 if | |
| 2098 // necessary. | |
| 2099 if (key.address_family == ADDRESS_FAMILY_IPV6 || | |
| 2100 key.address_family == ADDRESS_FAMILY_UNSPECIFIED) { | |
| 2101 DnsHosts::const_iterator it = hosts.find( | |
| 2102 DnsHostsKey(hostname, ADDRESS_FAMILY_IPV6)); | |
| 2103 if (it != hosts.end()) | |
| 2104 addresses->push_back(IPEndPoint(it->second, info.port())); | |
| 2105 } | |
| 2106 | |
| 2107 if (key.address_family == ADDRESS_FAMILY_IPV4 || | |
| 2108 key.address_family == ADDRESS_FAMILY_UNSPECIFIED) { | |
| 2109 DnsHosts::const_iterator it = hosts.find( | |
| 2110 DnsHostsKey(hostname, ADDRESS_FAMILY_IPV4)); | |
| 2111 if (it != hosts.end()) | |
| 2112 addresses->push_back(IPEndPoint(it->second, info.port())); | |
| 2113 } | |
| 2114 | |
| 2115 // If got only loopback addresses and the family was restricted, resolve | |
| 2116 // again, without restrictions. See SystemHostResolverCall for rationale. | |
| 2117 if ((key.host_resolver_flags & | |
| 2118 HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6) && | |
| 2119 IsAllIPv4Loopback(*addresses)) { | |
| 2120 Key new_key(key); | |
| 2121 new_key.address_family = ADDRESS_FAMILY_UNSPECIFIED; | |
| 2122 new_key.host_resolver_flags &= | |
| 2123 ~HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6; | |
| 2124 return ServeFromHosts(new_key, info, addresses); | |
| 2125 } | |
| 2126 return !addresses->empty(); | |
| 2127 } | |
| 2128 | |
| 2129 void HostResolverImpl::CacheResult(const Key& key, | |
| 2130 const HostCache::Entry& entry, | |
| 2131 base::TimeDelta ttl) { | |
| 2132 if (cache_.get()) | |
| 2133 cache_->Set(key, entry, base::TimeTicks::Now(), ttl); | |
| 2134 } | |
| 2135 | |
| 2136 void HostResolverImpl::RemoveJob(Job* job) { | |
| 2137 DCHECK(job); | |
| 2138 JobMap::iterator it = jobs_.find(job->key()); | |
| 2139 if (it != jobs_.end() && it->second == job) | |
| 2140 jobs_.erase(it); | |
| 2141 } | |
| 2142 | |
| 2143 void HostResolverImpl::SetHaveOnlyLoopbackAddresses(bool result) { | |
| 2144 if (result) { | |
| 2145 additional_resolver_flags_ |= HOST_RESOLVER_LOOPBACK_ONLY; | |
| 2146 } else { | |
| 2147 additional_resolver_flags_ &= ~HOST_RESOLVER_LOOPBACK_ONLY; | |
| 2148 } | |
| 2149 } | |
| 2150 | |
| 2151 HostResolverImpl::Key HostResolverImpl::GetEffectiveKeyForRequest( | |
| 2152 const RequestInfo& info, const BoundNetLog& net_log) const { | |
| 2153 HostResolverFlags effective_flags = | |
| 2154 info.host_resolver_flags() | additional_resolver_flags_; | |
| 2155 AddressFamily effective_address_family = info.address_family(); | |
| 2156 | |
| 2157 if (info.address_family() == ADDRESS_FAMILY_UNSPECIFIED) { | |
| 2158 unsigned char ip_number[4]; | |
| 2159 url::Component host_comp(0, info.hostname().size()); | |
| 2160 int num_components; | |
| 2161 if (probe_ipv6_support_ && !use_local_ipv6_ && | |
| 2162 // Don't bother IPv6 probing when resolving IPv4 literals. | |
| 2163 url::IPv4AddressToNumber(info.hostname().c_str(), host_comp, ip_number, | |
| 2164 &num_components) != url::CanonHostInfo::IPV4) { | |
| 2165 // Google DNS address. | |
| 2166 const uint8 kIPv6Address[] = | |
| 2167 { 0x20, 0x01, 0x48, 0x60, 0x48, 0x60, 0x00, 0x00, | |
| 2168 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x88 }; | |
| 2169 IPAddressNumber address(kIPv6Address, | |
| 2170 kIPv6Address + arraysize(kIPv6Address)); | |
| 2171 BoundNetLog probe_net_log = BoundNetLog::Make( | |
| 2172 net_log.net_log(), NetLog::SOURCE_IPV6_REACHABILITY_CHECK); | |
| 2173 probe_net_log.BeginEvent(NetLog::TYPE_IPV6_REACHABILITY_CHECK, | |
| 2174 net_log.source().ToEventParametersCallback()); | |
| 2175 bool rv6 = IsGloballyReachable(address, probe_net_log); | |
| 2176 probe_net_log.EndEvent(NetLog::TYPE_IPV6_REACHABILITY_CHECK); | |
| 2177 if (rv6) | |
| 2178 net_log.AddEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_IPV6_SUPPORTED); | |
| 2179 | |
| 2180 if (rv6) { | |
| 2181 UMA_HISTOGRAM_BOOLEAN("Net.IPv6ConnectSuccessMatch", | |
| 2182 default_address_family_ == ADDRESS_FAMILY_UNSPECIFIED); | |
| 2183 } else { | |
| 2184 UMA_HISTOGRAM_BOOLEAN("Net.IPv6ConnectFailureMatch", | |
| 2185 default_address_family_ != ADDRESS_FAMILY_UNSPECIFIED); | |
| 2186 | |
| 2187 effective_address_family = ADDRESS_FAMILY_IPV4; | |
| 2188 effective_flags |= HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6; | |
| 2189 } | |
| 2190 } else { | |
| 2191 effective_address_family = default_address_family_; | |
| 2192 } | |
| 2193 } | |
| 2194 | |
| 2195 return Key(info.hostname(), effective_address_family, effective_flags); | |
| 2196 } | |
| 2197 | |
| 2198 void HostResolverImpl::AbortAllInProgressJobs() { | |
| 2199 // In Abort, a Request callback could spawn new Jobs with matching keys, so | |
| 2200 // first collect and remove all running jobs from |jobs_|. | |
| 2201 ScopedVector<Job> jobs_to_abort; | |
| 2202 for (JobMap::iterator it = jobs_.begin(); it != jobs_.end(); ) { | |
| 2203 Job* job = it->second; | |
| 2204 if (job->is_running()) { | |
| 2205 jobs_to_abort.push_back(job); | |
| 2206 jobs_.erase(it++); | |
| 2207 } else { | |
| 2208 DCHECK(job->is_queued()); | |
| 2209 ++it; | |
| 2210 } | |
| 2211 } | |
| 2212 | |
| 2213 // Pause the dispatcher so it won't start any new dispatcher jobs while | |
| 2214 // aborting the old ones. This is needed so that it won't start the second | |
| 2215 // DnsTransaction for a job in |jobs_to_abort| if the DnsConfig just became | |
| 2216 // invalid. | |
| 2217 PrioritizedDispatcher::Limits limits = dispatcher_->GetLimits(); | |
| 2218 dispatcher_->SetLimits( | |
| 2219 PrioritizedDispatcher::Limits(limits.reserved_slots.size(), 0)); | |
| 2220 | |
| 2221 // Life check to bail once |this| is deleted. | |
| 2222 base::WeakPtr<HostResolverImpl> self = weak_ptr_factory_.GetWeakPtr(); | |
| 2223 | |
| 2224 // Then Abort them. | |
| 2225 for (size_t i = 0; self.get() && i < jobs_to_abort.size(); ++i) { | |
| 2226 jobs_to_abort[i]->Abort(); | |
| 2227 jobs_to_abort[i] = NULL; | |
| 2228 } | |
| 2229 | |
| 2230 if (self) | |
| 2231 dispatcher_->SetLimits(limits); | |
| 2232 } | |
| 2233 | |
| 2234 void HostResolverImpl::AbortDnsTasks() { | |
| 2235 // Pause the dispatcher so it won't start any new dispatcher jobs while | |
| 2236 // aborting the old ones. This is needed so that it won't start the second | |
| 2237 // DnsTransaction for a job if the DnsConfig just changed. | |
| 2238 PrioritizedDispatcher::Limits limits = dispatcher_->GetLimits(); | |
| 2239 dispatcher_->SetLimits( | |
| 2240 PrioritizedDispatcher::Limits(limits.reserved_slots.size(), 0)); | |
| 2241 | |
| 2242 for (JobMap::iterator it = jobs_.begin(); it != jobs_.end(); ++it) | |
| 2243 it->second->AbortDnsTask(); | |
| 2244 dispatcher_->SetLimits(limits); | |
| 2245 } | |
| 2246 | |
| 2247 void HostResolverImpl::TryServingAllJobsFromHosts() { | |
| 2248 if (!HaveDnsConfig()) | |
| 2249 return; | |
| 2250 | |
| 2251 // TODO(szym): Do not do this if nsswitch.conf instructs not to. | |
| 2252 // http://crbug.com/117655 | |
| 2253 | |
| 2254 // Life check to bail once |this| is deleted. | |
| 2255 base::WeakPtr<HostResolverImpl> self = weak_ptr_factory_.GetWeakPtr(); | |
| 2256 | |
| 2257 for (JobMap::iterator it = jobs_.begin(); self.get() && it != jobs_.end();) { | |
| 2258 Job* job = it->second; | |
| 2259 ++it; | |
| 2260 // This could remove |job| from |jobs_|, but iterator will remain valid. | |
| 2261 job->ServeFromHosts(); | |
| 2262 } | |
| 2263 } | |
| 2264 | |
| 2265 void HostResolverImpl::OnIPAddressChanged() { | |
| 2266 resolved_known_ipv6_hostname_ = false; | |
| 2267 // Abandon all ProbeJobs. | |
| 2268 probe_weak_ptr_factory_.InvalidateWeakPtrs(); | |
| 2269 if (cache_.get()) | |
| 2270 cache_->clear(); | |
| 2271 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) | |
| 2272 new LoopbackProbeJob(probe_weak_ptr_factory_.GetWeakPtr()); | |
| 2273 #endif | |
| 2274 AbortAllInProgressJobs(); | |
| 2275 // |this| may be deleted inside AbortAllInProgressJobs(). | |
| 2276 } | |
| 2277 | |
| 2278 void HostResolverImpl::OnDNSChanged() { | |
| 2279 DnsConfig dns_config; | |
| 2280 NetworkChangeNotifier::GetDnsConfig(&dns_config); | |
| 2281 | |
| 2282 if (net_log_) { | |
| 2283 net_log_->AddGlobalEntry( | |
| 2284 NetLog::TYPE_DNS_CONFIG_CHANGED, | |
| 2285 base::Bind(&NetLogDnsConfigCallback, &dns_config)); | |
| 2286 } | |
| 2287 | |
| 2288 // TODO(szym): Remove once http://crbug.com/137914 is resolved. | |
| 2289 received_dns_config_ = dns_config.IsValid(); | |
| 2290 // Conservatively assume local IPv6 is needed when DnsConfig is not valid. | |
| 2291 use_local_ipv6_ = !dns_config.IsValid() || dns_config.use_local_ipv6; | |
| 2292 | |
| 2293 num_dns_failures_ = 0; | |
| 2294 | |
| 2295 // We want a new DnsSession in place, before we Abort running Jobs, so that | |
| 2296 // the newly started jobs use the new config. | |
| 2297 if (dns_client_.get()) { | |
| 2298 dns_client_->SetConfig(dns_config); | |
| 2299 if (dns_client_->GetConfig()) | |
| 2300 UMA_HISTOGRAM_BOOLEAN("AsyncDNS.DnsClientEnabled", true); | |
| 2301 } | |
| 2302 | |
| 2303 // If the DNS server has changed, existing cached info could be wrong so we | |
| 2304 // have to drop our internal cache :( Note that OS level DNS caches, such | |
| 2305 // as NSCD's cache should be dropped automatically by the OS when | |
| 2306 // resolv.conf changes so we don't need to do anything to clear that cache. | |
| 2307 if (cache_.get()) | |
| 2308 cache_->clear(); | |
| 2309 | |
| 2310 // Life check to bail once |this| is deleted. | |
| 2311 base::WeakPtr<HostResolverImpl> self = weak_ptr_factory_.GetWeakPtr(); | |
| 2312 | |
| 2313 // Existing jobs will have been sent to the original server so they need to | |
| 2314 // be aborted. | |
| 2315 AbortAllInProgressJobs(); | |
| 2316 | |
| 2317 // |this| may be deleted inside AbortAllInProgressJobs(). | |
| 2318 if (self.get()) | |
| 2319 TryServingAllJobsFromHosts(); | |
| 2320 } | |
| 2321 | |
| 2322 bool HostResolverImpl::HaveDnsConfig() const { | |
| 2323 // Use DnsClient only if it's fully configured and there is no override by | |
| 2324 // ScopedDefaultHostResolverProc. | |
| 2325 // The alternative is to use NetworkChangeNotifier to override DnsConfig, | |
| 2326 // but that would introduce construction order requirements for NCN and SDHRP. | |
| 2327 return (dns_client_.get() != NULL) && (dns_client_->GetConfig() != NULL) && | |
| 2328 !(proc_params_.resolver_proc.get() == NULL && | |
| 2329 HostResolverProc::GetDefault() != NULL); | |
| 2330 } | |
| 2331 | |
| 2332 void HostResolverImpl::OnDnsTaskResolve(int net_error) { | |
| 2333 DCHECK(dns_client_); | |
| 2334 if (net_error == OK) { | |
| 2335 num_dns_failures_ = 0; | |
| 2336 return; | |
| 2337 } | |
| 2338 ++num_dns_failures_; | |
| 2339 if (num_dns_failures_ < kMaximumDnsFailures) | |
| 2340 return; | |
| 2341 | |
| 2342 // Disable DnsClient until the next DNS change. Must be done before aborting | |
| 2343 // DnsTasks, since doing so may start new jobs. | |
| 2344 dns_client_->SetConfig(DnsConfig()); | |
| 2345 | |
| 2346 // Switch jobs with active DnsTasks over to using ProcTasks. | |
| 2347 AbortDnsTasks(); | |
| 2348 | |
| 2349 UMA_HISTOGRAM_BOOLEAN("AsyncDNS.DnsClientEnabled", false); | |
| 2350 UMA_HISTOGRAM_CUSTOM_ENUMERATION("AsyncDNS.DnsClientDisabledReason", | |
| 2351 std::abs(net_error), | |
| 2352 GetAllErrorCodesForUma()); | |
| 2353 } | |
| 2354 | |
| 2355 void HostResolverImpl::SetDnsClient(scoped_ptr<DnsClient> dns_client) { | |
| 2356 // DnsClient and config must be updated before aborting DnsTasks, since doing | |
| 2357 // so may start new jobs. | |
| 2358 dns_client_ = dns_client.Pass(); | |
| 2359 if (dns_client_ && !dns_client_->GetConfig() && | |
| 2360 num_dns_failures_ < kMaximumDnsFailures) { | |
| 2361 DnsConfig dns_config; | |
| 2362 NetworkChangeNotifier::GetDnsConfig(&dns_config); | |
| 2363 dns_client_->SetConfig(dns_config); | |
| 2364 num_dns_failures_ = 0; | |
| 2365 if (dns_client_->GetConfig()) | |
| 2366 UMA_HISTOGRAM_BOOLEAN("AsyncDNS.DnsClientEnabled", true); | |
| 2367 } | |
| 2368 | |
| 2369 AbortDnsTasks(); | |
| 2370 } | |
| 2371 | |
| 2372 } // namespace net | |
| OLD | NEW |