diff options
-rw-r--r-- | net/base/host_resolver_impl.cc | 46 | ||||
-rw-r--r-- | net/base/host_resolver_impl_unittest.cc | 155 | ||||
-rw-r--r-- | net/base/net_error_list.h | 3 | ||||
-rw-r--r-- | net/dns/dns_transaction.cc | 2 | ||||
-rw-r--r-- | net/dns/dns_transaction_unittest.cc | 2 |
5 files changed, 123 insertions, 85 deletions
diff --git a/net/base/host_resolver_impl.cc b/net/base/host_resolver_impl.cc index 811a002..edab00e 100644 --- a/net/base/host_resolver_impl.cc +++ b/net/base/host_resolver_impl.cc @@ -137,6 +137,7 @@ enum DnsResolveStatus { RESOLVE_STATUS_DNS_SUCCESS = 0, RESOLVE_STATUS_PROC_SUCCESS, RESOLVE_STATUS_FAIL, + RESOLVE_STATUS_SUSPECT_NETBIOS, RESOLVE_STATUS_MAX }; @@ -146,6 +147,25 @@ void UmaAsyncDnsResolveStatus(DnsResolveStatus result) { RESOLVE_STATUS_MAX); } +bool ResemblesNetBIOSName(const std::string& hostname) { + return (hostname.size() < 16) && (hostname.find('.') == std::string::npos); +} + +// True if |hostname| ends with either ".local" or ".local.". +bool ResemblesMulticastDNSName(const std::string& hostname) { + DCHECK(!hostname.empty()); + const char kSuffix[] = ".local."; + const size_t kSuffixLen = sizeof(kSuffix) - 1; + const size_t kSuffixLenTrimmed = kSuffixLen - 1; + if (hostname[hostname.size() - 1] == '.') { + return hostname.size() > kSuffixLen && + !hostname.compare(hostname.size() - kSuffixLen, kSuffixLen, kSuffix); + } + return hostname.size() > kSuffixLenTrimmed && + !hostname.compare(hostname.size() - kSuffixLenTrimmed, kSuffixLenTrimmed, + kSuffix, kSuffixLenTrimmed); +} + //----------------------------------------------------------------------------- // Wraps call to SystemHostResolverProc as an instance of HostResolverProc. @@ -1055,7 +1075,7 @@ class HostResolverImpl::Job : public PrioritizedDispatcher::Job { : resolver_(resolver->AsWeakPtr()), key_(key), had_non_speculative_request_(false), - had_dns_config_(false), + dns_task_error_(OK), net_log_(BoundNetLog::Make(request_net_log.net_log(), NetLog::SOURCE_HOST_RESOLVER_IMPL_JOB)) { request_net_log.AddEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_CREATE_JOB); @@ -1219,9 +1239,9 @@ class HostResolverImpl::Job : public PrioritizedDispatcher::Job { net_log_.AddEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_JOB_STARTED); - had_dns_config_ = resolver_->HaveDnsConfig(); - // Job::Start must not complete synchronously. - if (had_dns_config_) { + // Caution: Job::Start must not complete synchronously. + if (resolver_->HaveDnsConfig() && + !ResemblesMulticastDNSName(key_.hostname)) { StartDnsTask(); } else { StartProcTask(); @@ -1251,10 +1271,17 @@ class HostResolverImpl::Job : public PrioritizedDispatcher::Job { void OnProcTaskComplete(int net_error, const AddressList& addr_list) { DCHECK(is_proc_running()); - if (had_dns_config_) { - // TODO(szym): guess if the hostname is a NetBIOS name and discount it. + if (dns_task_error_ != OK) { if (net_error == OK) { - UmaAsyncDnsResolveStatus(RESOLVE_STATUS_PROC_SUCCESS); + if ((dns_task_error_ == ERR_NAME_NOT_RESOLVED) && + ResemblesNetBIOSName(key_.hostname)) { + UmaAsyncDnsResolveStatus(RESOLVE_STATUS_SUSPECT_NETBIOS); + } else { + UmaAsyncDnsResolveStatus(RESOLVE_STATUS_PROC_SUCCESS); + } + UMA_HISTOGRAM_CUSTOM_ENUMERATION("AsyncDNS.ResolveError", + std::abs(dns_task_error_), + GetAllErrorCodesForUma()); } else { UmaAsyncDnsResolveStatus(RESOLVE_STATUS_FAIL); } @@ -1291,6 +1318,7 @@ class HostResolverImpl::Job : public PrioritizedDispatcher::Job { DCHECK(is_dns_running()); if (net_error != OK) { + dns_task_error_ = net_error; dns_task_.reset(); // TODO(szym): Run ServeFromHosts now if nsswitch.conf says so. @@ -1413,8 +1441,8 @@ class HostResolverImpl::Job : public PrioritizedDispatcher::Job { bool had_non_speculative_request_; - // True if resolver had DnsConfig when the Job was started. - bool had_dns_config_; + // Result of DnsTask. + int dns_task_error_; BoundNetLog net_log_; diff --git a/net/base/host_resolver_impl_unittest.cc b/net/base/host_resolver_impl_unittest.cc index 60d6ed1..43b2e52 100644 --- a/net/base/host_resolver_impl_unittest.cc +++ b/net/base/host_resolver_impl_unittest.cc @@ -43,46 +43,6 @@ HostResolverImpl::ProcTaskParams DefaultParams( return HostResolverImpl::ProcTaskParams(resolver_proc, kMaxRetryAttempts); } -HostResolverImpl* CreateHostResolverImpl(HostResolverProc* resolver_proc) { - return new HostResolverImpl( - HostCache::CreateDefaultCache(), - DefaultLimits(), - DefaultParams(resolver_proc), - scoped_ptr<DnsConfigService>(NULL), - scoped_ptr<DnsClient>(NULL), - NULL); -} - -HostResolverImpl* CreateHostResolverImplWithDnsClient( - HostResolverProc* resolver_proc, - scoped_ptr<DnsConfigService> dns_config_service) { - // Initially with empty DnsConfig. Use |dns_config_service| to update it. - return new HostResolverImpl( - HostCache::CreateDefaultCache(), - DefaultLimits(), - DefaultParams(resolver_proc), - dns_config_service.Pass(), - CreateMockDnsClient(DnsConfig()), - NULL); -} - -// This HostResolverImpl will only allow 1 outstanding resolve at a time. -HostResolverImpl* CreateSerialHostResolverImpl( - HostResolverProc* resolver_proc) { - HostResolverImpl::ProcTaskParams params = DefaultParams(resolver_proc); - params.max_retry_attempts = 0u; - - PrioritizedDispatcher::Limits limits(NUM_PRIORITIES, 1); - - return new HostResolverImpl( - HostCache::CreateDefaultCache(), - limits, - params, - scoped_ptr<DnsConfigService>(NULL), - scoped_ptr<DnsClient>(NULL), - NULL); -} - // A HostResolverProc that pushes each host mapped into a list and allows // waiting for a specific number of requests. Unlike RuleBasedHostResolverProc // it never calls SystemHostResolverProc. By default resolves all hostnames to @@ -446,10 +406,7 @@ class HostResolverImplTest : public testing::Test { public: static const int kDefaultPort = 80; - HostResolverImplTest() - : proc_(new MockHostResolverProc()), - resolver_(CreateHostResolverImpl(proc_)) { - } + HostResolverImplTest() : proc_(new MockHostResolverProc()) {} protected: // A Request::Handler which is a proxy to the HostResolverImplTest fixture. @@ -473,8 +430,29 @@ class HostResolverImplTest : public testing::Test { HostResolverImplTest* test; }; + void CreateResolver() { + resolver_.reset(new HostResolverImpl( + HostCache::CreateDefaultCache(), + DefaultLimits(), + DefaultParams(proc_), + scoped_ptr<DnsConfigService>(NULL), + scoped_ptr<DnsClient>(NULL), + NULL)); + } + + // This HostResolverImpl will only allow 1 outstanding resolve at a time and + // perform no retries. void CreateSerialResolver() { - resolver_.reset(CreateSerialHostResolverImpl(proc_)); + HostResolverImpl::ProcTaskParams params = DefaultParams(proc_); + params.max_retry_attempts = 0u; + PrioritizedDispatcher::Limits limits(NUM_PRIORITIES, 1); + resolver_.reset(new HostResolverImpl( + HostCache::CreateDefaultCache(), + limits, + params, + scoped_ptr<DnsConfigService>(NULL), + scoped_ptr<DnsClient>(NULL), + NULL)); } // The Request will not be made until a call to |Resolve()|, and the Job will @@ -510,7 +488,11 @@ class HostResolverImplTest : public testing::Test { return CreateRequest(hostname, kDefaultPort); } - void TearDown() OVERRIDE { + virtual void SetUp() OVERRIDE { + CreateResolver(); + } + + virtual void TearDown() OVERRIDE { if (resolver_.get()) EXPECT_EQ(0u, resolver_->num_running_jobs_for_tests()); EXPECT_FALSE(proc_->HasBlockedRequests()); @@ -573,7 +555,7 @@ TEST_F(HostResolverImplTest, AbortedAsynchronousLookup) { proc_->SignalAll(); // To ensure there was no spurious callback, complete with a new resolver. - resolver_.reset(CreateHostResolverImpl(proc_)); + CreateResolver(); Request* req1 = CreateRequest("just.testing", 80); EXPECT_EQ(ERR_IO_PENDING, req1->Resolve()); @@ -1250,19 +1232,36 @@ DnsConfig CreateValidDnsConfig() { return config; } +// Specialized fixture for tests of DnsTask. +class HostResolverImplDnsTest : public HostResolverImplTest { + protected: + virtual void SetUp() OVERRIDE { + config_service_ = new MockDnsConfigService(); + resolver_.reset(new HostResolverImpl( + HostCache::CreateDefaultCache(), + DefaultLimits(), + DefaultParams(proc_), + scoped_ptr<DnsConfigService>(config_service_), + CreateMockDnsClient(DnsConfig()), + NULL)); + } + + void ChangeDnsConfig(const DnsConfig& config) { + config_service_->ChangeConfig(config); + config_service_->ChangeHosts(config.hosts); + } + + // Owned by |resolver_|. + MockDnsConfigService* config_service_; +}; + // TODO(szym): Test AbortAllInProgressJobs due to DnsConfig change. // TODO(cbentzel): Test a mix of requests with different HostResolverFlags. // Test successful and fallback resolutions in HostResolverImpl::DnsTask. -TEST_F(HostResolverImplTest, DnsTask) { - // Initially, there's DnsConfigService, but no DnsConfig. - // Note, |config_service| will be owned by |resolver_|. - MockDnsConfigService* config_service = new MockDnsConfigService(); - resolver_.reset( - CreateHostResolverImplWithDnsClient( - proc_, - scoped_ptr<DnsConfigService>(config_service))); +TEST_F(HostResolverImplDnsTest, DnsTask) { + resolver_->SetDefaultAddressFamily(ADDRESS_FAMILY_IPV4); proc_->AddRuleForAllFamilies("er_succeed", "192.168.1.101"); proc_->AddRuleForAllFamilies("nx_succeed", "192.168.1.102"); @@ -1274,9 +1273,7 @@ TEST_F(HostResolverImplTest, DnsTask) { EXPECT_EQ(ERR_NAME_NOT_RESOLVED, requests_[0]->WaitForResult()); - DnsConfig config = CreateValidDnsConfig(); - config_service->ChangeHosts(config.hosts); - config_service->ChangeConfig(config); + ChangeDnsConfig(CreateValidDnsConfig()); EXPECT_EQ(ERR_IO_PENDING, CreateRequest("ok_fail", 80)->Resolve()); EXPECT_EQ(ERR_IO_PENDING, CreateRequest("er_fail", 80)->Resolve()); @@ -1286,13 +1283,13 @@ TEST_F(HostResolverImplTest, DnsTask) { proc_->SignalMultiple(requests_.size()); - for (size_t i = 0; i < requests_.size(); ++i) { - EXPECT_NE(ERR_UNEXPECTED, requests_[i]->WaitForResult()) << 1; - } + for (size_t i = 1; i < requests_.size(); ++i) + EXPECT_NE(ERR_UNEXPECTED, requests_[i]->WaitForResult()) << i; EXPECT_EQ(OK, requests_[1]->result()); // Resolved by MockDnsClient. EXPECT_TRUE(requests_[1]->HasOneAddress("127.0.0.1", 80)); + // Fallback to ProcTask. EXPECT_EQ(ERR_NAME_NOT_RESOLVED, requests_[2]->result()); EXPECT_EQ(ERR_NAME_NOT_RESOLVED, requests_[3]->result()); EXPECT_EQ(OK, requests_[4]->result()); @@ -1301,18 +1298,9 @@ TEST_F(HostResolverImplTest, DnsTask) { EXPECT_TRUE(requests_[5]->HasOneAddress("192.168.1.102", 80)); } -TEST_F(HostResolverImplTest, ServeFromHosts) { - // Note, |config_service| will be owned by |resolver_|. - MockDnsConfigService* config_service = new MockDnsConfigService(); - resolver_.reset( - CreateHostResolverImplWithDnsClient( - proc_, - scoped_ptr<DnsConfigService>(config_service))); - +TEST_F(HostResolverImplDnsTest, ServeFromHosts) { // Initially, use empty HOSTS file. - DnsConfig config = CreateValidDnsConfig(); - config_service->ChangeHosts(DnsHosts()); - config_service->ChangeConfig(config); + ChangeDnsConfig(CreateValidDnsConfig()); proc_->AddRuleForAllFamilies("", "0.0.0.0"); // Default to failures. proc_->SignalMultiple(1u); // For the first request which misses. @@ -1332,8 +1320,7 @@ TEST_F(HostResolverImplTest, ServeFromHosts) { hosts[DnsHostsKey("er_both", ADDRESS_FAMILY_IPV6)] = local_ipv6; // Update HOSTS file. - config_service->ChangeConfig(config); - config_service->ChangeHosts(hosts); + config_service_->ChangeHosts(hosts); Request* req1 = CreateRequest("er_ipv4", 80); EXPECT_EQ(OK, req1->Resolve()); @@ -1363,4 +1350,24 @@ TEST_F(HostResolverImplTest, ServeFromHosts) { EXPECT_TRUE(req6->HasOneAddress("127.0.0.1", 80)); } +TEST_F(HostResolverImplDnsTest, BypassDnsTask) { + ChangeDnsConfig(CreateValidDnsConfig()); + + proc_->AddRuleForAllFamilies("", "0.0.0.0"); // 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("oklocal", 80)->Resolve()); + EXPECT_EQ(ERR_IO_PENDING, CreateRequest("oklocal.", 80)->Resolve()); + EXPECT_EQ(ERR_IO_PENDING, CreateRequest("ok", 80)->Resolve()); + + proc_->SignalMultiple(requests_.size()); + + for (size_t i = 0; i < 2; ++i) + EXPECT_EQ(ERR_NAME_NOT_RESOLVED, requests_[i]->WaitForResult()) << i; + + for (size_t i = 2; i < requests_.size(); ++i) + EXPECT_EQ(OK, requests_[i]->WaitForResult()) << i; +} + } // namespace net diff --git a/net/base/net_error_list.h b/net/base/net_error_list.h index 44da326..90f2337 100644 --- a/net/base/net_error_list.h +++ b/net/base/net_error_list.h @@ -657,3 +657,6 @@ NET_ERROR(DNS_TIMED_OUT, -803) // The entry was not found in cache, for cache-only lookups. NET_ERROR(DNS_CACHE_MISS, -804) + +// Suffix search list rules prevent resolution of the given host name. +NET_ERROR(DNS_SEARCH_EMPTY, -805) diff --git a/net/dns/dns_transaction.cc b/net/dns/dns_transaction.cc index 9df0614..a496fef 100644 --- a/net/dns/dns_transaction.cc +++ b/net/dns/dns_transaction.cc @@ -360,7 +360,7 @@ class DnsTransactionImpl : public DnsTransaction, if (ndots > 0 && !had_hostname) qnames_.push_back(labeled_hostname); - return qnames_.empty() ? ERR_NAME_NOT_RESOLVED : OK; + return qnames_.empty() ? ERR_DNS_SEARCH_EMPTY : OK; } void DoCallback(AttemptResult result) { diff --git a/net/dns/dns_transaction_unittest.cc b/net/dns/dns_transaction_unittest.cc index 4192ca8..51c5b6a 100644 --- a/net/dns/dns_transaction_unittest.cc +++ b/net/dns/dns_transaction_unittest.cc @@ -647,7 +647,7 @@ TEST_F(DnsTransactionTest, EmptySuffixSearch) { // A single label name is not even attempted. TransactionHelper helper1("singlelabel", dns_protocol::kTypeA, - ERR_NAME_NOT_RESOLVED); + ERR_DNS_SEARCH_EMPTY); helper1.StartTransaction(transaction_factory_.get()); EXPECT_TRUE(helper1.has_completed()); |