diff options
author | szym@chromium.org <szym@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-08-14 22:36:36 +0000 |
---|---|---|
committer | szym@chromium.org <szym@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-08-14 22:36:36 +0000 |
commit | 6c41190e39d8ecdc0dbcc50bd87125f39c39a101 (patch) | |
tree | 80be44493d34177ca8eb0e1c0f0fe00eaeac1845 /net/base/host_resolver_impl.cc | |
parent | 5d4b7adcca413109257be1a72a18d883480c3cc0 (diff) | |
download | chromium_src-6c41190e39d8ecdc0dbcc50bd87125f39c39a101.zip chromium_src-6c41190e39d8ecdc0dbcc50bd87125f39c39a101.tar.gz chromium_src-6c41190e39d8ecdc0dbcc50bd87125f39c39a101.tar.bz2 |
[net/dns] Resolve AF_UNSPEC on dual-stacked systems. Sort addresses according to RFC3484.
BUG=113993
TEST=./net_unittests --gtest_filter=AddressSorter*:HostResolverImplDnsTest.DnsTaskUnspec
Review URL: https://chromiumcodereview.appspot.com/10442098
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@151586 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/base/host_resolver_impl.cc')
-rw-r--r-- | net/base/host_resolver_impl.cc | 162 |
1 files changed, 128 insertions, 34 deletions
diff --git a/net/base/host_resolver_impl.cc b/net/base/host_resolver_impl.cc index 68b6bd5..8b42a22 100644 --- a/net/base/host_resolver_impl.cc +++ b/net/base/host_resolver_impl.cc @@ -38,6 +38,7 @@ #include "net/base/net_errors.h" #include "net/base/net_log.h" #include "net/base/net_util.h" +#include "net/dns/address_sorter.h" #include "net/dns/dns_client.h" #include "net/dns/dns_config_service.h" #include "net/dns/dns_protocol.h" @@ -609,7 +610,7 @@ class HostResolverImpl::ProcTask void Cancel() { DCHECK(origin_loop_->BelongsToCurrentThread()); - if (was_canceled()) + if (was_canceled() || was_completed()) return; callback_.Reset(); @@ -1042,32 +1043,33 @@ class HostResolverImpl::IPv6ProbeJob // Resolves the hostname using DnsTransaction. // TODO(szym): This could be moved to separate source file as well. -class HostResolverImpl::DnsTask { +class HostResolverImpl::DnsTask : public base::SupportsWeakPtr<DnsTask> { public: typedef base::Callback<void(int net_error, const AddressList& addr_list, base::TimeDelta ttl)> Callback; - DnsTask(DnsTransactionFactory* factory, + DnsTask(DnsClient* client, const Key& key, const Callback& callback, const BoundNetLog& job_net_log) - : callback_(callback), net_log_(job_net_log) { - DCHECK(factory); + : client_(client), + family_(key.address_family), + callback_(callback), + net_log_(job_net_log) { + DCHECK(client); DCHECK(!callback.is_null()); - // For now we treat ADDRESS_FAMILY_UNSPEC as if it was IPV4. - uint16 qtype = (key.address_family == ADDRESS_FAMILY_IPV6) - ? dns_protocol::kTypeAAAA - : dns_protocol::kTypeA; - // TODO(szym): Implement "happy eyeballs". - transaction_ = factory->CreateTransaction( + // If unspecified, do IPv4 first, because suffix search will be faster. + uint16 qtype = (family_ == ADDRESS_FAMILY_IPV6) ? + dns_protocol::kTypeAAAA : + dns_protocol::kTypeA; + transaction_ = client_->GetTransactionFactory()->CreateTransaction( key.hostname, qtype, base::Bind(&DnsTask::OnTransactionComplete, base::Unretained(this), - base::TimeTicks::Now()), + true /* first_query */, base::TimeTicks::Now()), net_log_); - DCHECK(transaction_.get()); } int Start() { @@ -1075,47 +1077,138 @@ class HostResolverImpl::DnsTask { return transaction_->Start(); } - void OnTransactionComplete(const base::TimeTicks& start_time, + private: + void OnTransactionComplete(bool first_query, + const base::TimeTicks& start_time, DnsTransaction* transaction, int net_error, const DnsResponse* response) { DCHECK(transaction); // Run |callback_| last since the owning Job will then delete this DnsTask. - DnsResponse::Result result = DnsResponse::DNS_SUCCESS; - if (net_error == OK) { - CHECK(response); - DNS_HISTOGRAM("AsyncDNS.TransactionSuccess", + if (net_error != OK) { + DNS_HISTOGRAM("AsyncDNS.TransactionFailure", base::TimeTicks::Now() - start_time); - AddressList addr_list; - base::TimeDelta ttl; - result = response->ParseToAddressList(&addr_list, &ttl); - UMA_HISTOGRAM_ENUMERATION("AsyncDNS.ParseToAddressList", - result, - DnsResponse::DNS_PARSE_RESULT_MAX); - if (result == DnsResponse::DNS_SUCCESS) { - net_log_.EndEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_DNS_TASK, - addr_list.CreateNetLogCallback()); - callback_.Run(net_error, addr_list, ttl); + OnFailure(net_error, DnsResponse::DNS_PARSE_OK); + return; + } + + CHECK(response); + DNS_HISTOGRAM("AsyncDNS.TransactionSuccess", + base::TimeTicks::Now() - start_time); + AddressList addr_list; + base::TimeDelta ttl; + DnsResponse::Result result = response->ParseToAddressList(&addr_list, &ttl); + UMA_HISTOGRAM_ENUMERATION("AsyncDNS.ParseToAddressList", + result, + DnsResponse::DNS_PARSE_RESULT_MAX); + if (result != DnsResponse::DNS_PARSE_OK) { + // Fail even if the other query succeeds. + OnFailure(ERR_DNS_MALFORMED_RESPONSE, result); + return; + } + + bool needs_sort = false; + if (first_query) { + DCHECK(client_->GetConfig()) << + "Transaction should have been aborted when config changed!"; + if (family_ == ADDRESS_FAMILY_IPV6) { + needs_sort = (addr_list.size() > 1); + } else if (family_ == ADDRESS_FAMILY_UNSPECIFIED) { + first_addr_list_ = addr_list; + first_ttl_ = ttl; + // Use fully-qualified domain name to avoid search. + transaction_ = client_->GetTransactionFactory()->CreateTransaction( + response->GetDottedName() + ".", + dns_protocol::kTypeAAAA, + base::Bind(&DnsTask::OnTransactionComplete, base::Unretained(this), + false /* first_query */, base::TimeTicks::Now()), + net_log_); + net_error = transaction_->Start(); + if (net_error != ERR_IO_PENDING) + OnFailure(net_error, DnsResponse::DNS_PARSE_OK); return; } - net_error = ERR_DNS_MALFORMED_RESPONSE; } else { - DNS_HISTOGRAM("AsyncDNS.TransactionFailure", + DCHECK_EQ(ADDRESS_FAMILY_UNSPECIFIED, family_); + bool has_ipv6_addresses = !addr_list.empty(); + if (!first_addr_list_.empty()) { + ttl = std::min(ttl, first_ttl_); + // Place IPv4 addresses after IPv6. + addr_list.insert(addr_list.end(), first_addr_list_.begin(), + first_addr_list_.end()); + } + needs_sort = (has_ipv6_addresses && addr_list.size() > 1); + } + + if (addr_list.empty()) { + // TODO(szym): Don't fallback to ProcTask in this case. + OnFailure(ERR_NAME_NOT_RESOLVED, DnsResponse::DNS_PARSE_OK); + return; + } + + if (needs_sort) { + // Sort could complete synchronously. + client_->GetAddressSorter()->Sort( + addr_list, + base::Bind(&DnsTask::OnSortComplete, AsWeakPtr(), + base::TimeTicks::Now(), + ttl)); + } else { + OnSuccess(addr_list, ttl); + } + } + + void OnSortComplete(base::TimeTicks start_time, + base::TimeDelta ttl, + bool success, + const AddressList& addr_list) { + if (!success) { + DNS_HISTOGRAM("AsyncDNS.SortFailure", base::TimeTicks::Now() - start_time); + OnFailure(ERR_DNS_SORT_ERROR, DnsResponse::DNS_PARSE_OK); + return; + } + + DNS_HISTOGRAM("AsyncDNS.SortSuccess", + base::TimeTicks::Now() - start_time); + + // AddressSorter prunes unusable destinations. + if (addr_list.empty()) { + LOG(WARNING) << "Address list empty after RFC3484 sort"; + OnFailure(ERR_NAME_NOT_RESOLVED, DnsResponse::DNS_PARSE_OK); + return; } + + OnSuccess(addr_list, ttl); + } + + void OnFailure(int net_error, DnsResponse::Result result) { + DCHECK_NE(OK, net_error); net_log_.EndEvent( NetLog::TYPE_HOST_RESOLVER_IMPL_DNS_TASK, base::Bind(&NetLogDnsTaskFailedCallback, net_error, result)); callback_.Run(net_error, AddressList(), base::TimeDelta()); } - private: + void OnSuccess(const AddressList& addr_list, base::TimeDelta ttl) { + net_log_.EndEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_DNS_TASK, + addr_list.CreateNetLogCallback()); + callback_.Run(OK, addr_list, ttl); + } + + DnsClient* client_; + AddressFamily family_; // The listener to the results of this DnsTask. Callback callback_; - const BoundNetLog net_log_; scoped_ptr<DnsTransaction> transaction_; + + // Results from the first transaction. Used only if |family_| is unspecified. + AddressList first_addr_list_; + base::TimeDelta first_ttl_; + + DISALLOW_COPY_AND_ASSIGN(DnsTask); }; //----------------------------------------------------------------------------- @@ -1214,7 +1307,7 @@ class HostResolverImpl::Job : public PrioritizedDispatcher::Job { } // Marks |req| as cancelled. If it was the last active Request, also finishes - // this Job marking it either as aborted or cancelled, and deletes it. + // this Job, marking it as cancelled, and deletes it. void CancelRequest(Request* req) { DCHECK_EQ(key_.hostname, req->info().hostname()); DCHECK(!req->was_canceled()); @@ -1381,7 +1474,7 @@ class HostResolverImpl::Job : public PrioritizedDispatcher::Job { void StartDnsTask() { DCHECK(resolver_->HaveDnsConfig()); dns_task_.reset(new DnsTask( - resolver_->dns_client_->GetTransactionFactory(), + resolver_->dns_client_.get(), key_, base::Bind(&Job::OnDnsTaskComplete, base::Unretained(this)), net_log_)); @@ -1415,6 +1508,7 @@ class HostResolverImpl::Job : public PrioritizedDispatcher::Job { } UmaAsyncDnsResolveStatus(RESOLVE_STATUS_DNS_SUCCESS); + CompleteRequests(net_error, addr_list, ttl); } |