Commit 0adcb2b7 authored by szym@chromium.org's avatar szym@chromium.org

[net/dns] Resolve AF_UNSPEC on dual-stacked systems. Sort addresses according to RFC3484.

Original review: http://codereview.chromium.org/10442098/

BUG=113993
TEST=./net_unittests --gtest_filter=AddressSorter*:HostResolverImplDnsTest.DnsTaskUnspec

Review URL: https://chromiumcodereview.appspot.com/10855179

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@151750 0039d316-1c4b-4281-b951-d872f2087c98
parent 69cc9fd8
...@@ -38,6 +38,7 @@ ...@@ -38,6 +38,7 @@
#include "net/base/net_errors.h" #include "net/base/net_errors.h"
#include "net/base/net_log.h" #include "net/base/net_log.h"
#include "net/base/net_util.h" #include "net/base/net_util.h"
#include "net/dns/address_sorter.h"
#include "net/dns/dns_client.h" #include "net/dns/dns_client.h"
#include "net/dns/dns_config_service.h" #include "net/dns/dns_config_service.h"
#include "net/dns/dns_protocol.h" #include "net/dns/dns_protocol.h"
...@@ -609,7 +610,7 @@ class HostResolverImpl::ProcTask ...@@ -609,7 +610,7 @@ class HostResolverImpl::ProcTask
void Cancel() { void Cancel() {
DCHECK(origin_loop_->BelongsToCurrentThread()); DCHECK(origin_loop_->BelongsToCurrentThread());
if (was_canceled()) if (was_canceled() || was_completed())
return; return;
callback_.Reset(); callback_.Reset();
...@@ -1042,32 +1043,33 @@ class HostResolverImpl::IPv6ProbeJob ...@@ -1042,32 +1043,33 @@ class HostResolverImpl::IPv6ProbeJob
// Resolves the hostname using DnsTransaction. // Resolves the hostname using DnsTransaction.
// TODO(szym): This could be moved to separate source file as well. // TODO(szym): This could be moved to separate source file as well.
class HostResolverImpl::DnsTask { class HostResolverImpl::DnsTask : public base::SupportsWeakPtr<DnsTask> {
public: public:
typedef base::Callback<void(int net_error, typedef base::Callback<void(int net_error,
const AddressList& addr_list, const AddressList& addr_list,
base::TimeDelta ttl)> Callback; base::TimeDelta ttl)> Callback;
DnsTask(DnsTransactionFactory* factory, DnsTask(DnsClient* client,
const Key& key, const Key& key,
const Callback& callback, const Callback& callback,
const BoundNetLog& job_net_log) const BoundNetLog& job_net_log)
: callback_(callback), net_log_(job_net_log) { : client_(client),
DCHECK(factory); family_(key.address_family),
callback_(callback),
net_log_(job_net_log) {
DCHECK(client);
DCHECK(!callback.is_null()); DCHECK(!callback.is_null());
// For now we treat ADDRESS_FAMILY_UNSPEC as if it was IPV4. // If unspecified, do IPv4 first, because suffix search will be faster.
uint16 qtype = (key.address_family == ADDRESS_FAMILY_IPV6) uint16 qtype = (family_ == ADDRESS_FAMILY_IPV6) ?
? dns_protocol::kTypeAAAA dns_protocol::kTypeAAAA :
: dns_protocol::kTypeA; dns_protocol::kTypeA;
// TODO(szym): Implement "happy eyeballs". transaction_ = client_->GetTransactionFactory()->CreateTransaction(
transaction_ = factory->CreateTransaction(
key.hostname, key.hostname,
qtype, qtype,
base::Bind(&DnsTask::OnTransactionComplete, base::Unretained(this), base::Bind(&DnsTask::OnTransactionComplete, base::Unretained(this),
base::TimeTicks::Now()), true /* first_query */, base::TimeTicks::Now()),
net_log_); net_log_);
DCHECK(transaction_.get());
} }
int Start() { int Start() {
...@@ -1075,47 +1077,138 @@ class HostResolverImpl::DnsTask { ...@@ -1075,47 +1077,138 @@ class HostResolverImpl::DnsTask {
return transaction_->Start(); return transaction_->Start();
} }
void OnTransactionComplete(const base::TimeTicks& start_time, private:
void OnTransactionComplete(bool first_query,
const base::TimeTicks& start_time,
DnsTransaction* transaction, DnsTransaction* transaction,
int net_error, int net_error,
const DnsResponse* response) { const DnsResponse* response) {
DCHECK(transaction); DCHECK(transaction);
// Run |callback_| last since the owning Job will then delete this DnsTask. // Run |callback_| last since the owning Job will then delete this DnsTask.
DnsResponse::Result result = DnsResponse::DNS_SUCCESS; if (net_error != OK) {
if (net_error == OK) { DNS_HISTOGRAM("AsyncDNS.TransactionFailure",
CHECK(response);
DNS_HISTOGRAM("AsyncDNS.TransactionSuccess",
base::TimeTicks::Now() - start_time); base::TimeTicks::Now() - start_time);
AddressList addr_list; OnFailure(net_error, DnsResponse::DNS_PARSE_OK);
base::TimeDelta ttl; return;
result = response->ParseToAddressList(&addr_list, &ttl); }
UMA_HISTOGRAM_ENUMERATION("AsyncDNS.ParseToAddressList",
result, CHECK(response);
DnsResponse::DNS_PARSE_RESULT_MAX); DNS_HISTOGRAM("AsyncDNS.TransactionSuccess",
if (result == DnsResponse::DNS_SUCCESS) { base::TimeTicks::Now() - start_time);
net_log_.EndEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_DNS_TASK, AddressList addr_list;
addr_list.CreateNetLogCallback()); base::TimeDelta ttl;
callback_.Run(net_error, addr_list, 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; return;
} }
net_error = ERR_DNS_MALFORMED_RESPONSE;
} else { } 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); 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( net_log_.EndEvent(
NetLog::TYPE_HOST_RESOLVER_IMPL_DNS_TASK, NetLog::TYPE_HOST_RESOLVER_IMPL_DNS_TASK,
base::Bind(&NetLogDnsTaskFailedCallback, net_error, result)); base::Bind(&NetLogDnsTaskFailedCallback, net_error, result));
callback_.Run(net_error, AddressList(), base::TimeDelta()); 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. // The listener to the results of this DnsTask.
Callback callback_; Callback callback_;
const BoundNetLog net_log_; const BoundNetLog net_log_;
scoped_ptr<DnsTransaction> transaction_; 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 { ...@@ -1214,7 +1307,7 @@ class HostResolverImpl::Job : public PrioritizedDispatcher::Job {
} }
// Marks |req| as cancelled. If it was the last active Request, also finishes // 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) { void CancelRequest(Request* req) {
DCHECK_EQ(key_.hostname, req->info().hostname()); DCHECK_EQ(key_.hostname, req->info().hostname());
DCHECK(!req->was_canceled()); DCHECK(!req->was_canceled());
...@@ -1381,7 +1474,7 @@ class HostResolverImpl::Job : public PrioritizedDispatcher::Job { ...@@ -1381,7 +1474,7 @@ class HostResolverImpl::Job : public PrioritizedDispatcher::Job {
void StartDnsTask() { void StartDnsTask() {
DCHECK(resolver_->HaveDnsConfig()); DCHECK(resolver_->HaveDnsConfig());
dns_task_.reset(new DnsTask( dns_task_.reset(new DnsTask(
resolver_->dns_client_->GetTransactionFactory(), resolver_->dns_client_.get(),
key_, key_,
base::Bind(&Job::OnDnsTaskComplete, base::Unretained(this)), base::Bind(&Job::OnDnsTaskComplete, base::Unretained(this)),
net_log_)); net_log_));
...@@ -1415,6 +1508,7 @@ class HostResolverImpl::Job : public PrioritizedDispatcher::Job { ...@@ -1415,6 +1508,7 @@ class HostResolverImpl::Job : public PrioritizedDispatcher::Job {
} }
UmaAsyncDnsResolveStatus(RESOLVE_STATUS_DNS_SUCCESS); UmaAsyncDnsResolveStatus(RESOLVE_STATUS_DNS_SUCCESS);
CompleteRequests(net_error, addr_list, ttl); CompleteRequests(net_error, addr_list, ttl);
} }
......
...@@ -19,14 +19,17 @@ ...@@ -19,14 +19,17 @@
#include "net/base/host_resolver.h" #include "net/base/host_resolver.h"
#include "net/base/host_resolver_proc.h" #include "net/base/host_resolver_proc.h"
#include "net/base/net_export.h" #include "net/base/net_export.h"
#include "net/base/net_log.h"
#include "net/base/network_change_notifier.h" #include "net/base/network_change_notifier.h"
#include "net/base/prioritized_dispatcher.h" #include "net/base/prioritized_dispatcher.h"
#include "net/dns/dns_client.h"
#include "net/dns/dns_config_service.h"
namespace net { namespace net {
class BoundNetLog;
class DnsClient;
struct DnsConfig;
class DnsConfigService;
class NetLog;
// For each hostname that is requested, HostResolver creates a // For each hostname that is requested, HostResolver creates a
// HostResolverImpl::Job. When this job gets dispatched it creates a ProcTask // HostResolverImpl::Job. When this job gets dispatched it creates a ProcTask
// which runs the given HostResolverProc on a WorkerPool thread. If requests for // which runs the given HostResolverProc on a WorkerPool thread. If requests for
......
...@@ -1236,21 +1236,46 @@ DnsConfig CreateValidDnsConfig() { ...@@ -1236,21 +1236,46 @@ DnsConfig CreateValidDnsConfig() {
class HostResolverImplDnsTest : public HostResolverImplTest { class HostResolverImplDnsTest : public HostResolverImplTest {
protected: protected:
virtual void SetUp() OVERRIDE { virtual void SetUp() OVERRIDE {
AddDnsRule("er", dns_protocol::kTypeA, MockDnsClientRule::FAIL_SYNC);
AddDnsRule("er", dns_protocol::kTypeAAAA, MockDnsClientRule::FAIL_SYNC);
AddDnsRule("nx", dns_protocol::kTypeA, MockDnsClientRule::FAIL_ASYNC);
AddDnsRule("nx", dns_protocol::kTypeAAAA, MockDnsClientRule::FAIL_ASYNC);
AddDnsRule("ok", dns_protocol::kTypeA, MockDnsClientRule::OK);
AddDnsRule("ok", dns_protocol::kTypeAAAA, MockDnsClientRule::OK);
AddDnsRule("4ok", dns_protocol::kTypeA, MockDnsClientRule::OK);
AddDnsRule("4ok", dns_protocol::kTypeAAAA, MockDnsClientRule::EMPTY);
AddDnsRule("6ok", dns_protocol::kTypeA, MockDnsClientRule::EMPTY);
AddDnsRule("6ok", dns_protocol::kTypeAAAA, MockDnsClientRule::OK);
AddDnsRule("4nx", dns_protocol::kTypeA, MockDnsClientRule::OK);
AddDnsRule("4nx", dns_protocol::kTypeAAAA, MockDnsClientRule::FAIL_ASYNC);
CreateResolver();
}
void CreateResolver() {
config_service_ = new MockDnsConfigService(); config_service_ = new MockDnsConfigService();
resolver_.reset(new HostResolverImpl( resolver_.reset(new HostResolverImpl(
HostCache::CreateDefaultCache(), HostCache::CreateDefaultCache(),
DefaultLimits(), DefaultLimits(),
DefaultParams(proc_), DefaultParams(proc_),
scoped_ptr<DnsConfigService>(config_service_), scoped_ptr<DnsConfigService>(config_service_),
CreateMockDnsClient(DnsConfig()), CreateMockDnsClient(DnsConfig(), dns_rules_),
NULL)); NULL));
} }
// Adds a rule to |dns_rules_|. Must be followed by |CreateResolver| to apply.
void AddDnsRule(const std::string& prefix,
uint16 qtype,
MockDnsClientRule::Result result) {
MockDnsClientRule rule = { prefix, qtype, result };
dns_rules_.push_back(rule);
}
void ChangeDnsConfig(const DnsConfig& config) { void ChangeDnsConfig(const DnsConfig& config) {
config_service_->ChangeConfig(config); config_service_->ChangeConfig(config);
config_service_->ChangeHosts(config.hosts); config_service_->ChangeHosts(config.hosts);
} }
MockDnsClientRuleList dns_rules_;
// Owned by |resolver_|. // Owned by |resolver_|.
MockDnsConfigService* config_service_; MockDnsConfigService* config_service_;
}; };
...@@ -1298,11 +1323,38 @@ TEST_F(HostResolverImplDnsTest, DnsTask) { ...@@ -1298,11 +1323,38 @@ TEST_F(HostResolverImplDnsTest, DnsTask) {
EXPECT_TRUE(requests_[5]->HasOneAddress("192.168.1.102", 80)); EXPECT_TRUE(requests_[5]->HasOneAddress("192.168.1.102", 80));
} }
TEST_F(HostResolverImplDnsTest, DnsTaskUnspec) {
ChangeDnsConfig(CreateValidDnsConfig());
proc_->AddRuleForAllFamilies("4nx", "192.168.1.101");
// All other hostnames will fail in proc_.
EXPECT_EQ(ERR_IO_PENDING, CreateRequest("ok", 80)->Resolve());
EXPECT_EQ(ERR_IO_PENDING, CreateRequest("4ok", 80)->Resolve());
EXPECT_EQ(ERR_IO_PENDING, CreateRequest("6ok", 80)->Resolve());
EXPECT_EQ(ERR_IO_PENDING, CreateRequest("4nx", 80)->Resolve());
proc_->SignalMultiple(requests_.size());
for (size_t i = 0; i < requests_.size(); ++i)
EXPECT_EQ(OK, requests_[i]->WaitForResult()) << i;
EXPECT_EQ(2u, requests_[0]->NumberOfAddresses());
EXPECT_TRUE(requests_[0]->HasAddress("127.0.0.1", 80));
EXPECT_TRUE(requests_[0]->HasAddress("::1", 80));
EXPECT_EQ(1u, requests_[1]->NumberOfAddresses());
EXPECT_TRUE(requests_[1]->HasAddress("127.0.0.1", 80));
EXPECT_EQ(1u, requests_[2]->NumberOfAddresses());
EXPECT_TRUE(requests_[2]->HasAddress("::1", 80));
EXPECT_EQ(1u, requests_[3]->NumberOfAddresses());
EXPECT_TRUE(requests_[3]->HasAddress("192.168.1.101", 80));
}
TEST_F(HostResolverImplDnsTest, ServeFromHosts) { TEST_F(HostResolverImplDnsTest, ServeFromHosts) {
// Initially, use empty HOSTS file. // Initially, use empty HOSTS file.
ChangeDnsConfig(CreateValidDnsConfig()); ChangeDnsConfig(CreateValidDnsConfig());
proc_->AddRuleForAllFamilies("", "0.0.0.0"); // Default to failures. proc_->AddRuleForAllFamilies("", ""); // Default to failures.
proc_->SignalMultiple(1u); // For the first request which misses. proc_->SignalMultiple(1u); // For the first request which misses.
Request* req0 = CreateRequest("er_ipv4", 80); Request* req0 = CreateRequest("er_ipv4", 80);
...@@ -1353,7 +1405,7 @@ TEST_F(HostResolverImplDnsTest, ServeFromHosts) { ...@@ -1353,7 +1405,7 @@ TEST_F(HostResolverImplDnsTest, ServeFromHosts) {
TEST_F(HostResolverImplDnsTest, BypassDnsTask) { TEST_F(HostResolverImplDnsTest, BypassDnsTask) {
ChangeDnsConfig(CreateValidDnsConfig()); ChangeDnsConfig(CreateValidDnsConfig());
proc_->AddRuleForAllFamilies("", "0.0.0.0"); // Default to failures. proc_->AddRuleForAllFamilies("", ""); // Default to failures.
EXPECT_EQ(ERR_IO_PENDING, CreateRequest("ok.local", 80)->Resolve()); EXPECT_EQ(ERR_IO_PENDING, CreateRequest("ok.local", 80)->Resolve());
EXPECT_EQ(ERR_IO_PENDING, CreateRequest("ok.local.", 80)->Resolve()); EXPECT_EQ(ERR_IO_PENDING, CreateRequest("ok.local.", 80)->Resolve());
......
...@@ -658,3 +658,6 @@ NET_ERROR(DNS_CACHE_MISS, -804) ...@@ -658,3 +658,6 @@ NET_ERROR(DNS_CACHE_MISS, -804)
// Suffix search list rules prevent resolution of the given host name. // Suffix search list rules prevent resolution of the given host name.
NET_ERROR(DNS_SEARCH_EMPTY, -805) NET_ERROR(DNS_SEARCH_EMPTY, -805)
// Failed to sort addresses according to RFC3484.
NET_ERROR(DNS_SORT_ERROR, -806)
...@@ -2216,6 +2216,12 @@ bool ParseIPLiteralToNumber(const std::string& ip_literal, ...@@ -2216,6 +2216,12 @@ bool ParseIPLiteralToNumber(const std::string& ip_literal,
return family == url_canon::CanonHostInfo::IPV4; return family == url_canon::CanonHostInfo::IPV4;
} }
namespace {
const unsigned char kIPv4MappedPrefix[] =
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF };
}
IPAddressNumber ConvertIPv4NumberToIPv6Number( IPAddressNumber ConvertIPv4NumberToIPv6Number(
const IPAddressNumber& ipv4_number) { const IPAddressNumber& ipv4_number) {
DCHECK(ipv4_number.size() == 4); DCHECK(ipv4_number.size() == 4);
...@@ -2224,13 +2230,27 @@ IPAddressNumber ConvertIPv4NumberToIPv6Number( ...@@ -2224,13 +2230,27 @@ IPAddressNumber ConvertIPv4NumberToIPv6Number(
// <80 bits of zeros> + <16 bits of ones> + <32-bit IPv4 address>. // <80 bits of zeros> + <16 bits of ones> + <32-bit IPv4 address>.
IPAddressNumber ipv6_number; IPAddressNumber ipv6_number;
ipv6_number.reserve(16); ipv6_number.reserve(16);
ipv6_number.insert(ipv6_number.end(), 10, 0); ipv6_number.insert(ipv6_number.end(),
ipv6_number.push_back(0xFF); kIPv4MappedPrefix,
ipv6_number.push_back(0xFF); kIPv4MappedPrefix + arraysize(kIPv4MappedPrefix));
ipv6_number.insert(ipv6_number.end(), ipv4_number.begin(), ipv4_number.end()); ipv6_number.insert(ipv6_number.end(), ipv4_number.begin(), ipv4_number.end());
return ipv6_number; return ipv6_number;
} }
bool IsIPv4Mapped(const IPAddressNumber& address) {
if (address.size() != kIPv6AddressSize)
return false;
return std::equal(address.begin(),
address.begin() + arraysize(kIPv4MappedPrefix),
kIPv4MappedPrefix);
}
IPAddressNumber ConvertIPv4MappedToIPv4(const IPAddressNumber& address) {
DCHECK(IsIPv4Mapped(address));
return IPAddressNumber(address.begin() + arraysize(kIPv4MappedPrefix),
address.end());
}
bool ParseCIDRBlock(const std::string& cidr_literal, bool ParseCIDRBlock(const std::string& cidr_literal,
IPAddressNumber* ip_number, IPAddressNumber* ip_number,
size_t* prefix_length_in_bits) { size_t* prefix_length_in_bits) {
......
...@@ -424,6 +424,14 @@ NET_EXPORT_PRIVATE bool ParseIPLiteralToNumber(const std::string& ip_literal, ...@@ -424,6 +424,14 @@ NET_EXPORT_PRIVATE bool ParseIPLiteralToNumber(const std::string& ip_literal,
NET_EXPORT_PRIVATE IPAddressNumber ConvertIPv4NumberToIPv6Number( NET_EXPORT_PRIVATE IPAddressNumber ConvertIPv4NumberToIPv6Number(
const IPAddressNumber& ipv4_number); const IPAddressNumber& ipv4_number);
// Returns true iff |address| is an IPv4-mapped IPv6 address.
NET_EXPORT_PRIVATE bool IsIPv4Mapped(const IPAddressNumber& address);
// Converts an IPv4-mapped IPv6 address to IPv4 address. Should only be called
// on IPv4-mapped IPv6 addresses.
NET_EXPORT_PRIVATE IPAddressNumber ConvertIPv4MappedToIPv4(
const IPAddressNumber& address);
// Parses an IP block specifier from CIDR notation to an // Parses an IP block specifier from CIDR notation to an
// (IP address, prefix length) pair. Returns true on success and fills // (IP address, prefix length) pair. Returns true on success and fills
// |*ip_number| with the numeric value of the IP address and sets // |*ip_number| with the numeric value of the IP address and sets
......
...@@ -3049,6 +3049,29 @@ TEST(NetUtilTest, ConvertIPv4NumberToIPv6Number) { ...@@ -3049,6 +3049,29 @@ TEST(NetUtilTest, ConvertIPv4NumberToIPv6Number) {
DumpIPNumber(ipv6_number)); DumpIPNumber(ipv6_number));
} }
TEST(NetUtilTest, IsIPv4Mapped) {
IPAddressNumber ipv4_number;
EXPECT_TRUE(ParseIPLiteralToNumber("192.168.0.1", &ipv4_number));
EXPECT_FALSE(IsIPv4Mapped(ipv4_number));
IPAddressNumber ipv6_number;
EXPECT_TRUE(ParseIPLiteralToNumber("::1", &ipv4_number));
EXPECT_FALSE(IsIPv4Mapped(ipv6_number));
IPAddressNumber ipv4mapped_number;
EXPECT_TRUE(ParseIPLiteralToNumber("::ffff:0101:1", &ipv4mapped_number));
EXPECT_TRUE(IsIPv4Mapped(ipv4mapped_number));
}
TEST(NetUtilTest, ConvertIPv4MappedToIPv4) {
IPAddressNumber ipv4mapped_number;
EXPECT_TRUE(ParseIPLiteralToNumber("::ffff:0101:1", &ipv4mapped_number));
IPAddressNumber expected;
EXPECT_TRUE(ParseIPLiteralToNumber("1.1.0.1", &expected));
IPAddressNumber result = ConvertIPv4MappedToIPv4(ipv4mapped_number);
EXPECT_EQ(expected, result);
}
// Test parsing invalid CIDR notation literals. // Test parsing invalid CIDR notation literals.
TEST(NetUtilTest, ParseCIDRBlock_Invalid) { TEST(NetUtilTest, ParseCIDRBlock_Invalid) {
const char* bad_literals[] = { const char* bad_literals[] = {
......
// 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_ADDRESS_SORTER_H_
#define NET_DNS_ADDRESS_SORTER_H_
#include "base/basictypes.h"
#include "base/callback.h"
#include "base/memory/scoped_ptr.h"
#include "net/base/net_export.h"
namespace net {
class AddressList;
// Sorts AddressList according to RFC3484, by likelihood of successful
// connection. Depending on the platform, the sort could be performed
// asynchronously by the OS, or synchronously by local implementation.
// AddressSorter does not necessarily preserve port numbers on the sorted list.
class NET_EXPORT AddressSorter {
public:
typedef base::Callback<void(bool success,
const AddressList& list)> CallbackType;
virtual ~AddressSorter() {}
// Sorts |list|, which must include at least one IPv6 address.
// Calls |callback| upon completion. Could complete synchronously. Could
// complete after this AddressSorter is destroyed.
virtual void Sort(const AddressList& list,
const CallbackType& callback) const = 0;
// Creates platform-dependent AddressSorter.
static scoped_ptr<AddressSorter> CreateAddressSorter();
protected:
AddressSorter() {}
private:
DISALLOW_COPY_AND_ASSIGN(AddressSorter);
};
} // namespace net
#endif // NET_DNS_ADDRESS_SORTER_H_
This diff is collapsed.
// 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_ADDRESS_SORTER_POSIX_H_
#define NET_DNS_ADDRESS_SORTER_POSIX_H_
#include <map>
#include <vector>
#include "base/threading/non_thread_safe.h"
#include "net/base/address_list.h"
#include "net/base/net_errors.h"
#include "net/base/net_export.h"
#include "net/base/net_util.h"
#include "net/base/network_change_notifier.h"
#include "net/dns/address_sorter.h"
namespace net {
class ClientSocketFactory;
// This implementation uses explicit policy to perform the sorting. It is not
// thread-safe and always completes synchronously.
class NET_EXPORT_PRIVATE AddressSorterPosix
: public AddressSorter,
public base::NonThreadSafe,
public NetworkChangeNotifier::IPAddressObserver {
public:
// Generic policy entry.
struct PolicyEntry {
// IPv4 addresses must be mapped to IPv6.
unsigned char prefix[kIPv6AddressSize];
unsigned prefix_length;
unsigned value;
};
typedef std::vector<PolicyEntry> PolicyTable;
enum AddressScope {
SCOPE_UNDEFINED = 0,
SCOPE_NODELOCAL = 1,
SCOPE_LINKLOCAL = 2,
SCOPE_SITELOCAL = 5,
SCOPE_ORGLOCAL = 8,
SCOPE_GLOBAL = 14,
};
struct SourceAddressInfo {
// Values read from policy tables.
AddressScope scope;
unsigned label;
// Values from the OS, matter only if more than one source address is used.
unsigned prefix_length;
bool deprecated; // vs. preferred RFC4862
bool home; // vs. care-of RFC6275
bool native;
};
typedef std::map<IPAddressNumber, SourceAddressInfo> SourceAddressMap;
explicit AddressSorterPosix(ClientSocketFactory* socket_factory);
virtual ~AddressSorterPosix();
// AddressSorter:
virtual void Sort(const AddressList& list,
const CallbackType& callback) const OVERRIDE;
private:
friend class AddressSorterPosixTest;
// NetworkChangeNotifier::IPAddressObserver:
virtual void OnIPAddressChanged() OVERRIDE;
// Fills |info| with values for |address| from policy tables.
void FillPolicy(const IPAddressNumber& address,
SourceAddressInfo* info) const;
// Mutable to allow using default values for source addresses which were not
// found in most recent OnIPAddressChanged.
mutable SourceAddressMap source_map_;
ClientSocketFactory* socket_factory_;
PolicyTable precedence_table_;
PolicyTable label_table_;
PolicyTable ipv4_scope_table_;
DISALLOW_COPY_AND_ASSIGN(AddressSorterPosix);
};
} // namespace net
#endif // NET_DNS_ADDRESS_SORTER_POSIX_H_
This diff is collapsed.
// 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/address_sorter.h"
#if defined(OS_WIN)
#include <winsock2.h>
#endif
#include "base/bind.h"
#include "base/logging.h"
#include "net/base/address_list.h"
#include "net/base/net_util.h"
#include "net/base/test_completion_callback.h"
#include "testing/gtest/include/gtest/gtest.h"
#if defined(OS_WIN)
#include "net/base/winsock_init.h"
#endif
namespace net {
namespace {
IPEndPoint MakeEndPoint(const std::string& str) {
IPAddressNumber addr;
CHECK(ParseIPLiteralToNumber(str, &addr));
return IPEndPoint(addr, 0);
}
void OnSortComplete(AddressList* result_buf,
const CompletionCallback& callback,
bool success,
const AddressList& result) {
if (success)
*result_buf = result;
callback.Run(success ? OK : ERR_FAILED);
}
TEST(AddressSorterTest, Sort) {
int expected_result = OK;
#if defined(OS_WIN)
EnsureWinsockInit();
SOCKET sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
if (sock == INVALID_SOCKET) {
expected_result = ERR_FAILED;
} else {
closesocket(sock);
}
#endif
scoped_ptr<AddressSorter> sorter(AddressSorter::CreateAddressSorter());
AddressList list;
list.push_back(MakeEndPoint("10.0.0.1"));
list.push_back(MakeEndPoint("8.8.8.8"));
list.push_back(MakeEndPoint("::1"));
list.push_back(MakeEndPoint("2001:4860:4860::8888"));
AddressList result;
TestCompletionCallback callback;
sorter->Sort(list, base::Bind(&OnSortComplete, &result,
callback.callback()));
EXPECT_EQ(expected_result, callback.WaitForResult());
}
} // namespace
} // namespace net
// 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/address_sorter.h"
#include <winsock2.h>
#include <algorithm>
#include "base/bind.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/threading/worker_pool.h"
#include "base/win/windows_version.h"
#include "net/base/address_list.h"
#include "net/base/ip_endpoint.h"
#include "net/base/winsock_init.h"
namespace net {
namespace {
class AddressSorterWin : public AddressSorter {
public:
AddressSorterWin() {
EnsureWinsockInit();
}
virtual ~AddressSorterWin() {}
// AddressSorter:
virtual void Sort(const AddressList& list,
const CallbackType& callback) const OVERRIDE {
DCHECK(!list.empty());
scoped_refptr<Job> job = new Job(list, callback);
}
private:
// Executes the SIO_ADDRESS_LIST_SORT ioctl on the WorkerPool, and
// performs the necessary conversions to/from AddressList.
class Job : public base::RefCountedThreadSafe<Job> {
public:
Job(const AddressList& list, const CallbackType& callback)
: callback_(callback),
buffer_size_(sizeof(SOCKET_ADDRESS_LIST) +
list.size() * (sizeof(SOCKET_ADDRESS) +
sizeof(SOCKADDR_STORAGE))),
input_buffer_(reinterpret_cast<SOCKET_ADDRESS_LIST*>(
malloc(buffer_size_))),
output_buffer_(reinterpret_cast<SOCKET_ADDRESS_LIST*>(
malloc(buffer_size_))),
success_(false) {
input_buffer_->iAddressCount = list.size();
SOCKADDR_STORAGE* storage = reinterpret_cast<SOCKADDR_STORAGE*>(
input_buffer_->Address + input_buffer_->iAddressCount);
for (size_t i = 0; i < list.size(); ++i) {
IPEndPoint ipe = list[i];
// Addresses must be sockaddr_in6.
if (ipe.GetFamily() == AF_INET) {
ipe = IPEndPoint(ConvertIPv4NumberToIPv6Number(ipe.address()),
ipe.port());
}
struct sockaddr* addr = reinterpret_cast<struct sockaddr*>(storage + i);
socklen_t addr_len = sizeof(SOCKADDR_STORAGE);
bool result = ipe.ToSockAddr(addr, &addr_len);
DCHECK(result);
input_buffer_->Address[i].lpSockaddr = addr;
input_buffer_->Address[i].iSockaddrLength = addr_len;
}
if (!base::WorkerPool::PostTaskAndReply(
FROM_HERE,
base::Bind(&Job::Run, this),
base::Bind(&Job::OnComplete, this),
false /* task is slow */)) {
LOG(ERROR) << "WorkerPool::PostTaskAndReply failed";
OnComplete();
}
}
private:
friend class base::RefCountedThreadSafe<Job>;
~Job() {}
// Executed on the WorkerPool.
void Run() {
SOCKET sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
if (sock == INVALID_SOCKET)
return;
DWORD result_size = 0;
int result = WSAIoctl(sock, SIO_ADDRESS_LIST_SORT, input_buffer_.get(),
buffer_size_, output_buffer_.get(), buffer_size_,
&result_size, NULL, NULL);
if (result == SOCKET_ERROR) {
LOG(ERROR) << "SIO_ADDRESS_LIST_SORT failed " << WSAGetLastError();
} else {
success_ = true;
}
closesocket(sock);
}
// Executed on the calling thread.
void OnComplete() {
AddressList list;
if (success_) {
list.reserve(output_buffer_->iAddressCount);
for (int i = 0; i < output_buffer_->iAddressCount; ++i) {
IPEndPoint ipe;
ipe.FromSockAddr(output_buffer_->Address[i].lpSockaddr,
output_buffer_->Address[i].iSockaddrLength);
// Unmap V4MAPPED IPv6 addresses so that Happy Eyeballs works.
if (IsIPv4Mapped(ipe.address())) {
ipe = IPEndPoint(ConvertIPv4MappedToIPv4(ipe.address()),
ipe.port());
}
list.push_back(ipe);
}
}
callback_.Run(success_, list);
}
const CallbackType callback_;
const size_t buffer_size_;
scoped_ptr_malloc<SOCKET_ADDRESS_LIST> input_buffer_;
scoped_ptr_malloc<SOCKET_ADDRESS_LIST> output_buffer_;
bool success_;
DISALLOW_COPY_AND_ASSIGN(Job);
};
DISALLOW_COPY_AND_ASSIGN(AddressSorterWin);
};
// Merges |list_ipv4| and |list_ipv6| before passing it to |callback|, but
// only if |success| is true.
void MergeResults(const AddressSorter::CallbackType& callback,
const AddressList& list_ipv4,
bool success,
const AddressList& list_ipv6) {
if (!success) {
callback.Run(false, AddressList());
return;
}
AddressList list;
list.insert(list.end(), list_ipv6.begin(), list_ipv6.end());
list.insert(list.end(), list_ipv4.begin(), list_ipv4.end());
callback.Run(true, list);
}
// Wrapper for AddressSorterWin which does not sort IPv4 or IPv4-mapped
// addresses but always puts them at the end of the list. Needed because the
// SIO_ADDRESS_LIST_SORT does not support IPv4 addresses on Windows XP.
class AddressSorterWinXP : public AddressSorter {
public:
AddressSorterWinXP() {}
virtual ~AddressSorterWinXP() {}
// AddressSorter:
virtual void Sort(const AddressList& list,
const CallbackType& callback) const OVERRIDE {
AddressList list_ipv4;
AddressList list_ipv6;
for (size_t i = 0; i < list.size(); ++i) {
const IPEndPoint& ipe = list[i];
if (ipe.GetFamily() == AF_INET) {
list_ipv4.push_back(ipe);
} else {
list_ipv6.push_back(ipe);
}
}
if (!list_ipv6.empty()) {
sorter_.Sort(list_ipv6, base::Bind(&MergeResults, callback, list_ipv4));
} else {
NOTREACHED() << "Should not be called with IPv4-only addresses.";
callback.Run(true, list);
}
}
private:
AddressSorterWin sorter_;
DISALLOW_COPY_AND_ASSIGN(AddressSorterWinXP);
};
} // namespace
// static
scoped_ptr<AddressSorter> AddressSorter::CreateAddressSorter() {
if (base::win::GetVersion() < base::win::VERSION_VISTA)
return scoped_ptr<AddressSorter>(new AddressSorterWinXP());
return scoped_ptr<AddressSorter>(new AddressSorterWin());
}
} // namespace net
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "base/bind.h" #include "base/bind.h"
#include "base/rand_util.h" #include "base/rand_util.h"
#include "net/base/net_log.h" #include "net/base/net_log.h"
#include "net/dns/address_sorter.h"
#include "net/dns/dns_config_service.h" #include "net/dns/dns_config_service.h"
#include "net/dns/dns_session.h" #include "net/dns/dns_session.h"
#include "net/dns/dns_transaction.h" #include "net/dns/dns_transaction.h"
...@@ -18,7 +19,9 @@ namespace { ...@@ -18,7 +19,9 @@ namespace {
class DnsClientImpl : public DnsClient { class DnsClientImpl : public DnsClient {
public: public:
explicit DnsClientImpl(NetLog* net_log) : net_log_(net_log) {} explicit DnsClientImpl(NetLog* net_log)
: address_sorter_(AddressSorter::CreateAddressSorter()),
net_log_(net_log) {}
virtual void SetConfig(const DnsConfig& config) OVERRIDE { virtual void SetConfig(const DnsConfig& config) OVERRIDE {
factory_.reset(); factory_.reset();
...@@ -40,9 +43,14 @@ class DnsClientImpl : public DnsClient { ...@@ -40,9 +43,14 @@ class DnsClientImpl : public DnsClient {
return session_.get() ? factory_.get() : NULL; return session_.get() ? factory_.get() : NULL;
} }
virtual AddressSorter* GetAddressSorter() OVERRIDE {
return address_sorter_.get();
}
private: private:
scoped_refptr<DnsSession> session_; scoped_refptr<DnsSession> session_;
scoped_ptr<DnsTransactionFactory> factory_; scoped_ptr<DnsTransactionFactory> factory_;
scoped_ptr<AddressSorter> address_sorter_;
NetLog* net_log_; NetLog* net_log_;
}; };
......
...@@ -10,12 +10,14 @@ ...@@ -10,12 +10,14 @@
namespace net { namespace net {
class AddressSorter;
struct DnsConfig; struct DnsConfig;
class DnsTransactionFactory; class DnsTransactionFactory;
class NetLog; class NetLog;
// Convenience wrapper allows easy injection of DnsTransaction into // Convenience wrapper which allows easy injection of DnsTransaction into
// HostResolverImpl. // HostResolverImpl. Pointers returned by the Get* methods are only guaranteed
// to remain valid until next time SetConfig is called.
class NET_EXPORT DnsClient { class NET_EXPORT DnsClient {
public: public:
virtual ~DnsClient() {} virtual ~DnsClient() {}
...@@ -29,6 +31,9 @@ class NET_EXPORT DnsClient { ...@@ -29,6 +31,9 @@ class NET_EXPORT DnsClient {
// Returns NULL if the current config is not valid. // Returns NULL if the current config is not valid.
virtual DnsTransactionFactory* GetTransactionFactory() = 0; virtual DnsTransactionFactory* GetTransactionFactory() = 0;
// Returns NULL if the current config is not valid.
virtual AddressSorter* GetAddressSorter() = 0;
// Creates default client. // Creates default client.
static scoped_ptr<DnsClient> CreateClient(NetLog* net_log); static scoped_ptr<DnsClient> CreateClient(NetLog* net_log);
}; };
......
...@@ -280,15 +280,13 @@ DnsResponse::Result DnsResponse::ParseToAddressList( ...@@ -280,15 +280,13 @@ DnsResponse::Result DnsResponse::ParseToAddressList(
} }
// TODO(szym): Extract TTL for NODATA results. http://crbug.com/115051 // TODO(szym): Extract TTL for NODATA results. http://crbug.com/115051
if (ip_addresses.empty())
return DNS_NO_ADDRESSES;
// getcanonname in eglibc returns the first owner name of an A or AAAA RR. // 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. // If the response passed all the checks so far, then |expected_name| is it.
*addr_list = AddressList::CreateFromIPAddressList(ip_addresses, *addr_list = AddressList::CreateFromIPAddressList(ip_addresses,
expected_name); expected_name);
*ttl = base::TimeDelta::FromSeconds(std::min(cname_ttl_sec, addr_ttl_sec)); *ttl = base::TimeDelta::FromSeconds(std::min(cname_ttl_sec, addr_ttl_sec));
return DNS_SUCCESS; return DNS_PARSE_OK;
} }
} // namespace net } // namespace net
...@@ -81,7 +81,7 @@ class NET_EXPORT_PRIVATE DnsResponse { ...@@ -81,7 +81,7 @@ class NET_EXPORT_PRIVATE DnsResponse {
public: public:
// Possible results from ParseToAddressList. // Possible results from ParseToAddressList.
enum Result { enum Result {
DNS_SUCCESS = 0, DNS_PARSE_OK = 0,
DNS_MALFORMED_RESPONSE, // DnsRecordParser failed before the end of DNS_MALFORMED_RESPONSE, // DnsRecordParser failed before the end of
// packet. // packet.
DNS_MALFORMED_CNAME, // Could not parse CNAME out of RRDATA. DNS_MALFORMED_CNAME, // Could not parse CNAME out of RRDATA.
...@@ -90,7 +90,7 @@ class NET_EXPORT_PRIVATE DnsResponse { ...@@ -90,7 +90,7 @@ class NET_EXPORT_PRIVATE DnsResponse {
DNS_SIZE_MISMATCH, // Got an address but size does not match. DNS_SIZE_MISMATCH, // Got an address but size does not match.
DNS_CNAME_AFTER_ADDRESS, // Found CNAME after an address record. DNS_CNAME_AFTER_ADDRESS, // Found CNAME after an address record.
DNS_ADDRESS_TTL_MISMATCH, // TTL of all address records are not identical. DNS_ADDRESS_TTL_MISMATCH, // TTL of all address records are not identical.
DNS_NO_ADDRESSES, // No address records found. DNS_NO_ADDRESSES, // OBSOLETE. No longer used.
// Only add new values here. // Only add new values here.
DNS_PARSE_RESULT_MAX, // Bounding value for histograms. DNS_PARSE_RESULT_MAX, // Bounding value for histograms.
}; };
......
...@@ -299,7 +299,7 @@ TEST(DnsResponseTest, ParseToAddressList) { ...@@ -299,7 +299,7 @@ TEST(DnsResponseTest, ParseToAddressList) {
DnsResponse response(t.response_data, t.response_size, t.query_size); DnsResponse response(t.response_data, t.response_size, t.query_size);
AddressList addr_list; AddressList addr_list;
base::TimeDelta ttl; base::TimeDelta ttl;
EXPECT_EQ(DnsResponse::DNS_SUCCESS, EXPECT_EQ(DnsResponse::DNS_PARSE_OK,
response.ParseToAddressList(&addr_list, &ttl)); response.ParseToAddressList(&addr_list, &ttl));
std::vector<const char*> expected_addresses( std::vector<const char*> expected_addresses(
t.expected_addresses, t.expected_addresses,
...@@ -429,8 +429,9 @@ TEST(DnsResponseTest, ParseToAddressListFail) { ...@@ -429,8 +429,9 @@ TEST(DnsResponseTest, ParseToAddressListFail) {
DnsResponse::DNS_CNAME_AFTER_ADDRESS }, DnsResponse::DNS_CNAME_AFTER_ADDRESS },
{ kResponseTTLMismatch, arraysize(kResponseTTLMismatch), { kResponseTTLMismatch, arraysize(kResponseTTLMismatch),
DnsResponse::DNS_ADDRESS_TTL_MISMATCH }, DnsResponse::DNS_ADDRESS_TTL_MISMATCH },
// Not actually a failure, just an empty result.
{ kResponseNoAddresses, arraysize(kResponseNoAddresses), { kResponseNoAddresses, arraysize(kResponseNoAddresses),
DnsResponse::DNS_NO_ADDRESSES }, DnsResponse::DNS_PARSE_OK },
}; };
const size_t kQuerySize = 12 + 7; const size_t kQuerySize = 12 + 7;
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "net/base/dns_util.h" #include "net/base/dns_util.h"
#include "net/base/io_buffer.h" #include "net/base/io_buffer.h"
#include "net/base/net_errors.h" #include "net/base/net_errors.h"
#include "net/dns/address_sorter.h"
#include "net/dns/dns_client.h" #include "net/dns/dns_client.h"
#include "net/dns/dns_config_service.h" #include "net/dns/dns_config_service.h"
#include "net/dns/dns_protocol.h" #include "net/dns/dns_protocol.h"
...@@ -25,19 +26,29 @@ ...@@ -25,19 +26,29 @@
namespace net { namespace net {
namespace { namespace {
// A DnsTransaction which responds with loopback to all queries starting with // A DnsTransaction which uses MockDnsClientRuleList to determine the response.
// "ok", fails synchronously on all queries starting with "er", and NXDOMAIN to
// all others.
class MockTransaction : public DnsTransaction, class MockTransaction : public DnsTransaction,
public base::SupportsWeakPtr<MockTransaction> { public base::SupportsWeakPtr<MockTransaction> {
public: public:
MockTransaction(const std::string& hostname, MockTransaction(const MockDnsClientRuleList& rules,
const std::string& hostname,
uint16 qtype, uint16 qtype,
const DnsTransactionFactory::CallbackType& callback) const DnsTransactionFactory::CallbackType& callback)
: hostname_(hostname), : result_(MockDnsClientRule::FAIL_SYNC),
hostname_(hostname),
qtype_(qtype), qtype_(qtype),
callback_(callback), callback_(callback),
started_(false) { started_(false) {
// Find the relevant rule which matches |qtype| and prefix of |hostname|.
for (size_t i = 0; i < rules.size(); ++i) {
const std::string& prefix = rules[i].prefix;
if ((rules[i].qtype == qtype) &&
(hostname.size() >= prefix.size()) &&
(hostname.compare(0, prefix.size(), prefix) == 0)) {
result_ = rules[i].result;
break;
}
}
} }
virtual const std::string& GetHostname() const OVERRIDE { virtual const std::string& GetHostname() const OVERRIDE {
...@@ -51,7 +62,7 @@ class MockTransaction : public DnsTransaction, ...@@ -51,7 +62,7 @@ class MockTransaction : public DnsTransaction,
virtual int Start() OVERRIDE { virtual int Start() OVERRIDE {
EXPECT_FALSE(started_); EXPECT_FALSE(started_);
started_ = true; started_ = true;
if (hostname_.substr(0, 2) == "er") if (MockDnsClientRule::FAIL_SYNC == result_)
return ERR_NAME_NOT_RESOLVED; return ERR_NAME_NOT_RESOLVED;
// Using WeakPtr to cleanly cancel when transaction is destroyed. // Using WeakPtr to cleanly cancel when transaction is destroyed.
MessageLoop::current()->PostTask( MessageLoop::current()->PostTask(
...@@ -62,54 +73,66 @@ class MockTransaction : public DnsTransaction, ...@@ -62,54 +73,66 @@ class MockTransaction : public DnsTransaction,
private: private:
void Finish() { void Finish() {
if (hostname_.substr(0, 2) == "ok") { switch (result_) {
std::string qname; case MockDnsClientRule::EMPTY:
DNSDomainFromDot(hostname_, &qname); case MockDnsClientRule::OK: {
DnsQuery query(0, qname, qtype_); std::string qname;
DNSDomainFromDot(hostname_, &qname);
DnsResponse response; DnsQuery query(0, qname, qtype_);
char* buffer = response.io_buffer()->data();
int nbytes = query.io_buffer()->size(); DnsResponse response;
memcpy(buffer, query.io_buffer()->data(), nbytes); char* buffer = response.io_buffer()->data();
int nbytes = query.io_buffer()->size();
const uint16 kPointerToQueryName = memcpy(buffer, query.io_buffer()->data(), nbytes);
static_cast<uint16>(0xc000 | sizeof(net::dns_protocol::Header)); dns_protocol::Header* header =
reinterpret_cast<dns_protocol::Header*>(buffer);
const uint32 kTTL = 86400; // One day. header->flags |= dns_protocol::kFlagResponse;
// Size of RDATA which is a IPv4 or IPv6 address. if (MockDnsClientRule::OK == result_) {
size_t rdata_size = qtype_ == net::dns_protocol::kTypeA ? const uint16 kPointerToQueryName =
net::kIPv4AddressSize : net::kIPv6AddressSize; static_cast<uint16>(0xc000 | sizeof(*header));
// 12 is the sum of sizes of the compressed name reference, TYPE, const uint32 kTTL = 86400; // One day.
// CLASS, TTL and RDLENGTH.
size_t answer_size = 12 + rdata_size; // Size of RDATA which is a IPv4 or IPv6 address.
size_t rdata_size = qtype_ == net::dns_protocol::kTypeA ?
// Write answer with loopback IP address. net::kIPv4AddressSize : net::kIPv6AddressSize;
reinterpret_cast<dns_protocol::Header*>(buffer)->ancount =
base::HostToNet16(1); // 12 is the sum of sizes of the compressed name reference, TYPE,
BigEndianWriter writer(buffer + nbytes, answer_size); // CLASS, TTL and RDLENGTH.
writer.WriteU16(kPointerToQueryName); size_t answer_size = 12 + rdata_size;
writer.WriteU16(qtype_);
writer.WriteU16(net::dns_protocol::kClassIN); // Write answer with loopback IP address.
writer.WriteU32(kTTL); header->ancount = base::HostToNet16(1);
writer.WriteU16(rdata_size); BigEndianWriter writer(buffer + nbytes, answer_size);
if (qtype_ == net::dns_protocol::kTypeA) { writer.WriteU16(kPointerToQueryName);
char kIPv4Loopback[] = { 0x7f, 0, 0, 1 }; writer.WriteU16(qtype_);
writer.WriteBytes(kIPv4Loopback, sizeof(kIPv4Loopback)); writer.WriteU16(net::dns_protocol::kClassIN);
} else { writer.WriteU32(kTTL);
char kIPv6Loopback[] = { 0, 0, 0, 0, 0, 0, 0, 0, writer.WriteU16(rdata_size);
0, 0, 0, 0, 0, 0, 0, 1 }; if (qtype_ == net::dns_protocol::kTypeA) {
writer.WriteBytes(kIPv6Loopback, sizeof(kIPv6Loopback)); char kIPv4Loopback[] = { 0x7f, 0, 0, 1 };
} writer.WriteBytes(kIPv4Loopback, sizeof(kIPv4Loopback));
} else {
EXPECT_TRUE(response.InitParse(nbytes + answer_size, query)); char kIPv6Loopback[] = { 0, 0, 0, 0, 0, 0, 0, 0,
callback_.Run(this, OK, &response); 0, 0, 0, 0, 0, 0, 0, 1 };
} else { writer.WriteBytes(kIPv6Loopback, sizeof(kIPv6Loopback));
callback_.Run(this, ERR_NAME_NOT_RESOLVED, NULL); }
nbytes += answer_size;
}
EXPECT_TRUE(response.InitParse(nbytes, query));
callback_.Run(this, OK, &response);
} break;
case MockDnsClientRule::FAIL_ASYNC:
callback_.Run(this, ERR_NAME_NOT_RESOLVED, NULL);
break;
default:
NOTREACHED();
break;
} }
} }
MockDnsClientRule::Result result_;
const std::string hostname_; const std::string hostname_;
const uint16 qtype_; const uint16 qtype_;
DnsTransactionFactory::CallbackType callback_; DnsTransactionFactory::CallbackType callback_;
...@@ -120,7 +143,8 @@ class MockTransaction : public DnsTransaction, ...@@ -120,7 +143,8 @@ class MockTransaction : public DnsTransaction,
// A DnsTransactionFactory which creates MockTransaction. // A DnsTransactionFactory which creates MockTransaction.
class MockTransactionFactory : public DnsTransactionFactory { class MockTransactionFactory : public DnsTransactionFactory {
public: public:
MockTransactionFactory() {} explicit MockTransactionFactory(const MockDnsClientRuleList& rules)
: rules_(rules) {}
virtual ~MockTransactionFactory() {} virtual ~MockTransactionFactory() {}
virtual scoped_ptr<DnsTransaction> CreateTransaction( virtual scoped_ptr<DnsTransaction> CreateTransaction(
...@@ -129,14 +153,29 @@ class MockTransactionFactory : public DnsTransactionFactory { ...@@ -129,14 +153,29 @@ class MockTransactionFactory : public DnsTransactionFactory {
const DnsTransactionFactory::CallbackType& callback, const DnsTransactionFactory::CallbackType& callback,
const BoundNetLog&) OVERRIDE { const BoundNetLog&) OVERRIDE {
return scoped_ptr<DnsTransaction>( return scoped_ptr<DnsTransaction>(
new MockTransaction(hostname, qtype, callback)); new MockTransaction(rules_, hostname, qtype, callback));
}
private:
MockDnsClientRuleList rules_;
};
class MockAddressSorter : public AddressSorter {
public:
virtual ~MockAddressSorter() {}
virtual void Sort(const AddressList& list,
const CallbackType& callback) const OVERRIDE {
// Do nothing.
callback.Run(true, list);
} }
}; };
// MockDnsClient provides MockTransactionFactory. // MockDnsClient provides MockTransactionFactory.
class MockDnsClient : public DnsClient { class MockDnsClient : public DnsClient {
public: public:
explicit MockDnsClient(const DnsConfig& config) : config_(config) {} MockDnsClient(const DnsConfig& config,
const MockDnsClientRuleList& rules)
: config_(config), factory_(rules) {}
virtual ~MockDnsClient() {} virtual ~MockDnsClient() {}
virtual void SetConfig(const DnsConfig& config) OVERRIDE { virtual void SetConfig(const DnsConfig& config) OVERRIDE {
...@@ -151,16 +190,22 @@ class MockDnsClient : public DnsClient { ...@@ -151,16 +190,22 @@ class MockDnsClient : public DnsClient {
return config_.IsValid() ? &factory_ : NULL; return config_.IsValid() ? &factory_ : NULL;
} }
virtual AddressSorter* GetAddressSorter() OVERRIDE {
return &address_sorter_;
}
private: private:
DnsConfig config_; DnsConfig config_;
MockTransactionFactory factory_; MockTransactionFactory factory_;
MockAddressSorter address_sorter_;
}; };
} // namespace } // namespace
// static // static
scoped_ptr<DnsClient> CreateMockDnsClient(const DnsConfig& config) { scoped_ptr<DnsClient> CreateMockDnsClient(const DnsConfig& config,
return scoped_ptr<DnsClient>(new MockDnsClient(config)); const MockDnsClientRuleList& rules) {
return scoped_ptr<DnsClient>(new MockDnsClient(config, rules));
} }
MockDnsConfigService::~MockDnsConfigService() { MockDnsConfigService::~MockDnsConfigService() {
......
...@@ -5,6 +5,9 @@ ...@@ -5,6 +5,9 @@
#ifndef NET_DNS_DNS_TEST_UTIL_H_ #ifndef NET_DNS_DNS_TEST_UTIL_H_
#define NET_DNS_DNS_TEST_UTIL_H_ #define NET_DNS_DNS_TEST_UTIL_H_
#include <string>
#include <vector>
#include "base/basictypes.h" #include "base/basictypes.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "net/dns/dns_config_service.h" #include "net/dns/dns_config_service.h"
...@@ -166,8 +169,25 @@ static const int kT3TTL = 0x00000015; ...@@ -166,8 +169,25 @@ static const int kT3TTL = 0x00000015;
static const unsigned kT3RecordCount = arraysize(kT3IpAddresses) + 2; static const unsigned kT3RecordCount = arraysize(kT3IpAddresses) + 2;
class DnsClient; class DnsClient;
struct MockDnsClientRule {
enum Result {
FAIL_SYNC, // Fail synchronously with ERR_NAME_NOT_RESOLVED.
FAIL_ASYNC, // Fail asynchronously with ERR_NAME_NOT_RESOLVED.
EMPTY, // Return an empty response.
OK, // Return a response with loopback address.
};
std::string prefix;
uint16 qtype;
Result result;
};
typedef std::vector<MockDnsClientRule> MockDnsClientRuleList;
// Creates mock DnsClient for testing HostResolverImpl. // Creates mock DnsClient for testing HostResolverImpl.
scoped_ptr<DnsClient> CreateMockDnsClient(const DnsConfig& config); scoped_ptr<DnsClient> CreateMockDnsClient(const DnsConfig& config,
const MockDnsClientRuleList& rules);
class MockDnsConfigService : public DnsConfigService { class MockDnsConfigService : public DnsConfigService {
public: public:
......
...@@ -364,6 +364,10 @@ ...@@ -364,6 +364,10 @@
'disk_cache/stress_support.h', 'disk_cache/stress_support.h',
'disk_cache/trace.cc', 'disk_cache/trace.cc',
'disk_cache/trace.h', 'disk_cache/trace.h',
'dns/address_sorter.h',
'dns/address_sorter_posix.cc',
'dns/address_sorter_posix.h',
'dns/address_sorter_win.cc',
'dns/dns_client.cc', 'dns/dns_client.cc',
'dns/dns_client.h', 'dns/dns_client.h',
'dns/dns_config_service.cc', 'dns/dns_config_service.cc',
...@@ -1195,6 +1199,8 @@ ...@@ -1195,6 +1199,8 @@
'disk_cache/entry_unittest.cc', 'disk_cache/entry_unittest.cc',
'disk_cache/mapped_file_unittest.cc', 'disk_cache/mapped_file_unittest.cc',
'disk_cache/storage_block_unittest.cc', 'disk_cache/storage_block_unittest.cc',
'dns/address_sorter_posix_unittest.cc',
'dns/address_sorter_unittest.cc',
'dns/dns_config_service_posix_unittest.cc', 'dns/dns_config_service_posix_unittest.cc',
'dns/dns_config_service_unittest.cc', 'dns/dns_config_service_unittest.cc',
'dns/dns_config_service_win_unittest.cc', 'dns/dns_config_service_win_unittest.cc',
......
...@@ -149,7 +149,7 @@ void RunTestCase(uint16 id, std::string& qname, uint16 qtype, ...@@ -149,7 +149,7 @@ void RunTestCase(uint16 id, std::string& qname, uint16 qtype,
base::TimeDelta ttl; base::TimeDelta ttl;
net::DnsResponse::Result result = response.ParseToAddressList( net::DnsResponse::Result result = response.ParseToAddressList(
&address_list, &ttl); &address_list, &ttl);
if (result != net::DnsResponse::DNS_SUCCESS) { if (result != net::DnsResponse::DNS_PARSE_OK) {
LOG(INFO) << "ParseToAddressList failed: " << result; LOG(INFO) << "ParseToAddressList failed: " << result;
return; return;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment