diff options
author | szym@chromium.org <szym@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-02-21 21:23:20 +0000 |
---|---|---|
committer | szym@chromium.org <szym@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-02-21 21:23:20 +0000 |
commit | b3601bc27df66f4e58086defc5788fc6365e4601 (patch) | |
tree | 1a938a765e1386e784ff726c580cfa93502727c4 /net/dns | |
parent | ad48b7fbd53d9180b5b917dbf9142b7bd6fa6fa6 (diff) | |
download | chromium_src-b3601bc27df66f4e58086defc5788fc6365e4601.zip chromium_src-b3601bc27df66f4e58086defc5788fc6365e4601.tar.gz chromium_src-b3601bc27df66f4e58086defc5788fc6365e4601.tar.bz2 |
[net] Asynchronous DNS ready for experiments.
If started with --enable-async-dns, HostResolverImpl will use
DnsConfigService to determine system DNS configuration and
DnsTransaction to resolve host names. It will fallback to
HostResolverProc on failure.
BUG=90881, 107880, 113829
TEST=./net_unittests --gtest_filter=HostResolverImpl*:Dns*
Review URL: http://codereview.chromium.org/9369045
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@122878 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/dns')
-rw-r--r-- | net/dns/async_host_resolver.cc | 524 | ||||
-rw-r--r-- | net/dns/async_host_resolver.h | 138 | ||||
-rw-r--r-- | net/dns/async_host_resolver_unittest.cc | 544 | ||||
-rw-r--r-- | net/dns/dns_config_service.h | 4 | ||||
-rw-r--r-- | net/dns/dns_config_service_posix.cc | 4 | ||||
-rw-r--r-- | net/dns/dns_config_service_posix_unittest.cc | 2 | ||||
-rw-r--r-- | net/dns/dns_config_service_win.cc | 13 | ||||
-rw-r--r-- | net/dns/dns_response.cc | 84 | ||||
-rw-r--r-- | net/dns/dns_response.h | 28 | ||||
-rw-r--r-- | net/dns/dns_response_unittest.cc | 272 | ||||
-rw-r--r-- | net/dns/dns_session.h | 4 | ||||
-rw-r--r-- | net/dns/dns_test_util.h | 43 | ||||
-rw-r--r-- | net/dns/dns_transaction.cc | 47 | ||||
-rw-r--r-- | net/dns/dns_transaction.h | 4 | ||||
-rw-r--r-- | net/dns/dns_transaction_unittest.cc | 19 |
15 files changed, 417 insertions, 1313 deletions
diff --git a/net/dns/async_host_resolver.cc b/net/dns/async_host_resolver.cc deleted file mode 100644 index 72e5472..0000000 --- a/net/dns/async_host_resolver.cc +++ /dev/null @@ -1,524 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "net/dns/async_host_resolver.h" - -#include <algorithm> - -#include "base/bind.h" -#include "base/logging.h" -#include "base/message_loop.h" -#include "base/rand_util.h" -#include "base/stl_util.h" -#include "base/values.h" -#include "net/base/address_list.h" -#include "net/base/dns_util.h" -#include "net/base/net_errors.h" -#include "net/dns/dns_protocol.h" -#include "net/dns/dns_response.h" -#include "net/dns/dns_session.h" -#include "net/socket/client_socket_factory.h" - -namespace net { - -namespace { - -// TODO(agayev): fix this when IPv6 support is added. -uint16 QueryTypeFromAddressFamily(AddressFamily address_family) { - return dns_protocol::kTypeA; -} - -class RequestParameters : public NetLog::EventParameters { - public: - RequestParameters(const HostResolver::RequestInfo& info, - const NetLog::Source& source) - : info_(info), source_(source) {} - - virtual Value* ToValue() const { - DictionaryValue* dict = new DictionaryValue(); - dict->SetString("hostname", info_.host_port_pair().ToString()); - dict->SetInteger("address_family", - static_cast<int>(info_.address_family())); - dict->SetBoolean("allow_cached_response", info_.allow_cached_response()); - dict->SetBoolean("is_speculative", info_.is_speculative()); - dict->SetInteger("priority", info_.priority()); - - if (source_.is_valid()) - dict->Set("source_dependency", source_.ToValue()); - - return dict; - } - - private: - const HostResolver::RequestInfo info_; - const NetLog::Source source_; -}; - -} // namespace - -HostResolver* CreateAsyncHostResolver(size_t max_concurrent_resolves, - const IPAddressNumber& dns_ip, - NetLog* net_log) { - size_t max_dns_requests = max_concurrent_resolves; - if (max_dns_requests == 0) - max_dns_requests = 20; - size_t max_pending_requests = max_dns_requests * 100; - DnsConfig config; - config.nameservers.push_back(IPEndPoint(dns_ip, 53)); - DnsSession* session = new DnsSession( - config, - ClientSocketFactory::GetDefaultFactory(), - base::Bind(&base::RandInt), - net_log); - HostResolver* resolver = new AsyncHostResolver( - max_dns_requests, - max_pending_requests, - HostCache::CreateDefaultCache(), - DnsTransactionFactory::CreateFactory(session), - net_log); - return resolver; -} - -//----------------------------------------------------------------------------- -// Every call to Resolve() results in Request object being created. Such a -// call may complete either synchronously or asynchronously or it may get -// cancelled, which can be either through specific CancelRequest call or by -// the destruction of AsyncHostResolver, which would destruct pending or -// in-progress requests, causing them to be cancelled. Synchronous -// resolution sets |callback_| to NULL, thus, if in the destructor we still -// have a non-NULL |callback_|, we are being cancelled. -class AsyncHostResolver::Request { - public: - Request(AsyncHostResolver* resolver, - const BoundNetLog& source_net_log, - const BoundNetLog& request_net_log, - const HostResolver::RequestInfo& info, - const CompletionCallback& callback, - AddressList* addresses) - : resolver_(resolver), - source_net_log_(source_net_log), - request_net_log_(request_net_log), - info_(info), - callback_(callback), - addresses_(addresses), - result_(ERR_UNEXPECTED) { - DCHECK(addresses_); - DCHECK(resolver_); - resolver_->OnStart(this); - key_ = Key(info.hostname(), - QueryTypeFromAddressFamily(info.address_family())); - } - - ~Request() { - if (!callback_.is_null()) - resolver_->OnCancel(this); - } - - int result() const { return result_; } - const Key& key() const { - DCHECK(IsValid()); - return key_; - } - const HostResolver::RequestInfo& info() const { return info_; } - RequestPriority priority() const { return info_.priority(); } - const BoundNetLog& source_net_log() const { return source_net_log_; } - const BoundNetLog& request_net_log() const { return request_net_log_; } - - bool ResolveAsIp() { - IPAddressNumber ip_number; - if (!ParseIPLiteralToNumber(info_.hostname(), &ip_number)) - return false; - - if (ip_number.size() != kIPv4AddressSize) { - result_ = ERR_NAME_NOT_RESOLVED; - } else { - *addresses_ = AddressList::CreateFromIPAddressWithCname( - ip_number, - info_.port(), - info_.host_resolver_flags() & HOST_RESOLVER_CANONNAME); - result_ = OK; - } - return true; - } - - bool ServeFromCache() { - HostCache* cache = resolver_->cache_.get(); - if (!cache || !info_.allow_cached_response()) - return false; - - HostCache::Key key(info_.hostname(), info_.address_family(), - info_.host_resolver_flags()); - const HostCache::Entry* cache_entry = cache->Lookup( - key, base::TimeTicks::Now()); - if (cache_entry) { - request_net_log_.AddEvent( - NetLog::TYPE_ASYNC_HOST_RESOLVER_CACHE_HIT, NULL); - DCHECK_EQ(OK, cache_entry->error); - result_ = cache_entry->error; - *addresses_ = - CreateAddressListUsingPort(cache_entry->addrlist, info_.port()); - return true; - } - return false; - } - - // Called when a request completes synchronously; we do not have an - // AddressList argument, since in case of a successful synchronous - // completion, either ResolveAsIp or ServerFromCache would set the - // |addresses_| and in case of an unsuccessful synchronous completion, we - // do not touch |addresses_|. - void OnSyncComplete(int result) { - callback_.Reset(); - resolver_->OnFinish(this, result); - } - - // Called when a request completes asynchronously. - void OnAsyncComplete(int result, const AddressList& addresses) { - if (result == OK) - *addresses_ = CreateAddressListUsingPort(addresses, info_.port()); - DCHECK_EQ(false, callback_.is_null()); - CompletionCallback callback = callback_; - callback_.Reset(); - resolver_->OnFinish(this, result); - callback.Run(result); - } - - // Returns true if request has a validly formed hostname. - bool IsValid() const { - return !info_.hostname().empty() && !key_.first.empty(); - } - - private: - AsyncHostResolver* resolver_; - BoundNetLog source_net_log_; - BoundNetLog request_net_log_; - const HostResolver::RequestInfo info_; - Key key_; - CompletionCallback callback_; - AddressList* addresses_; - int result_; -}; - -//----------------------------------------------------------------------------- -AsyncHostResolver::AsyncHostResolver(size_t max_dns_requests, - size_t max_pending_requests, - HostCache* cache, - scoped_ptr<DnsTransactionFactory> client, - NetLog* net_log) - : max_dns_transactions_(max_dns_requests), - max_pending_requests_(max_pending_requests), - cache_(cache), - client_(client.Pass()), - net_log_(net_log) { -} - -AsyncHostResolver::~AsyncHostResolver() { - // Destroy request lists. - for (KeyRequestListMap::iterator it = requestlist_map_.begin(); - it != requestlist_map_.end(); ++it) - STLDeleteElements(&it->second); - - // Destroy DNS transactions. - STLDeleteElements(&dns_transactions_); - - // Destroy pending requests. - for (size_t i = 0; i < arraysize(pending_requests_); ++i) - STLDeleteElements(&pending_requests_[i]); -} - -int AsyncHostResolver::Resolve(const RequestInfo& info, - AddressList* addresses, - const CompletionCallback& callback, - RequestHandle* out_req, - const BoundNetLog& source_net_log) { - DCHECK(addresses); - DCHECK_EQ(false, callback.is_null()); - scoped_ptr<Request> request( - CreateNewRequest(info, callback, addresses, source_net_log)); - - int rv = ERR_UNEXPECTED; - if (!request->IsValid()) - rv = ERR_NAME_NOT_RESOLVED; - else if (request->ResolveAsIp() || request->ServeFromCache()) - rv = request->result(); - else if (AttachToRequestList(request.get())) - rv = ERR_IO_PENDING; - else if (dns_transactions_.size() < max_dns_transactions_) - rv = StartNewDnsRequestFor(request.get()); - else - rv = Enqueue(request.get()); - - if (rv != ERR_IO_PENDING) { - request->OnSyncComplete(rv); - } else { - Request* req = request.release(); - if (out_req) - *out_req = reinterpret_cast<RequestHandle>(req); - } - return rv; -} - -int AsyncHostResolver::ResolveFromCache(const RequestInfo& info, - AddressList* addresses, - const BoundNetLog& source_net_log) { - scoped_ptr<Request> request( - CreateNewRequest(info, CompletionCallback(), addresses, source_net_log)); - int rv = ERR_UNEXPECTED; - if (!request->IsValid()) - rv = ERR_NAME_NOT_RESOLVED; - else if (request->ResolveAsIp() || request->ServeFromCache()) - rv = request->result(); - else - rv = ERR_DNS_CACHE_MISS; - request->OnSyncComplete(rv); - return rv; -} - -void AsyncHostResolver::OnStart(Request* request) { - DCHECK(request); - - request->source_net_log().BeginEvent( - NetLog::TYPE_ASYNC_HOST_RESOLVER, - make_scoped_refptr(new NetLogSourceParameter( - "source_dependency", request->request_net_log().source()))); - request->request_net_log().BeginEvent( - NetLog::TYPE_ASYNC_HOST_RESOLVER_REQUEST, - make_scoped_refptr(new RequestParameters( - request->info(), request->source_net_log().source()))); -} - -void AsyncHostResolver::OnFinish(Request* request, int result) { - DCHECK(request); - request->request_net_log().EndEventWithNetErrorCode( - NetLog::TYPE_ASYNC_HOST_RESOLVER_REQUEST, result); - request->source_net_log().EndEvent( - NetLog::TYPE_ASYNC_HOST_RESOLVER, NULL); -} - -void AsyncHostResolver::OnCancel(Request* request) { - DCHECK(request); - - request->request_net_log().AddEvent( - NetLog::TYPE_CANCELLED, NULL); - request->request_net_log().EndEvent( - NetLog::TYPE_ASYNC_HOST_RESOLVER_REQUEST, NULL); - request->source_net_log().EndEvent( - NetLog::TYPE_ASYNC_HOST_RESOLVER, NULL); -} - -void AsyncHostResolver::CancelRequest(RequestHandle req_handle) { - scoped_ptr<Request> request(reinterpret_cast<Request*>(req_handle)); - DCHECK(request.get()); - - KeyRequestListMap::iterator it = requestlist_map_.find(request->key()); - if (it != requestlist_map_.end()) - it->second.remove(request.get()); - else - pending_requests_[request->priority()].remove(request.get()); -} - -void AsyncHostResolver::SetDefaultAddressFamily( - AddressFamily address_family) { - NOTIMPLEMENTED(); -} - -AddressFamily AsyncHostResolver::GetDefaultAddressFamily() const { - return ADDRESS_FAMILY_IPV4; -} - -HostCache* AsyncHostResolver::GetHostCache() { - return cache_.get(); -} - -void AsyncHostResolver::OnDnsTransactionComplete( - DnsTransaction* transaction, - int result, - const DnsResponse* response) { - DCHECK(std::find(dns_transactions_.begin(), - dns_transactions_.end(), - transaction) != dns_transactions_.end()); - - // If by the time requests that caused |transaction| are cancelled, we do - // not have a port number to associate with the result, therefore, we - // assume the most common port, otherwise we use the port number of the - // first request. - KeyRequestListMap::iterator rit = requestlist_map_.find( - std::make_pair(transaction->GetHostname(), transaction->GetType())); - DCHECK(rit != requestlist_map_.end()); - RequestList& requests = rit->second; - int port = requests.empty() ? 80 : requests.front()->info().port(); - - // Extract AddressList and TTL out of DnsResponse. - AddressList addr_list; - uint32 ttl = kuint32max; - if (result == OK) { - IPAddressList ip_addresses; - DnsRecordParser parser = response->Parser(); - DnsResourceRecord record; - // TODO(szym): Add stricter checking of names, aliases and address lengths. - while (parser.ParseRecord(&record)) { - if (record.type == transaction->GetType() && - (record.rdata.size() == kIPv4AddressSize || - record.rdata.size() == kIPv6AddressSize)) { - ip_addresses.push_back(IPAddressNumber(record.rdata.begin(), - record.rdata.end())); - ttl = std::min(ttl, record.ttl); - } - } - if (!ip_addresses.empty()) - addr_list = AddressList::CreateFromIPAddressList(ip_addresses, port); - else - result = ERR_NAME_NOT_RESOLVED; - } - - // Run callback of every request that was depending on this DNS request, - // also notify observers. - for (RequestList::iterator it = requests.begin(); it != requests.end(); ++it) - (*it)->OnAsyncComplete(result, addr_list); - - // It is possible that the requests that caused |transaction| to be - // created are cancelled by the time |transaction| completes. In that - // case |requests| would be empty. We are knowingly throwing away the - // result of a DNS resolution in that case, because (a) if there are no - // requests, we do not have info to obtain a key from, (b) DnsTransaction - // does not have info(). - // TODO(szym): Should DnsTransaction ignore HostResolverFlags or use defaults? - if ((result == OK || result == ERR_NAME_NOT_RESOLVED) && cache_.get() && - !requests.empty()) { - Request* request = requests.front(); - HostResolver::RequestInfo info = request->info(); - HostCache::Key key( - info.hostname(), info.address_family(), info.host_resolver_flags()); - // Store negative results with TTL 0 to flush out the old entry. - cache_->Set(key, - result, - addr_list, - base::TimeTicks::Now(), - (result == OK) ? base::TimeDelta::FromSeconds(ttl) - : base::TimeDelta()); - } - - // Cleanup requests. - STLDeleteElements(&requests); - requestlist_map_.erase(rit); - - // Cleanup |transaction| and start a new one if there are pending requests. - dns_transactions_.remove(transaction); - delete transaction; - ProcessPending(); -} - -AsyncHostResolver::Request* AsyncHostResolver::CreateNewRequest( - const RequestInfo& info, - const CompletionCallback& callback, - AddressList* addresses, - const BoundNetLog& source_net_log) { - BoundNetLog request_net_log = BoundNetLog::Make(net_log_, - NetLog::SOURCE_ASYNC_HOST_RESOLVER_REQUEST); - return new Request( - this, source_net_log, request_net_log, info, callback, addresses); -} - -bool AsyncHostResolver::AttachToRequestList(Request* request) { - KeyRequestListMap::iterator it = requestlist_map_.find(request->key()); - if (it == requestlist_map_.end()) - return false; - it->second.push_back(request); - return true; -} - -int AsyncHostResolver::StartNewDnsRequestFor(Request* request) { - DCHECK(requestlist_map_.find(request->key()) == requestlist_map_.end()); - DCHECK(dns_transactions_.size() < max_dns_transactions_); - - request->request_net_log().AddEvent( - NetLog::TYPE_ASYNC_HOST_RESOLVER_CREATE_DNS_TRANSACTION, NULL); - - requestlist_map_[request->key()].push_back(request); - scoped_ptr<DnsTransaction> transaction(client_->CreateTransaction( - request->key().first, - request->key().second, - base::Bind(&AsyncHostResolver::OnDnsTransactionComplete, - base::Unretained(this)), - request->request_net_log())); - int rv = transaction->Start(); - if (rv == ERR_IO_PENDING) - dns_transactions_.push_back(transaction.release()); - return rv; -} - -int AsyncHostResolver::Enqueue(Request* request) { - Request* evicted_request = Insert(request); - int rv = ERR_HOST_RESOLVER_QUEUE_TOO_LARGE; - if (evicted_request == request) - return rv; - if (evicted_request != NULL) { - evicted_request->OnAsyncComplete(rv, AddressList()); - delete evicted_request; - } - return ERR_IO_PENDING; -} - -AsyncHostResolver::Request* AsyncHostResolver::Insert(Request* request) { - pending_requests_[request->priority()].push_back(request); - if (GetNumPending() > max_pending_requests_) { - Request* req = RemoveLowest(); - DCHECK(req); - return req; - } - return NULL; -} - -size_t AsyncHostResolver::GetNumPending() const { - size_t num_pending = 0; - for (size_t i = 0; i < arraysize(pending_requests_); ++i) - num_pending += pending_requests_[i].size(); - return num_pending; -} - -AsyncHostResolver::Request* AsyncHostResolver::RemoveLowest() { - for (int i = static_cast<int>(arraysize(pending_requests_)) - 1; - i >= 0; --i) { - RequestList& requests = pending_requests_[i]; - if (!requests.empty()) { - Request* request = requests.front(); - requests.pop_front(); - return request; - } - } - return NULL; -} - -AsyncHostResolver::Request* AsyncHostResolver::RemoveHighest() { - for (size_t i = 0; i < arraysize(pending_requests_) - 1; ++i) { - RequestList& requests = pending_requests_[i]; - if (!requests.empty()) { - Request* request = requests.front(); - requests.pop_front(); - return request; - } - } - return NULL; -} - -void AsyncHostResolver::ProcessPending() { - Request* request = RemoveHighest(); - if (!request) - return; - for (size_t i = 0; i < arraysize(pending_requests_); ++i) { - RequestList& requests = pending_requests_[i]; - RequestList::iterator it = requests.begin(); - while (it != requests.end()) { - if (request->key() == (*it)->key()) { - requestlist_map_[request->key()].push_back(*it); - it = requests.erase(it); - } else { - ++it; - } - } - } - StartNewDnsRequestFor(request); -} - -} // namespace net diff --git a/net/dns/async_host_resolver.h b/net/dns/async_host_resolver.h deleted file mode 100644 index 23d9808..0000000 --- a/net/dns/async_host_resolver.h +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef NET_DNS_ASYNC_HOST_RESOLVER_H_ -#define NET_DNS_ASYNC_HOST_RESOLVER_H_ -#pragma once - -#include <list> -#include <map> -#include <string> -#include <utility> - -#include "base/threading/non_thread_safe.h" -#include "net/base/address_family.h" -#include "net/base/host_cache.h" -#include "net/base/host_resolver.h" -#include "net/base/ip_endpoint.h" -#include "net/base/net_log.h" -#include "net/dns/dns_transaction.h" - -namespace net { - -class NET_EXPORT AsyncHostResolver - : public HostResolver, - NON_EXPORTED_BASE(public base::NonThreadSafe) { - public: - AsyncHostResolver(size_t max_dns_requests, - size_t max_pending_requests, - HostCache* cache, - scoped_ptr<DnsTransactionFactory> client, - NetLog* net_log); - virtual ~AsyncHostResolver(); - - // HostResolver interface - virtual int Resolve(const RequestInfo& info, - AddressList* addresses, - const CompletionCallback& callback, - RequestHandle* out_req, - const BoundNetLog& source_net_log) OVERRIDE; - virtual int ResolveFromCache(const RequestInfo& info, - AddressList* addresses, - const BoundNetLog& source_net_log) OVERRIDE; - virtual void CancelRequest(RequestHandle req_handle) OVERRIDE; - virtual void SetDefaultAddressFamily(AddressFamily address_family) OVERRIDE; - virtual AddressFamily GetDefaultAddressFamily() const OVERRIDE; - virtual HostCache* GetHostCache() OVERRIDE; - - void OnDnsTransactionComplete(DnsTransaction* transaction, - int result, - const DnsResponse* response); - - private: - FRIEND_TEST_ALL_PREFIXES(AsyncHostResolverTest, QueuedLookup); - FRIEND_TEST_ALL_PREFIXES(AsyncHostResolverTest, CancelPendingLookup); - FRIEND_TEST_ALL_PREFIXES(AsyncHostResolverTest, - ResolverDestructionCancelsLookups); - FRIEND_TEST_ALL_PREFIXES(AsyncHostResolverTest, - OverflowQueueWithLowPriorityLookup); - FRIEND_TEST_ALL_PREFIXES(AsyncHostResolverTest, - OverflowQueueWithHighPriorityLookup); - - class Request; - - typedef std::pair<std::string, uint16> Key; - typedef std::list<Request*> RequestList; - typedef std::list<const DnsTransaction*> DnsTransactionList; - typedef std::map<Key, RequestList> KeyRequestListMap; - - // Create a new request for the incoming Resolve() call. - Request* CreateNewRequest(const RequestInfo& info, - const CompletionCallback& callback, - AddressList* addresses, - const BoundNetLog& source_net_log); - - // Called when a request has just been started. - void OnStart(Request* request); - - // Called when a request has just completed (before its callback is run). - void OnFinish(Request* request, int result); - - // Called when a request has been cancelled. - void OnCancel(Request* request); - - // If there is an in-progress transaction for Request->key(), this will - // attach |request| to the respective list. - bool AttachToRequestList(Request* request); - - // Will start a new DNS request for |request|, will insert a new key in - // |requestlist_map_| and append |request| to the respective list. - int StartNewDnsRequestFor(Request* request); - - // Will enqueue |request| in |pending_requests_|. - int Enqueue(Request* request); - - // A helper used by Enqueue to insert |request| into |pending_requests_|. - Request* Insert(Request* request); - - // Returns the number of pending requests. - size_t GetNumPending() const; - - // Removes and returns a pointer to the lowest/highest priority request - // from |pending_requests_|. - Request* RemoveLowest(); - Request* RemoveHighest(); - - // Once a transaction has completed, called to start a new transaction if - // there are pending requests. - void ProcessPending(); - - // Maximum number of concurrent DNS transactions. - size_t max_dns_transactions_; - - // List of current DNS transactions. - DnsTransactionList dns_transactions_; - - // A map from Key to a list of requests waiting for the Key to resolve. - KeyRequestListMap requestlist_map_; - - // Maximum number of pending requests. - size_t max_pending_requests_; - - // Queues based on priority for putting pending requests. - RequestList pending_requests_[NUM_PRIORITIES]; - - // Cache of host resolution results. - scoped_ptr<HostCache> cache_; - - scoped_ptr<DnsTransactionFactory> client_; - - NetLog* net_log_; - - DISALLOW_COPY_AND_ASSIGN(AsyncHostResolver); -}; - -} // namespace net - -#endif // NET_DNS_ASYNC_HOST_RESOLVER_H_ diff --git a/net/dns/async_host_resolver_unittest.cc b/net/dns/async_host_resolver_unittest.cc deleted file mode 100644 index be57d49..0000000 --- a/net/dns/async_host_resolver_unittest.cc +++ /dev/null @@ -1,544 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "net/dns/async_host_resolver.h" - -#include "base/bind.h" -#include "base/memory/scoped_ptr.h" -#include "base/message_loop.h" -#include "base/stl_util.h" -#include "net/base/host_cache.h" -#include "net/base/net_errors.h" -#include "net/base/net_log.h" -#include "net/base/sys_addrinfo.h" -#include "net/base/test_completion_callback.h" -#include "net/dns/dns_query.h" -#include "net/dns/dns_response.h" -#include "net/dns/dns_test_util.h" -#include "net/dns/dns_transaction.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace net { - -namespace { - -const int kPortNum = 80; -const size_t kMaxTransactions = 2; -const size_t kMaxPendingRequests = 1; - -void VerifyAddressList(const std::vector<const char*>& ip_addresses, - int port, - const AddressList& addrlist) { - ASSERT_LT(0u, ip_addresses.size()); - ASSERT_NE(static_cast<addrinfo*>(NULL), addrlist.head()); - - IPAddressNumber ip_number; - const struct addrinfo* ainfo = addrlist.head(); - for (std::vector<const char*>::const_iterator i = ip_addresses.begin(); - i != ip_addresses.end(); ++i, ainfo = ainfo->ai_next) { - ASSERT_NE(static_cast<addrinfo*>(NULL), ainfo); - EXPECT_EQ(sizeof(struct sockaddr_in), - static_cast<size_t>(ainfo->ai_addrlen)); - - const struct sockaddr* sa = ainfo->ai_addr; - const struct sockaddr_in* sa_in = (const struct sockaddr_in*) sa; - EXPECT_TRUE(htons(port) == sa_in->sin_port); - EXPECT_STREQ(*i, NetAddressToString(sa, ainfo->ai_addrlen).c_str()); - } - ASSERT_EQ(static_cast<addrinfo*>(NULL), ainfo); -} - -class MockTransactionFactory : public DnsTransactionFactory, - public base::SupportsWeakPtr<MockTransactionFactory> { - public: - // Using WeakPtr to support cancellation. All MockTransactions succeed unless - // cancelled or MockTransactionFactory is destroyed. - class MockTransaction : public DnsTransaction, - public base::SupportsWeakPtr<MockTransaction> { - public: - MockTransaction(const std::string& hostname, - uint16 qtype, - const DnsTransactionFactory::CallbackType& callback, - const base::WeakPtr<MockTransactionFactory>& factory) - : hostname_(hostname), - qtype_(qtype), - callback_(callback), - started_(false), - factory_(factory) { - EXPECT_FALSE(started_); - started_ = true; - MessageLoop::current()->PostTask( - FROM_HERE, - base::Bind(&MockTransaction::Finish, AsWeakPtr())); - } - - virtual const std::string& GetHostname() const OVERRIDE { - return hostname_; - } - - virtual uint16 GetType() const OVERRIDE { - return qtype_; - } - - virtual int Start() OVERRIDE { - return ERR_IO_PENDING; - } - - private: - void Finish() { - if (!factory_) { - callback_.Run(this, ERR_DNS_SERVER_FAILED, NULL); - return; - } - callback_.Run(this, - OK, - factory_->responses_[Key(GetHostname(), GetType())]); - } - - const std::string hostname_; - const uint16 qtype_; - DnsTransactionFactory::CallbackType callback_; - bool started_; - const base::WeakPtr<MockTransactionFactory> factory_; - }; - - typedef std::pair<std::string, uint16> Key; - - MockTransactionFactory() : num_requests_(0) {} - ~MockTransactionFactory() { - STLDeleteValues(&responses_); - } - - scoped_ptr<DnsTransaction> CreateTransaction( - const std::string& qname, - uint16 qtype, - const DnsTransactionFactory::CallbackType& callback, - const BoundNetLog&) { - ++num_requests_; - return scoped_ptr<DnsTransaction>( - new MockTransaction(qname, qtype, callback, AsWeakPtr())); - } - - void AddResponse(const std::string& name, uint8 type, DnsResponse* response) { - responses_[MockTransactionFactory::Key(name, type)] = response; - } - - int num_requests() const { return num_requests_; } - - private: - int num_requests_; - std::map<Key, DnsResponse*> responses_; -}; - -} // namespace - - -// The following fixture sets up an environment for four different lookups -// with their data defined in dns_test_util.h. All tests make use of these -// predefined variables instead of each defining their own, to avoid -// boilerplate code in every test. Assuming every coming query is for a -// distinct hostname, as |kMaxTransactions| is set to 2 and -// |kMaxPendingRequests| is set to 1, first two queries start immediately -// and the next one is sent to pending queue; as a result, the next query -// should either fail itself or cause the pending query to fail depending -// on its priority. -class AsyncHostResolverTest : public testing::Test { - public: - AsyncHostResolverTest() - : info0_(HostPortPair(kT0HostName, kPortNum)), - info1_(HostPortPair(kT1HostName, kPortNum)), - info2_(HostPortPair(kT2HostName, kPortNum)), - info3_(HostPortPair(kT3HostName, kPortNum)), - ip_addresses0_(kT0IpAddresses, - kT0IpAddresses + arraysize(kT0IpAddresses)), - ip_addresses1_(kT1IpAddresses, - kT1IpAddresses + arraysize(kT1IpAddresses)), - ip_addresses2_(kT2IpAddresses, - kT2IpAddresses + arraysize(kT2IpAddresses)), - ip_addresses3_(kT3IpAddresses, - kT3IpAddresses + arraysize(kT3IpAddresses)) { - // AF_INET only for now. - info0_.set_address_family(ADDRESS_FAMILY_IPV4); - info1_.set_address_family(ADDRESS_FAMILY_IPV4); - info2_.set_address_family(ADDRESS_FAMILY_IPV4); - info3_.set_address_family(ADDRESS_FAMILY_IPV4); - - client_ = new MockTransactionFactory(); - - client_->AddResponse(kT0HostName, kT0Qtype, - new DnsResponse(reinterpret_cast<const char*>(kT0ResponseDatagram), - arraysize(kT0ResponseDatagram), - arraysize(kT0QueryDatagram))); - - client_->AddResponse(kT1HostName, kT1Qtype, - new DnsResponse(reinterpret_cast<const char*>(kT1ResponseDatagram), - arraysize(kT1ResponseDatagram), - arraysize(kT1QueryDatagram))); - - client_->AddResponse(kT2HostName, kT2Qtype, - new DnsResponse(reinterpret_cast<const char*>(kT2ResponseDatagram), - arraysize(kT2ResponseDatagram), - arraysize(kT2QueryDatagram))); - - client_->AddResponse(kT3HostName, kT3Qtype, - new DnsResponse(reinterpret_cast<const char*>(kT3ResponseDatagram), - arraysize(kT3ResponseDatagram), - arraysize(kT3QueryDatagram))); - - resolver_.reset( - new AsyncHostResolver(kMaxTransactions, kMaxPendingRequests, - HostCache::CreateDefaultCache(), - scoped_ptr<DnsTransactionFactory>(client_), NULL)); - } - - protected: - AddressList addrlist0_, addrlist1_, addrlist2_, addrlist3_; - HostResolver::RequestInfo info0_, info1_, info2_, info3_; - std::vector<const char*> ip_addresses0_, ip_addresses1_, - ip_addresses2_, ip_addresses3_; - scoped_ptr<HostResolver> resolver_; - MockTransactionFactory* client_; // Owned by the AsyncHostResolver. - TestCompletionCallback callback0_, callback1_, callback2_, callback3_; -}; - -TEST_F(AsyncHostResolverTest, EmptyHostLookup) { - info0_.set_host_port_pair(HostPortPair("", kPortNum)); - int rv = resolver_->Resolve(info0_, &addrlist0_, callback0_.callback(), NULL, - BoundNetLog()); - EXPECT_EQ(ERR_NAME_NOT_RESOLVED, rv); -} - -TEST_F(AsyncHostResolverTest, IPv4LiteralLookup) { - const char* kIPLiteral = "192.168.1.2"; - info0_.set_host_port_pair(HostPortPair(kIPLiteral, kPortNum)); - info0_.set_host_resolver_flags(HOST_RESOLVER_CANONNAME); - int rv = resolver_->Resolve(info0_, &addrlist0_, callback0_.callback(), NULL, - BoundNetLog()); - EXPECT_EQ(OK, rv); - std::vector<const char*> ip_addresses(1, kIPLiteral); - VerifyAddressList(ip_addresses, kPortNum, addrlist0_); - EXPECT_STREQ(kIPLiteral, addrlist0_.head()->ai_canonname); -} - -TEST_F(AsyncHostResolverTest, IPv6LiteralLookup) { - info0_.set_host_port_pair(HostPortPair("2001:db8:0::42", kPortNum)); - int rv = resolver_->Resolve(info0_, &addrlist0_, callback0_.callback(), NULL, - BoundNetLog()); - // When support for IPv6 is added, this should succeed. - EXPECT_EQ(ERR_NAME_NOT_RESOLVED, rv); -} - -TEST_F(AsyncHostResolverTest, CachedLookup) { - int rv = resolver_->ResolveFromCache(info0_, &addrlist0_, BoundNetLog()); - EXPECT_EQ(ERR_DNS_CACHE_MISS, rv); - - // Cache the result of |info0_| lookup. - rv = resolver_->Resolve(info0_, &addrlist0_, callback0_.callback(), NULL, - BoundNetLog()); - EXPECT_EQ(ERR_IO_PENDING, rv); - rv = callback0_.WaitForResult(); - EXPECT_EQ(OK, rv); - VerifyAddressList(ip_addresses0_, kPortNum, addrlist0_); - - // Now lookup |info0_| from cache only, store results in |addrlist1_|, - // should succeed synchronously. - rv = resolver_->ResolveFromCache(info0_, &addrlist1_, BoundNetLog()); - EXPECT_EQ(OK, rv); - VerifyAddressList(ip_addresses0_, kPortNum, addrlist1_); -} - -// TODO(szym): This tests DnsTransaction not AsyncHostResolver. Remove or move -// to dns_transaction_unittest.cc -TEST_F(AsyncHostResolverTest, DISABLED_InvalidHostNameLookup) { - const std::string kHostName1(64, 'a'); - info0_.set_host_port_pair(HostPortPair(kHostName1, kPortNum)); - int rv = resolver_->Resolve(info0_, &addrlist0_, callback0_.callback(), NULL, - BoundNetLog()); - EXPECT_EQ(ERR_INVALID_ARGUMENT, rv); - - const std::string kHostName2(4097, 'b'); - info0_.set_host_port_pair(HostPortPair(kHostName2, kPortNum)); - rv = resolver_->Resolve(info0_, &addrlist0_, callback0_.callback(), NULL, - BoundNetLog()); - EXPECT_EQ(ERR_INVALID_ARGUMENT, rv); -} - -TEST_F(AsyncHostResolverTest, Lookup) { - int rv = resolver_->Resolve(info0_, &addrlist0_, callback0_.callback(), NULL, - BoundNetLog()); - EXPECT_EQ(ERR_IO_PENDING, rv); - rv = callback0_.WaitForResult(); - EXPECT_EQ(OK, rv); - VerifyAddressList(ip_addresses0_, kPortNum, addrlist0_); -} - -TEST_F(AsyncHostResolverTest, ConcurrentLookup) { - int rv0 = resolver_->Resolve(info0_, &addrlist0_, callback0_.callback(), NULL, - BoundNetLog()); - int rv1 = resolver_->Resolve(info1_, &addrlist1_, callback1_.callback(), NULL, - BoundNetLog()); - int rv2 = resolver_->Resolve(info2_, &addrlist2_, callback2_.callback(), NULL, - BoundNetLog()); - EXPECT_EQ(ERR_IO_PENDING, rv0); - EXPECT_EQ(ERR_IO_PENDING, rv1); - EXPECT_EQ(ERR_IO_PENDING, rv2); - - rv0 = callback0_.WaitForResult(); - EXPECT_EQ(OK, rv0); - VerifyAddressList(ip_addresses0_, kPortNum, addrlist0_); - - rv1 = callback1_.WaitForResult(); - EXPECT_EQ(OK, rv1); - VerifyAddressList(ip_addresses1_, kPortNum, addrlist1_); - - rv2 = callback2_.WaitForResult(); - EXPECT_EQ(OK, rv2); - VerifyAddressList(ip_addresses2_, kPortNum, addrlist2_); - - EXPECT_EQ(3, client_->num_requests()); -} - -TEST_F(AsyncHostResolverTest, SameHostLookupsConsumeSingleTransaction) { - // We pass the info0_ to all requests. - int rv0 = resolver_->Resolve(info0_, &addrlist0_, callback0_.callback(), NULL, - BoundNetLog()); - int rv1 = resolver_->Resolve(info0_, &addrlist1_, callback1_.callback(), NULL, - BoundNetLog()); - int rv2 = resolver_->Resolve(info0_, &addrlist2_, callback2_.callback(), NULL, - BoundNetLog()); - EXPECT_EQ(ERR_IO_PENDING, rv0); - EXPECT_EQ(ERR_IO_PENDING, rv1); - EXPECT_EQ(ERR_IO_PENDING, rv2); - - rv0 = callback0_.WaitForResult(); - EXPECT_EQ(OK, rv0); - VerifyAddressList(ip_addresses0_, kPortNum, addrlist0_); - - rv1 = callback1_.WaitForResult(); - EXPECT_EQ(OK, rv1); - VerifyAddressList(ip_addresses0_, kPortNum, addrlist1_); - - rv2 = callback2_.WaitForResult(); - EXPECT_EQ(OK, rv2); - VerifyAddressList(ip_addresses0_, kPortNum, addrlist2_); - - // Although we have three lookups, a single UDP socket was used. - EXPECT_EQ(1, client_->num_requests()); -} - -TEST_F(AsyncHostResolverTest, CancelLookup) { - HostResolver::RequestHandle req0 = NULL, req2 = NULL; - int rv0 = resolver_->Resolve(info0_, &addrlist0_, callback0_.callback(), - &req0, BoundNetLog()); - int rv1 = resolver_->Resolve(info1_, &addrlist1_, callback1_.callback(), NULL, - BoundNetLog()); - int rv2 = resolver_->Resolve(info2_, &addrlist2_, callback2_.callback(), - &req2, BoundNetLog()); - EXPECT_EQ(ERR_IO_PENDING, rv0); - EXPECT_EQ(ERR_IO_PENDING, rv1); - EXPECT_EQ(ERR_IO_PENDING, rv2); - - resolver_->CancelRequest(req0); - resolver_->CancelRequest(req2); - - MessageLoop::current()->RunAllPending(); - - EXPECT_FALSE(callback0_.have_result()); - EXPECT_FALSE(callback2_.have_result()); - - rv1 = callback1_.WaitForResult(); - EXPECT_EQ(OK, rv1); - VerifyAddressList(ip_addresses1_, kPortNum, addrlist1_); -} - -// Tests the following scenario: start two resolutions for the same host, -// cancel one of them, make sure that the other one completes. -TEST_F(AsyncHostResolverTest, CancelSameHostLookup) { - HostResolver::RequestHandle req0 = NULL; - - // Pass the info0_ to both requests. - int rv0 = resolver_->Resolve(info0_, &addrlist0_, callback0_.callback(), - &req0, BoundNetLog()); - int rv1 = resolver_->Resolve(info0_, &addrlist1_, callback1_.callback(), NULL, - BoundNetLog()); - EXPECT_EQ(ERR_IO_PENDING, rv0); - EXPECT_EQ(ERR_IO_PENDING, rv1); - - resolver_->CancelRequest(req0); - MessageLoop::current()->RunAllPending(); - EXPECT_FALSE(callback0_.have_result()); - - rv1 = callback1_.WaitForResult(); - EXPECT_EQ(OK, rv1); - VerifyAddressList(ip_addresses0_, kPortNum, addrlist1_); - - EXPECT_EQ(1, client_->num_requests()); -} - -// Test that a queued lookup completes. -TEST_F(AsyncHostResolverTest, QueuedLookup) { - // kMaxTransactions is 2, thus the following requests consume all - // available transactions. - int rv0 = resolver_->Resolve(info0_, &addrlist0_, callback0_.callback(), NULL, - BoundNetLog()); - int rv1 = resolver_->Resolve(info1_, &addrlist1_, callback1_.callback(), NULL, - BoundNetLog()); - EXPECT_EQ(ERR_IO_PENDING, rv0); - EXPECT_EQ(ERR_IO_PENDING, rv1); - - // The following request will end up in queue. - int rv2 = resolver_->Resolve(info2_, &addrlist2_, callback2_.callback(), NULL, - BoundNetLog()); - EXPECT_EQ(ERR_IO_PENDING, rv2); - EXPECT_EQ(1u, - static_cast<AsyncHostResolver*>(resolver_.get())->GetNumPending()); - - // Make sure all requests complete. - rv0 = callback0_.WaitForResult(); - EXPECT_EQ(OK, rv0); - VerifyAddressList(ip_addresses0_, kPortNum, addrlist0_); - - rv1 = callback1_.WaitForResult(); - EXPECT_EQ(OK, rv1); - VerifyAddressList(ip_addresses1_, kPortNum, addrlist1_); - - rv2 = callback2_.WaitForResult(); - EXPECT_EQ(OK, rv2); - VerifyAddressList(ip_addresses2_, kPortNum, addrlist2_); -} - -// Test that cancelling a queued lookup works. -TEST_F(AsyncHostResolverTest, CancelPendingLookup) { - // kMaxTransactions is 2, thus the following requests consume all - // available transactions. - int rv0 = resolver_->Resolve(info0_, &addrlist0_, callback0_.callback(), NULL, - BoundNetLog()); - int rv1 = resolver_->Resolve(info1_, &addrlist1_, callback1_.callback(), NULL, - BoundNetLog()); - EXPECT_EQ(ERR_IO_PENDING, rv0); - EXPECT_EQ(ERR_IO_PENDING, rv1); - - // The following request will end up in queue. - HostResolver::RequestHandle req2 = NULL; - int rv2 = resolver_->Resolve(info2_, &addrlist2_, callback2_.callback(), - &req2, BoundNetLog()); - EXPECT_EQ(ERR_IO_PENDING, rv2); - EXPECT_EQ(1u, - static_cast<AsyncHostResolver*>(resolver_.get())->GetNumPending()); - - resolver_->CancelRequest(req2); - - // Make sure first two requests complete while the cancelled one doesn't. - MessageLoop::current()->RunAllPending(); - EXPECT_FALSE(callback2_.have_result()); - - rv0 = callback0_.WaitForResult(); - EXPECT_EQ(OK, rv0); - VerifyAddressList(ip_addresses0_, kPortNum, addrlist0_); - - rv1 = callback1_.WaitForResult(); - EXPECT_EQ(OK, rv1); - VerifyAddressList(ip_addresses1_, kPortNum, addrlist1_); -} - -TEST_F(AsyncHostResolverTest, ResolverDestructionCancelsLookups) { - int rv0 = resolver_->Resolve(info0_, &addrlist0_, callback0_.callback(), NULL, - BoundNetLog()); - int rv1 = resolver_->Resolve(info1_, &addrlist1_, callback1_.callback(), NULL, - BoundNetLog()); - // This one is queued. - int rv2 = resolver_->Resolve(info2_, &addrlist2_, callback2_.callback(), NULL, - BoundNetLog()); - EXPECT_EQ(1u, - static_cast<AsyncHostResolver*>(resolver_.get())->GetNumPending()); - - EXPECT_EQ(ERR_IO_PENDING, rv0); - EXPECT_EQ(ERR_IO_PENDING, rv1); - EXPECT_EQ(ERR_IO_PENDING, rv2); - - resolver_.reset(); - - MessageLoop::current()->RunAllPending(); - - EXPECT_FALSE(callback0_.have_result()); - EXPECT_FALSE(callback1_.have_result()); - EXPECT_FALSE(callback2_.have_result()); -} - -// Test that when the number of pending lookups is at max, a new lookup -// with a priority lower than all of those in the queue fails. -TEST_F(AsyncHostResolverTest, OverflowQueueWithLowPriorityLookup) { - int rv0 = resolver_->Resolve(info0_, &addrlist0_, callback0_.callback(), NULL, - BoundNetLog()); - int rv1 = resolver_->Resolve(info1_, &addrlist1_, callback1_.callback(), NULL, - BoundNetLog()); - // This one is queued and fills up the queue since its size is 1. - int rv2 = resolver_->Resolve(info2_, &addrlist2_, callback2_.callback(), NULL, - BoundNetLog()); - EXPECT_EQ(1u, - static_cast<AsyncHostResolver*>(resolver_.get())->GetNumPending()); - - EXPECT_EQ(ERR_IO_PENDING, rv0); - EXPECT_EQ(ERR_IO_PENDING, rv1); - EXPECT_EQ(ERR_IO_PENDING, rv2); - - // This one fails. - info3_.set_priority(LOWEST); - int rv3 = resolver_->Resolve(info3_, &addrlist3_, callback3_.callback(), NULL, - BoundNetLog()); - EXPECT_EQ(ERR_HOST_RESOLVER_QUEUE_TOO_LARGE, rv3); - - MessageLoop::current()->RunAllPending(); - EXPECT_FALSE(callback3_.have_result()); -} - -// Test that when the number of pending lookups is at max, a new lookup -// with a priority higher than any of those in the queue succeeds and -// causes the lowest priority lookup in the queue to fail. -TEST_F(AsyncHostResolverTest, OverflowQueueWithHighPriorityLookup) { - int rv0 = resolver_->Resolve(info0_, &addrlist0_, callback0_.callback(), NULL, - BoundNetLog()); - int rv1 = resolver_->Resolve(info1_, &addrlist1_, callback1_.callback(), NULL, - BoundNetLog()); - - // Next lookup is queued. Since this will be ejected from the queue and - // will not consume a socket from our factory, we are not passing it - // predefined members. - HostResolver::RequestInfo info(HostPortPair("cnn.com", 80)); - info.set_address_family(ADDRESS_FAMILY_IPV4); - AddressList addrlist_fail; - TestCompletionCallback callback_fail; - int rv_fail = resolver_->Resolve(info, &addrlist_fail, - callback_fail.callback(), NULL, - BoundNetLog()); - EXPECT_EQ(1u, - static_cast<AsyncHostResolver*>(resolver_.get())->GetNumPending()); - - EXPECT_EQ(ERR_IO_PENDING, rv0); - EXPECT_EQ(ERR_IO_PENDING, rv1); - EXPECT_EQ(ERR_IO_PENDING, rv_fail); - - // Lookup 2 causes the above to fail, but itself should succeed. - info2_.set_priority(HIGHEST); - int rv2 = resolver_->Resolve(info2_, &addrlist2_, callback2_.callback(), NULL, - BoundNetLog()); - - rv0 = callback0_.WaitForResult(); - EXPECT_EQ(OK, rv0); - VerifyAddressList(ip_addresses0_, kPortNum, addrlist0_); - - rv1 = callback1_.WaitForResult(); - EXPECT_EQ(OK, rv1); - VerifyAddressList(ip_addresses1_, kPortNum, addrlist1_); - - rv_fail = callback_fail.WaitForResult(); - EXPECT_EQ(ERR_HOST_RESOLVER_QUEUE_TOO_LARGE, rv_fail); - EXPECT_EQ(static_cast<addrinfo*>(NULL), addrlist_fail.head()); - - rv2 = callback2_.WaitForResult(); - EXPECT_EQ(OK, rv2); - VerifyAddressList(ip_addresses2_, kPortNum, addrlist2_); -} - -} // namespace net diff --git a/net/dns/dns_config_service.h b/net/dns/dns_config_service.h index b3e151f..51e26b3 100644 --- a/net/dns/dns_config_service.h +++ b/net/dns/dns_config_service.h @@ -12,6 +12,7 @@ #include "base/file_path.h" #include "base/gtest_prod_util.h" +#include "base/memory/scoped_ptr.h" #include "base/observer_list.h" #include "base/threading/non_thread_safe.h" #include "base/time.h" @@ -64,6 +65,7 @@ struct NET_EXPORT_PRIVATE DnsConfig { // Service for watching when the system DNS settings have changed. // Depending on the platform, watches files in /etc/ or win registry. +// This class also serves as the do-nothing mock implementation. class NET_EXPORT_PRIVATE DnsConfigService : NON_EXPORTED_BASE(public base::NonThreadSafe) { public: @@ -78,7 +80,7 @@ class NET_EXPORT_PRIVATE DnsConfigService }; // Creates the platform-specific DnsConfigService. - static DnsConfigService* CreateSystemService(); + static scoped_ptr<DnsConfigService> CreateSystemService(); DnsConfigService(); virtual ~DnsConfigService(); diff --git a/net/dns/dns_config_service_posix.cc b/net/dns/dns_config_service_posix.cc index 18a69cc..67df4e9 100644 --- a/net/dns/dns_config_service_posix.cc +++ b/net/dns/dns_config_service_posix.cc @@ -84,8 +84,8 @@ void DnsConfigServicePosix::Watch() { } // static -DnsConfigService* DnsConfigService::CreateSystemService() { - return new DnsConfigServicePosix(); +scoped_ptr<DnsConfigService> DnsConfigService::CreateSystemService() { + return scoped_ptr<DnsConfigService>(new DnsConfigServicePosix()); } #if !defined(OS_ANDROID) diff --git a/net/dns/dns_config_service_posix_unittest.cc b/net/dns/dns_config_service_posix_unittest.cc index 58c71ac..f1ff015 100644 --- a/net/dns/dns_config_service_posix_unittest.cc +++ b/net/dns/dns_config_service_posix_unittest.cc @@ -110,7 +110,7 @@ void CloseResState(res_state res) { } // namespace -TEST(DnsConfigTest, ResolverConfigConvertAndEquals) { +TEST(DnsConfigServicePosixTest, ConvertResStateToDnsConfig) { struct __res_state res[2]; DnsConfig config[2]; for (unsigned i = 0; i < 2; ++i) { diff --git a/net/dns/dns_config_service_win.cc b/net/dns/dns_config_service_win.cc index d4a615f..14bb773 100644 --- a/net/dns/dns_config_service_win.cc +++ b/net/dns/dns_config_service_win.cc @@ -307,9 +307,6 @@ class DnsConfigServiceWin::ConfigReader : public SerialWorker { bool StartWatch() { DCHECK(loop()->BelongsToCurrentThread()); - // This is done only once per lifetime so open the keys on this thread. - base::ThreadRestrictions::ScopedAllowIO allow_io; - base::Closure callback = base::Bind(&SerialWorker::WorkNow, base::Unretained(this)); @@ -448,6 +445,12 @@ DnsConfigServiceWin::~DnsConfigServiceWin() { void DnsConfigServiceWin::Watch() { DCHECK(CalledOnValidThread()); + + // This is done only once per lifetime so open the keys and file watcher + // handles on this thread. + // TODO(szym): Should/can this be avoided? http://crbug.com/114223 + base::ThreadRestrictions::ScopedAllowIO allow_io; + bool started = config_reader_->StartWatch(); // TODO(szym): handle possible failure DCHECK(started); @@ -461,8 +464,8 @@ void DnsConfigServiceWin::Watch() { } // static -DnsConfigService* DnsConfigService::CreateSystemService() { - return new DnsConfigServiceWin(); +scoped_ptr<DnsConfigService> DnsConfigService::CreateSystemService() { + return scoped_ptr<DnsConfigService>(new DnsConfigServiceWin()); } } // namespace net diff --git a/net/dns/dns_response.cc b/net/dns/dns_response.cc index 760bd5b..9518095 100644 --- a/net/dns/dns_response.cc +++ b/net/dns/dns_response.cc @@ -4,7 +4,9 @@ #include "net/dns/dns_response.h" +#include "base/string_util.h" #include "base/sys_byteorder.h" +#include "net/base/address_list.h" #include "net/base/big_endian.h" #include "net/base/dns_util.h" #include "net/base/io_buffer.h" @@ -32,7 +34,8 @@ DnsRecordParser::DnsRecordParser(const void* packet, DCHECK_LE(offset, length); } -int DnsRecordParser::ParseName(const void* const vpos, std::string* out) const { +unsigned DnsRecordParser::ReadName(const void* const vpos, + std::string* out) const { const char* const pos = reinterpret_cast<const char*>(vpos); DCHECK(packet_); DCHECK_LE(packet_, pos); @@ -41,9 +44,9 @@ int DnsRecordParser::ParseName(const void* const vpos, std::string* out) const { const char* p = pos; const char* end = packet_ + length_; // Count number of seen bytes to detect loops. - size_t seen = 0; + unsigned seen = 0; // Remember how many bytes were consumed before first jump. - size_t consumed = 0; + unsigned consumed = 0; if (pos >= end) return 0; @@ -54,7 +57,7 @@ int DnsRecordParser::ParseName(const void* const vpos, std::string* out) const { } for (;;) { - // The two couple of bits of the length give the type of the length. It's + // The first two bits of the length give the type of the length. It's // either a direct length or a pointer to the remainder of the name. switch (*p & dns_protocol::kLabelMask) { case dns_protocol::kLabelPointer: { @@ -105,9 +108,9 @@ int DnsRecordParser::ParseName(const void* const vpos, std::string* out) const { } } -bool DnsRecordParser::ParseRecord(DnsResourceRecord* out) { +bool DnsRecordParser::ReadRecord(DnsResourceRecord* out) { DCHECK(packet_); - size_t consumed = ParseName(cur_, &out->name); + size_t consumed = ReadName(cur_, &out->name); if (!consumed) return false; BigEndianReader reader(cur_ + consumed, @@ -182,7 +185,7 @@ uint8 DnsResponse::rcode() const { return ntohs(header()->flags) & dns_protocol::kRcodeMask; } -int DnsResponse::answer_count() const { +unsigned DnsResponse::answer_count() const { DCHECK(parser_.IsValid()); return ntohs(header()->ancount); } @@ -220,4 +223,71 @@ const dns_protocol::Header* DnsResponse::header() const { return reinterpret_cast<const dns_protocol::Header*>(io_buffer_->data()); } +DnsResponse::Result DnsResponse::ParseToAddressList( + AddressList* addr_list, + base::TimeDelta* ttl) const { + DCHECK(IsValid()); + // DnsTransaction already verified that |response| matches the issued query. + // We still need to determine if there is a valid chain of CNAMEs from the + // query name to the RR owner name. + // We err on the side of caution with the assumption that if we are too picky, + // we can always fall back to the system getaddrinfo. + + // Expected owner of record. No trailing dot. + std::string expected_name = GetDottedName(); + + uint16 expected_type = qtype(); + DCHECK(expected_type == dns_protocol::kTypeA || + expected_type == dns_protocol::kTypeAAAA); + + size_t expected_size = (expected_type == dns_protocol::kTypeAAAA) + ? kIPv6AddressSize : kIPv4AddressSize; + + uint32 cname_ttl_sec = kuint32max; + uint32 addr_ttl_sec = kuint32max; + IPAddressList ip_addresses; + DnsRecordParser parser = Parser(); + DnsResourceRecord record; + unsigned ancount = answer_count(); + for (unsigned i = 0; i < ancount; ++i) { + if (!parser.ReadRecord(&record)) + return DNS_MALFORMED_RESPONSE; + + if (base::strcasecmp(record.name.c_str(), expected_name.c_str()) != 0) + return DNS_NAME_MISMATCH; + if (record.type == dns_protocol::kTypeCNAME) { + // Following the CNAME chain, only if no addresses seen. + if (!ip_addresses.empty()) + return DNS_CNAME_AFTER_ADDRESS; + + if (record.rdata.size() != + parser.ReadName(record.rdata.begin(), &expected_name)) + return DNS_MALFORMED_CNAME; + + cname_ttl_sec = std::min(cname_ttl_sec, record.ttl); + } else if (record.type == expected_type) { + if (record.rdata.size() != expected_size) + return DNS_SIZE_MISMATCH; + if (ip_addresses.empty()) { + addr_ttl_sec = record.ttl; + } else { + if (addr_ttl_sec != record.ttl) + return DNS_ADDRESS_TTL_MISMATCH; + } + ip_addresses.push_back(IPAddressNumber(record.rdata.begin(), + record.rdata.end())); + } + } + + if (ip_addresses.empty()) + return DNS_NO_ADDRESSES; + + // getcanonname in eglibc returns the first owner name of an A or AAAA RR. + // If the response passed all the checks so far, then |expected_name| is it. + *addr_list = AddressList::CreateFromIPAddressList(ip_addresses, + expected_name); + *ttl = base::TimeDelta::FromSeconds(std::min(cname_ttl_sec, addr_ttl_sec)); + return DNS_SUCCESS; +} + } // namespace net diff --git a/net/dns/dns_response.h b/net/dns/dns_response.h index 764517a..4866045 100644 --- a/net/dns/dns_response.h +++ b/net/dns/dns_response.h @@ -11,11 +11,13 @@ #include "base/basictypes.h" #include "base/memory/ref_counted.h" #include "base/string_piece.h" +#include "base/time.h" #include "net/base/net_export.h" #include "net/base/net_util.h" namespace net { +class AddressList; class DnsQuery; class IOBufferWithSize; @@ -61,10 +63,10 @@ class NET_EXPORT_PRIVATE DnsRecordParser { // This is exposed to allow parsing compressed names within RRDATA for TYPEs // such as NS, CNAME, PTR, MX, SOA. // See RFC 1035 section 4.1.4. - int ParseName(const void* pos, std::string* out) const; + unsigned ReadName(const void* pos, std::string* out) const; - // Parses the next resource record. Returns true if succeeded. - bool ParseRecord(DnsResourceRecord* record); + // Parses the next resource record into |record|. Returns true if succeeded. + bool ReadRecord(DnsResourceRecord* record); private: const char* packet_; @@ -78,6 +80,20 @@ class NET_EXPORT_PRIVATE DnsRecordParser { // position the RR parser. class NET_EXPORT_PRIVATE DnsResponse { public: + // Possible results from ParseToAddressList + enum Result { + DNS_SUCCESS = 0, + DNS_MALFORMED_RESPONSE, // DnsRecordParser failed before the end of + // packet. + DNS_MALFORMED_CNAME, // Could not parse CNAME out of RRDATA. + DNS_NAME_MISMATCH, // Got an address but no ordered chain of CNAMEs + // leads there. + DNS_SIZE_MISMATCH, // Got an address but size does not match. + DNS_CNAME_AFTER_ADDRESS, // Found CNAME after an address record. + DNS_ADDRESS_TTL_MISMATCH, // TTL of all address records are not identical. + DNS_NO_ADDRESSES, // No address records found. + }; + // Constructs an object with an IOBuffer large enough to read // one byte more than largest possible response, to detect malformed // responses. @@ -102,7 +118,7 @@ class NET_EXPORT_PRIVATE DnsResponse { // Accessors for the header. uint16 flags() const; // excluding rcode uint8 rcode() const; - int answer_count() const; + unsigned answer_count() const; // Accessors to the question. The qname is unparsed. base::StringPiece qname() const; @@ -116,6 +132,10 @@ class NET_EXPORT_PRIVATE DnsResponse { // This operation is idempotent. DnsRecordParser Parser() const; + // Extracts an AddressList from this response. Returns SUCCESS if succeeded. + // Otherwise returns a detailed error number. + Result ParseToAddressList(AddressList* addr_list, base::TimeDelta* ttl) const; + private: // Convenience for header access. const dns_protocol::Header* header() const; diff --git a/net/dns/dns_response_unittest.cc b/net/dns/dns_response_unittest.cc index 41f3a72..6cbf6a1 100644 --- a/net/dns/dns_response_unittest.cc +++ b/net/dns/dns_response_unittest.cc @@ -4,9 +4,14 @@ #include "net/dns/dns_response.h" +#include "base/time.h" +#include "net/base/address_list.h" #include "net/base/io_buffer.h" +#include "net/base/net_util.h" +#include "net/base/sys_addrinfo.h" #include "net/dns/dns_protocol.h" #include "net/dns/dns_query.h" +#include "net/dns/dns_test_util.h" #include "testing/gtest/include/gtest/gtest.h" namespace net { @@ -24,7 +29,7 @@ TEST(DnsRecordParserTest, Constructor) { EXPECT_TRUE(DnsRecordParser(data, 1, 1).AtEnd()); } -TEST(DnsRecordParserTest, ParseName) { +TEST(DnsRecordParserTest, ReadName) { const uint8 data[] = { // all labels "foo.example.com" 0x03, 'f', 'o', 'o', @@ -46,31 +51,31 @@ TEST(DnsRecordParserTest, ParseName) { DnsRecordParser parser(data, sizeof(data), 0); ASSERT_TRUE(parser.IsValid()); - EXPECT_EQ(0x11, parser.ParseName(data + 0x00, &out)); + EXPECT_EQ(0x11u, parser.ReadName(data + 0x00, &out)); EXPECT_EQ("foo.example.com", out); // Check that the last "." is never stored. out.clear(); - EXPECT_EQ(0x1, parser.ParseName(data + 0x10, &out)); + EXPECT_EQ(0x1u, parser.ReadName(data + 0x10, &out)); EXPECT_EQ("", out); out.clear(); - EXPECT_EQ(0x6, parser.ParseName(data + 0x11, &out)); + EXPECT_EQ(0x6u, parser.ReadName(data + 0x11, &out)); EXPECT_EQ("bar.example.com", out); out.clear(); - EXPECT_EQ(0x2, parser.ParseName(data + 0x17, &out)); + EXPECT_EQ(0x2u, parser.ReadName(data + 0x17, &out)); EXPECT_EQ("bar.example.com", out); // Parse name without storing it. - EXPECT_EQ(0x11, parser.ParseName(data + 0x00, NULL)); - EXPECT_EQ(0x1, parser.ParseName(data + 0x10, NULL)); - EXPECT_EQ(0x6, parser.ParseName(data + 0x11, NULL)); - EXPECT_EQ(0x2, parser.ParseName(data + 0x17, NULL)); + EXPECT_EQ(0x11u, parser.ReadName(data + 0x00, NULL)); + EXPECT_EQ(0x1u, parser.ReadName(data + 0x10, NULL)); + EXPECT_EQ(0x6u, parser.ReadName(data + 0x11, NULL)); + EXPECT_EQ(0x2u, parser.ReadName(data + 0x17, NULL)); // Check that it works even if initial position is different. parser = DnsRecordParser(data, sizeof(data), 0x12); - EXPECT_EQ(0x6, parser.ParseName(data + 0x11, NULL)); + EXPECT_EQ(0x6u, parser.ReadName(data + 0x11, NULL)); } -TEST(DnsRecordParserTest, ParseNameFail) { +TEST(DnsRecordParserTest, ReadNameFail) { const uint8 data[] = { // label length beyond packet 0x30, 'x', 'x', @@ -90,15 +95,15 @@ TEST(DnsRecordParserTest, ParseNameFail) { ASSERT_TRUE(parser.IsValid()); std::string out; - EXPECT_EQ(0, parser.ParseName(data + 0x00, &out)); - EXPECT_EQ(0, parser.ParseName(data + 0x04, &out)); - EXPECT_EQ(0, parser.ParseName(data + 0x08, &out)); - EXPECT_EQ(0, parser.ParseName(data + 0x0a, &out)); - EXPECT_EQ(0, parser.ParseName(data + 0x0c, &out)); - EXPECT_EQ(0, parser.ParseName(data + 0x0e, &out)); + EXPECT_EQ(0u, parser.ReadName(data + 0x00, &out)); + EXPECT_EQ(0u, parser.ReadName(data + 0x04, &out)); + EXPECT_EQ(0u, parser.ReadName(data + 0x08, &out)); + EXPECT_EQ(0u, parser.ReadName(data + 0x0a, &out)); + EXPECT_EQ(0u, parser.ReadName(data + 0x0c, &out)); + EXPECT_EQ(0u, parser.ReadName(data + 0x0e, &out)); } -TEST(DnsRecordParserTest, ParseRecord) { +TEST(DnsRecordParserTest, ReadRecord) { const uint8 data[] = { // Type CNAME record. 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', @@ -124,17 +129,17 @@ TEST(DnsRecordParserTest, ParseRecord) { DnsRecordParser parser(data, sizeof(data), 0); DnsResourceRecord record; - EXPECT_TRUE(parser.ParseRecord(&record)); + EXPECT_TRUE(parser.ReadRecord(&record)); EXPECT_EQ("example.com", record.name); EXPECT_EQ(dns_protocol::kTypeCNAME, record.type); EXPECT_EQ(dns_protocol::kClassIN, record.klass); EXPECT_EQ(0x00012474u, record.ttl); EXPECT_EQ(6u, record.rdata.length()); - EXPECT_EQ(6, parser.ParseName(record.rdata.data(), &out)); + EXPECT_EQ(6u, parser.ReadName(record.rdata.data(), &out)); EXPECT_EQ("foo.example.com", out); EXPECT_FALSE(parser.AtEnd()); - EXPECT_TRUE(parser.ParseRecord(&record)); + EXPECT_TRUE(parser.ReadRecord(&record)); EXPECT_EQ("bar.example.com", record.name); EXPECT_EQ(dns_protocol::kTypeA, record.type); EXPECT_EQ(dns_protocol::kClassIN, record.klass); @@ -145,9 +150,9 @@ TEST(DnsRecordParserTest, ParseRecord) { // Test truncated record. parser = DnsRecordParser(data, sizeof(data) - 2, 0); - EXPECT_TRUE(parser.ParseRecord(&record)); + EXPECT_TRUE(parser.ReadRecord(&record)); EXPECT_FALSE(parser.AtEnd()); - EXPECT_FALSE(parser.ParseRecord(&record)); + EXPECT_FALSE(parser.ReadRecord(&record)); } TEST(DnsResponseTest, InitParse) { @@ -225,7 +230,7 @@ TEST(DnsResponseTest, InitParse) { // Check header access. EXPECT_EQ(0x8180, resp.flags()); EXPECT_EQ(0x0, resp.rcode()); - EXPECT_EQ(2, resp.answer_count()); + EXPECT_EQ(2u, resp.answer_count()); // Check question access. EXPECT_EQ(query->qname(), resp.qname()); @@ -234,11 +239,224 @@ TEST(DnsResponseTest, InitParse) { DnsResourceRecord record; DnsRecordParser parser = resp.Parser(); - EXPECT_TRUE(parser.ParseRecord(&record)); + EXPECT_TRUE(parser.ReadRecord(&record)); EXPECT_FALSE(parser.AtEnd()); - EXPECT_TRUE(parser.ParseRecord(&record)); + EXPECT_TRUE(parser.ReadRecord(&record)); EXPECT_TRUE(parser.AtEnd()); - EXPECT_FALSE(parser.ParseRecord(&record)); + EXPECT_FALSE(parser.ReadRecord(&record)); +} + +void VerifyAddressList(const std::vector<const char*>& ip_addresses, + const AddressList& addrlist) { + ASSERT_GT(ip_addresses.size(), 0u); + ASSERT_NE(static_cast<addrinfo*>(NULL), addrlist.head()); + + IPAddressNumber ip_number; + const struct addrinfo* ainfo = addrlist.head(); + for (std::vector<const char*>::const_iterator i = ip_addresses.begin(); + i != ip_addresses.end(); ++i, ainfo = ainfo->ai_next) { + ASSERT_NE(static_cast<addrinfo*>(NULL), ainfo); + EXPECT_EQ(sizeof(struct sockaddr_in), + static_cast<size_t>(ainfo->ai_addrlen)); + + const struct sockaddr* sa = ainfo->ai_addr; + EXPECT_STREQ(*i, NetAddressToString(sa, ainfo->ai_addrlen).c_str()); + } + ASSERT_EQ(static_cast<addrinfo*>(NULL), ainfo); +} + +TEST(DnsResponseTest, ParseToAddressList) { + const struct TestCase { + size_t query_size; + const uint8* response_data; + size_t response_size; + const char* const* expected_addresses; + size_t num_expected_addresses; + const char* expected_cname; + int expected_ttl_sec; + } cases[] = { + { + kT0QuerySize, + kT0ResponseDatagram, arraysize(kT0ResponseDatagram), + kT0IpAddresses, arraysize(kT0IpAddresses), + kT0CanonName, + kT0TTL, + }, + { + kT1QuerySize, + kT1ResponseDatagram, arraysize(kT1ResponseDatagram), + kT1IpAddresses, arraysize(kT1IpAddresses), + kT1CanonName, + kT1TTL, + }, + { + kT2QuerySize, + kT2ResponseDatagram, arraysize(kT2ResponseDatagram), + kT2IpAddresses, arraysize(kT2IpAddresses), + kT2CanonName, + kT2TTL, + }, + { + kT3QuerySize, + kT3ResponseDatagram, arraysize(kT3ResponseDatagram), + kT3IpAddresses, arraysize(kT3IpAddresses), + kT3CanonName, + kT3TTL, + }, + }; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { + const TestCase& t = cases[i]; + DnsResponse response(t.response_data, t.response_size, t.query_size); + AddressList addr_list; + base::TimeDelta ttl; + EXPECT_EQ(DnsResponse::DNS_SUCCESS, + response.ParseToAddressList(&addr_list, &ttl)); + std::vector<const char*> expected_addresses( + t.expected_addresses, + t.expected_addresses + t.num_expected_addresses); + VerifyAddressList(expected_addresses, addr_list); + std::string cname; + ASSERT_TRUE(addr_list.GetCanonicalName(&cname)); + EXPECT_EQ(t.expected_cname, cname); + EXPECT_EQ(base::TimeDelta::FromSeconds(t.expected_ttl_sec), ttl); + } +} + +const uint8 kResponseTruncatedRecord[] = { + // Header: 1 question, 1 answer RR + 0x00, 0x00, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + // Question: name = 'a', type = A (0x1) + 0x01, 'a', 0x00, 0x00, 0x01, 0x00, 0x01, + // Answer: name = 'a', type = A, TTL = 0xFF, RDATA = 10.10.10.10 + 0x01, 'a', 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, + 0x00, 0x04, 0x0A, 0x0A, 0x0A, // Truncated RDATA. +}; + +const uint8 kResponseTruncatedCNAME[] = { + // Header: 1 question, 1 answer RR + 0x00, 0x00, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + // Question: name = 'a', type = A (0x1) + 0x01, 'a', 0x00, 0x00, 0x01, 0x00, 0x01, + // Answer: name = 'a', type = CNAME, TTL = 0xFF, RDATA = 'foo' (truncated) + 0x01, 'a', 0x00, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, + 0x00, 0x03, 0x03, 'f', 'o', // Truncated name. +}; + +const uint8 kResponseNameMismatch[] = { + // Header: 1 question, 1 answer RR + 0x00, 0x00, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + // Question: name = 'a', type = A (0x1) + 0x01, 'a', 0x00, 0x00, 0x01, 0x00, 0x01, + // Answer: name = 'b', type = A, TTL = 0xFF, RDATA = 10.10.10.10 + 0x01, 'b', 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, + 0x00, 0x04, 0x0A, 0x0A, 0x0A, 0x0A, +}; + +const uint8 kResponseNameMismatchInChain[] = { + // Header: 1 question, 3 answer RR + 0x00, 0x00, 0x81, 0x80, 0x00, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, + // Question: name = 'a', type = A (0x1) + 0x01, 'a', 0x00, 0x00, 0x01, 0x00, 0x01, + // Answer: name = 'a', type = CNAME, TTL = 0xFF, RDATA = "b" + 0x01, 'a', 0x00, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, + 0x00, 0x03, 0x01, 'b', 0x00, + // Answer: name = 'b', type = A, TTL = 0xFF, RDATA = 10.10.10.10 + 0x01, 'b', 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, + 0x00, 0x04, 0x0A, 0x0A, 0x0A, 0x0A, + // Answer: name = 'c', type = A, TTL = 0xFF, RDATA = 10.10.10.11 + 0x01, 'c', 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, + 0x00, 0x04, 0x0A, 0x0A, 0x0A, 0x0B, +}; + +const uint8 kResponseSizeMismatch[] = { + // Header: 1 answer RR + 0x00, 0x00, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + // Question: name = 'a', type = AAAA (0x1c) + 0x01, 'a', 0x00, 0x00, 0x1c, 0x00, 0x01, + // Answer: name = 'a', type = AAAA, TTL = 0xFF, RDATA = 10.10.10.10 + 0x01, 'a', 0x00, 0x00, 0x1c, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, + 0x00, 0x04, 0x0A, 0x0A, 0x0A, 0x0A, +}; + +const uint8 kResponseCNAMEAfterAddress[] = { + // Header: 2 answer RR + 0x00, 0x00, 0x81, 0x80, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + // Question: name = 'a', type = A (0x1) + 0x01, 'a', 0x00, 0x00, 0x01, 0x00, 0x01, + // Answer: name = 'a', type = A, TTL = 0xFF, RDATA = 10.10.10.10. + 0x01, 'a', 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, + 0x00, 0x04, 0x0A, 0x0A, 0x0A, 0x0A, + // Answer: name = 'a', type = CNAME, TTL = 0xFF, RDATA = "b" + 0x01, 'a', 0x00, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, + 0x00, 0x03, 0x01, 'b', 0x00, +}; + +const uint8 kResponseTTLMismatch[] = { + // Header: 1 question, 3 answer RR + 0x00, 0x00, 0x81, 0x80, 0x00, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, + // Question: name = 'a', type = A (0x1) + 0x01, 'a', 0x00, 0x00, 0x01, 0x00, 0x01, + // Answer: name = 'a', type = CNAME, TTL = 0xFF, RDATA = "b" + 0x01, 'a', 0x00, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, + 0x00, 0x03, 0x01, 'b', 0x00, + // Answer: name = 'b', type = A, TTL = 0xFF, RDATA = 10.10.10.10 + 0x01, 'b', 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, + 0x00, 0x04, 0x0A, 0x0A, 0x0A, 0x0A, + // Answer: name = 'b', type = A, TTL = 0xBB, RDATA = 10.10.10.11 + 0x01, 'b', 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0xBB, + 0x00, 0x04, 0x0A, 0x0A, 0x0A, 0x0B, +}; + +const uint8 kResponseNoAddresses[] = { + // Header: 1 question, 1 answer RR, 1 authority RR + 0x00, 0x00, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, + // Question: name = 'a', type = A (0x1) + 0x01, 'a', 0x00, 0x00, 0x01, 0x00, 0x01, + // Answer: name = 'a', type = CNAME, TTL = 0xFF, RDATA = "b" + 0x01, 'a', 0x00, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, + 0x00, 0x03, 0x01, 'b', 0x00, + // Authority section + // Answer: name = 'b', type = A, TTL = 0xFF, RDATA = 10.10.10.10 + 0x01, 'b', 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, + 0x00, 0x04, 0x0A, 0x0A, 0x0A, 0x0A, +}; + +TEST(DnsResponseTest, ParseToAddressListFail) { + const struct TestCase { + const uint8* data; + size_t size; + DnsResponse::Result expected_result; + } cases[] = { + { kResponseTruncatedRecord, arraysize(kResponseTruncatedRecord), + DnsResponse::DNS_MALFORMED_RESPONSE }, + { kResponseTruncatedCNAME, arraysize(kResponseTruncatedCNAME), + DnsResponse::DNS_MALFORMED_CNAME }, + { kResponseNameMismatch, arraysize(kResponseNameMismatch), + DnsResponse::DNS_NAME_MISMATCH }, + { kResponseNameMismatchInChain, arraysize(kResponseNameMismatchInChain), + DnsResponse::DNS_NAME_MISMATCH }, + { kResponseSizeMismatch, arraysize(kResponseSizeMismatch), + DnsResponse::DNS_SIZE_MISMATCH }, + { kResponseCNAMEAfterAddress, arraysize(kResponseCNAMEAfterAddress), + DnsResponse::DNS_CNAME_AFTER_ADDRESS }, + { kResponseTTLMismatch, arraysize(kResponseTTLMismatch), + DnsResponse::DNS_ADDRESS_TTL_MISMATCH }, + { kResponseNoAddresses, arraysize(kResponseNoAddresses), + DnsResponse::DNS_NO_ADDRESSES }, + }; + + const size_t kQuerySize = 12 + 7; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { + const TestCase& t = cases[i]; + + DnsResponse response(t.data, t.size, kQuerySize); + AddressList addr_list; + base::TimeDelta ttl; + EXPECT_EQ(t.expected_result, + response.ParseToAddressList(&addr_list, &ttl)); + } } } // namespace diff --git a/net/dns/dns_session.h b/net/dns/dns_session.h index 85aea0a..aa070ac 100644 --- a/net/dns/dns_session.h +++ b/net/dns/dns_session.h @@ -34,7 +34,7 @@ class NET_EXPORT_PRIVATE DnsSession const DnsConfig& config() const { return config_; } NetLog* net_log() const { return net_log_; } - ClientSocketFactory* socket_factory() { return socket_factory_.get(); } + ClientSocketFactory* socket_factory() { return socket_factory_; } // Return the next random query ID. int NextQueryId() const; @@ -50,7 +50,7 @@ class NET_EXPORT_PRIVATE DnsSession ~DnsSession(); const DnsConfig config_; - scoped_ptr<ClientSocketFactory> socket_factory_; + ClientSocketFactory* socket_factory_; RandCallback rand_callback_; NetLog* net_log_; diff --git a/net/dns/dns_test_util.h b/net/dns/dns_test_util.h index 6c29058..8d8c0a6 100644 --- a/net/dns/dns_test_util.h +++ b/net/dns/dns_test_util.h @@ -21,13 +21,7 @@ static const char kT0DnsName[] = { 0x03, 'c', 'o', 'm', 0x00 }; -static const uint8 kT0QueryDatagram[] = { - // query for www.google.com, type A. - 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x03, 0x77, 0x77, 0x77, - 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, - 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01 -}; +static const size_t kT0QuerySize = 32; static const uint8 kT0ResponseDatagram[] = { // response contains one CNAME for www.l.google.com and the following // IP addresses: 74.125.226.{179,180,176,177,178} @@ -53,6 +47,8 @@ static const char* const kT0IpAddresses[] = { "74.125.226.179", "74.125.226.180", "74.125.226.176", "74.125.226.177", "74.125.226.178" }; +static const char kT0CanonName[] = "www.l.google.com"; +static const int kT0TTL = 0x000000e4; //----------------------------------------------------------------------------- // Query/response set for codereview.chromium.org, ID is fixed to 1. @@ -64,15 +60,7 @@ static const char kT1DnsName[] = { 0x03, 'o', 'r', 'g', 0x00 }; -static const uint8 kT1QueryDatagram[] = { - // query for codereview.chromium.org, type A. - 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0a, 0x63, 0x6f, 0x64, - 0x65, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x08, - 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x69, 0x75, 0x6d, - 0x03, 0x6f, 0x72, 0x67, 0x00, 0x00, 0x01, 0x00, - 0x01 -}; +static const size_t kT1QuerySize = 41; static const uint8 kT1ResponseDatagram[] = { // response contains one CNAME for ghs.l.google.com and the following // IP address: 64.233.169.121 @@ -91,6 +79,8 @@ static const uint8 kT1ResponseDatagram[] = { static const char* const kT1IpAddresses[] = { "64.233.169.121" }; +static const char kT1CanonName[] = "ghs.l.google.com"; +static const int kT1TTL = 0x0000010b; //----------------------------------------------------------------------------- // Query/response set for www.ccs.neu.edu, ID is fixed to 2. @@ -103,14 +93,7 @@ static const char kT2DnsName[] = { 0x03, 'e', 'd', 'u', 0x00 }; -static const uint8 kT2QueryDatagram[] = { - // query for www.ccs.neu.edu, type A. - 0x00, 0x02, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x03, 0x77, 0x77, 0x77, - 0x03, 0x63, 0x63, 0x73, 0x03, 0x6e, 0x65, 0x75, - 0x03, 0x65, 0x64, 0x75, 0x00, 0x00, 0x01, 0x00, - 0x01 -}; +static const size_t kT2QuerySize = 33; static const uint8 kT2ResponseDatagram[] = { // response contains one CNAME for vulcan.ccs.neu.edu and the following // IP address: 129.10.116.81 @@ -127,6 +110,8 @@ static const uint8 kT2ResponseDatagram[] = { static const char* const kT2IpAddresses[] = { "129.10.116.81" }; +static const char kT2CanonName[] = "vulcan.ccs.neu.edu"; +static const int kT2TTL = 0x0000012c; //----------------------------------------------------------------------------- // Query/response set for www.google.az, ID is fixed to 3. @@ -138,13 +123,7 @@ static const char kT3DnsName[] = { 0x02, 'a', 'z', 0x00 }; -static const uint8 kT3QueryDatagram[] = { - // query for www.google.az, type A. - 0x00, 0x03, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x03, 0x77, 0x77, 0x77, - 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x02, - 0x61, 0x7a, 0x00, 0x00, 0x01, 0x00, 0x01 -}; +static const size_t kT3QuerySize = 31; static const uint8 kT3ResponseDatagram[] = { // response contains www.google.com as CNAME for www.google.az and // www.l.google.com as CNAME for www.google.com and the following @@ -174,6 +153,8 @@ static const char* const kT3IpAddresses[] = { "74.125.226.178", "74.125.226.179", "74.125.226.180", "74.125.226.176", "74.125.226.177" }; +static const char kT3CanonName[] = "www.l.google.com"; +static const int kT3TTL = 0x00000015; } // namespace net diff --git a/net/dns/dns_transaction.cc b/net/dns/dns_transaction.cc index 6cd02a3..8bffa12 100644 --- a/net/dns/dns_transaction.cc +++ b/net/dns/dns_transaction.cc @@ -51,22 +51,19 @@ bool IsIPLiteral(const std::string& hostname) { class StartParameters : public NetLog::EventParameters { public: StartParameters(const std::string& hostname, - uint16 qtype, - const NetLog::Source& source) - : hostname_(hostname), qtype_(qtype), source_(source) {} + uint16 qtype) + : hostname_(hostname), qtype_(qtype) {} virtual Value* ToValue() const OVERRIDE { DictionaryValue* dict = new DictionaryValue(); dict->SetString("hostname", hostname_); dict->SetInteger("query_type", qtype_); - dict->Set("source_dependency", source_.ToValue()); return dict; } private: const std::string hostname_; const uint16 qtype_; - const NetLog::Source source_; }; class ResponseParameters : public NetLog::EventParameters { @@ -78,7 +75,7 @@ class ResponseParameters : public NetLog::EventParameters { DictionaryValue* dict = new DictionaryValue(); dict->SetInteger("rcode", rcode_); dict->SetInteger("answer_count", answer_count_); - dict->Set("socket_source", source_.ToValue()); + dict->Set("source_dependency", source_.ToValue()); return dict; } @@ -211,8 +208,14 @@ class DnsUDPAttempt { return rv; DCHECK(rv); - if (!response_->InitParse(rv, *query_)) + if (!response_->InitParse(rv, *query_)) { + // TODO(szym): Consider making this reaction less aggressive. + // Other implementations simply ignore mismatched responses. Since each + // DnsUDPAttempt binds to a different port, we might find that responses + // to previously timed out queries lead to failures in the future. + // http://crbug.com/107413 return ERR_DNS_MALFORMED_RESPONSE; + } if (response_->flags() & dns_protocol::kFlagTC) return ERR_DNS_SERVER_REQUIRES_TCP; if (response_->rcode() != dns_protocol::kRcodeNOERROR && @@ -258,13 +261,12 @@ class DnsTransactionImpl : public DnsTransaction, public base::NonThreadSafe { const std::string& hostname, uint16 qtype, const DnsTransactionFactory::CallbackType& callback, - const BoundNetLog& source_net_log) + const BoundNetLog& net_log) : session_(session), hostname_(hostname), qtype_(qtype), callback_(callback), - net_log_(BoundNetLog::Make(session->net_log(), - NetLog::SOURCE_DNS_TRANSACTION)), + net_log_(net_log), first_server_index_(0) { DCHECK(session_); DCHECK(!hostname_.empty()); @@ -273,7 +275,7 @@ class DnsTransactionImpl : public DnsTransaction, public base::NonThreadSafe { DCHECK(!IsIPLiteral(hostname_)); net_log_.BeginEvent(NetLog::TYPE_DNS_TRANSACTION, make_scoped_refptr( - new StartParameters(hostname_, qtype_, source_net_log.source()))); + new StartParameters(hostname_, qtype_))); } virtual ~DnsTransactionImpl() { @@ -299,7 +301,19 @@ class DnsTransactionImpl : public DnsTransaction, public base::NonThreadSafe { int rv = PrepareSearch(); if (rv == OK) rv = StartQuery(); - DCHECK_NE(OK, rv); + if (rv == OK) { + // In the very unlikely case that we immediately received the response, we + // cannot simply return OK nor run the callback, but instead complete + // asynchronously. + // TODO(szym): replace Unretained with WeakPtr factory. + MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&DnsTransactionImpl::DoCallback, + base::Unretained(this), + OK, + attempts_.back())); + return ERR_IO_PENDING; + } return rv; } @@ -387,7 +401,8 @@ class DnsTransactionImpl : public DnsTransaction, public base::NonThreadSafe { } net_log_.AddEvent(NetLog::TYPE_DNS_TRANSACTION_ATTEMPT, make_scoped_refptr( - new NetLogSourceParameter("socket_source", socket->NetLog().source()))); + new NetLogSourceParameter("source_dependency", + socket->NetLog().source()))); const DnsConfig& config = session_->config(); @@ -451,7 +466,7 @@ class DnsTransactionImpl : public DnsTransaction, public base::NonThreadSafe { DoCallback(rv, attempt); return; default: - // TODO(szym): Some nameservers could fail so try the next one. + // Some nameservers could fail so try the next one. const DnsConfig& config = session_->config(); if (attempts_.size() < config.attempts * config.nameservers.size()) { rv = MakeAttempt(); @@ -512,12 +527,12 @@ class DnsTransactionFactoryImpl : public DnsTransactionFactory { const std::string& hostname, uint16 qtype, const CallbackType& callback, - const BoundNetLog& source_net_log) OVERRIDE { + const BoundNetLog& net_log) OVERRIDE { return scoped_ptr<DnsTransaction>(new DnsTransactionImpl(session_, hostname, qtype, callback, - source_net_log)); + net_log)); } private: diff --git a/net/dns/dns_transaction.h b/net/dns/dns_transaction.h index 038f4b2..383f4a5 100644 --- a/net/dns/dns_transaction.h +++ b/net/dns/dns_transaction.h @@ -61,12 +61,12 @@ class NET_EXPORT_PRIVATE DnsTransactionFactory { // search. |hostname| should not be an IP literal. // // The transaction will run |callback| upon asynchronous completion. - // The source of |source_net_log| is used as source dependency in log. + // The |net_log| is used as the parent log. virtual scoped_ptr<DnsTransaction> CreateTransaction( const std::string& hostname, uint16 qtype, const CallbackType& callback, - const BoundNetLog& source_net_log) WARN_UNUSED_RESULT = 0; + const BoundNetLog& net_log) WARN_UNUSED_RESULT = 0; // Creates a DnsTransactionFactory which creates DnsTransactionImpl using the // |session|. diff --git a/net/dns/dns_transaction_unittest.cc b/net/dns/dns_transaction_unittest.cc index 8e0888a..b21eb794 100644 --- a/net/dns/dns_transaction_unittest.cc +++ b/net/dns/dns_transaction_unittest.cc @@ -141,14 +141,16 @@ class TransactionHelper { if (expected_answer_count_ >= 0) { EXPECT_EQ(OK, rv); - EXPECT_EQ(expected_answer_count_, response->answer_count()); + EXPECT_EQ(static_cast<unsigned>(expected_answer_count_), + response->answer_count()); EXPECT_EQ(qtype_, response->qtype()); DnsRecordParser parser = response->Parser(); DnsResourceRecord record; for (int i = 0; i < expected_answer_count_; ++i) { - EXPECT_TRUE(parser.ParseRecord(&record)); + EXPECT_TRUE(parser.ReadRecord(&record)); } + // Technically, there could be additional RRs, but not in our test data. EXPECT_TRUE(parser.AtEnd()); } else { EXPECT_EQ(expected_answer_count_, rv); @@ -212,10 +214,10 @@ class DnsTransactionTest : public testing::Test { // Called after fully configuring |config|. void ConfigureFactory() { - socket_factory_ = new TestSocketFactory(); + socket_factory_.reset(new TestSocketFactory()); session_ = new DnsSession( config_, - socket_factory_, + socket_factory_.get(), base::Bind(&DnsTransactionTest::GetNextId, base::Unretained(this)), NULL /* NetLog */); transaction_factory_ = DnsTransactionFactory::CreateFactory(session_.get()); @@ -232,7 +234,7 @@ class DnsTransactionTest : public testing::Test { uint16 id, const char* data, size_t data_length) { - CHECK(socket_factory_); + CHECK(socket_factory_.get()); DnsQuery* query = new DnsQuery(id, DomainFromDot(dotted_name), qtype); queries_.push_back(query); @@ -252,7 +254,7 @@ class DnsTransactionTest : public testing::Test { // Add expected query of |dotted_name| and |qtype| and no response. void AddTimeout(const char* dotted_name, uint16 qtype) { - CHECK(socket_factory_); + CHECK(socket_factory_.get()); uint16 id = base::RandInt(0, kuint16max); DnsQuery* query = new DnsQuery(id, DomainFromDot(dotted_name), qtype); queries_.push_back(query); @@ -268,7 +270,7 @@ class DnsTransactionTest : public testing::Test { // Add expected query of |dotted_name| and |qtype| and response with no answer // and rcode set to |rcode|. void AddRcode(const char* dotted_name, uint16 qtype, int rcode) { - CHECK(socket_factory_); + CHECK(socket_factory_.get()); CHECK_NE(dns_protocol::kRcodeNOERROR, rcode); uint16 id = base::RandInt(0, kuint16max); DnsQuery* query = new DnsQuery(id, DomainFromDot(dotted_name), qtype); @@ -360,8 +362,7 @@ class DnsTransactionTest : public testing::Test { ScopedVector<DelayedSocketData> socket_data_; std::deque<int> transaction_ids_; - // Owned by |session_|. - TestSocketFactory* socket_factory_; + scoped_ptr<TestSocketFactory> socket_factory_; scoped_refptr<DnsSession> session_; scoped_ptr<DnsTransactionFactory> transaction_factory_; }; |