diff options
author | szym@chromium.org <szym@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-04-17 20:05:59 +0000 |
---|---|---|
committer | szym@chromium.org <szym@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-04-17 20:05:59 +0000 |
commit | ec666ab229bffb09e7ee4757e2d4224475564635 (patch) | |
tree | eeef8ce113a7996018d8d6c1adc82dacb8ee6cc6 /net | |
parent | b9317188d97029eb518838fbf187c30fb7aa3a9f (diff) | |
download | chromium_src-ec666ab229bffb09e7ee4757e2d4224475564635.zip chromium_src-ec666ab229bffb09e7ee4757e2d4224475564635.tar.gz chromium_src-ec666ab229bffb09e7ee4757e2d4224475564635.tar.bz2 |
[net/dns] Add test DualFamilyLocalhost and fix it when DnsClient enabled.
On some platforms, listening on "localhost" binds to the IPv6 loopback
(::1), even if there's no global IPv6 connectivity. In such situations,
HostResolverImpl restricts lookups to IPv4 (AF_INET) for performance
reasons, but then navigating to http://localhost results in "connection
refused".
HostResolverProc has a workaround for this situation. This CL adds a
test for this behavior and replicates the workaround in
HostResolverImpl::ServeFromHosts (for the built-in asynchronous DNS).
BUG=224215
Review URL: https://codereview.chromium.org/14208010
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@194663 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r-- | net/dns/host_resolver_impl.cc | 32 | ||||
-rw-r--r-- | net/dns/host_resolver_impl_unittest.cc | 80 | ||||
-rw-r--r-- | net/dns/host_resolver_proc.h | 2 |
3 files changed, 106 insertions, 8 deletions
diff --git a/net/dns/host_resolver_impl.cc b/net/dns/host_resolver_impl.cc index 74a7675..d9c43df 100644 --- a/net/dns/host_resolver_impl.cc +++ b/net/dns/host_resolver_impl.cc @@ -250,6 +250,25 @@ AddressList EnsurePortOnAddressList(const AddressList& list, uint16 port) { return AddressList::CopyWithPort(list, port); } +// Returns true if |addresses| contains only IPv4 loopback addresses. +bool IsAllIPv4Loopback(const AddressList& addresses) { + for (unsigned i = 0; i < addresses.size(); ++i) { + const IPAddressNumber& address = addresses[i].address(); + switch (addresses[i].GetFamily()) { + case ADDRESS_FAMILY_IPV4: + if (address[0] != 127) + return false; + break; + case ADDRESS_FAMILY_IPV6: + return false; + default: + NOTREACHED(); + return false; + } + } + return true; +} + // Creates NetLog parameters when the resolve failed. base::Value* NetLogProcTaskFailedCallback(uint32 attempt_number, int net_error, @@ -1950,7 +1969,6 @@ bool HostResolverImpl::ServeFromHosts(const Key& key, DCHECK(addresses); if (!HaveDnsConfig()) return false; - addresses->clear(); // HOSTS lookups are case-insensitive. @@ -1979,6 +1997,17 @@ bool HostResolverImpl::ServeFromHosts(const Key& key, addresses->push_back(IPEndPoint(it->second, info.port())); } + // If got only loopback addresses and the family was restricted, resolve + // again, without restrictions. See SystemHostResolverCall for rationale. + if ((key.host_resolver_flags & + HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6) && + IsAllIPv4Loopback(*addresses)) { + Key new_key(key); + new_key.address_family = ADDRESS_FAMILY_UNSPECIFIED; + new_key.host_resolver_flags &= + ~HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6; + return ServeFromHosts(new_key, info, addresses); + } return !addresses->empty(); } @@ -2118,6 +2147,7 @@ void HostResolverImpl::OnIPAddressChanged() { void HostResolverImpl::OnDNSChanged() { DnsConfig dns_config; NetworkChangeNotifier::GetDnsConfig(&dns_config); + if (net_log_) { net_log_->AddGlobalEntry( NetLog::TYPE_DNS_CONFIG_CHANGED, diff --git a/net/dns/host_resolver_impl_unittest.cc b/net/dns/host_resolver_impl_unittest.cc index 9ff359f..a262131 100644 --- a/net/dns/host_resolver_impl_unittest.cc +++ b/net/dns/host_resolver_impl_unittest.cc @@ -177,6 +177,16 @@ class MockHostResolverProc : public HostResolverProc { DISALLOW_COPY_AND_ASSIGN(MockHostResolverProc); }; +bool AddressListContains(const AddressList& list, const std::string& address, + int port) { + IPAddressNumber ip; + bool rv = ParseIPLiteralToNumber(address, &ip); + DCHECK(rv); + return std::find(list.begin(), + list.end(), + IPEndPoint(ip, port)) != list.end(); +} + // A wrapper for requests to a HostResolver. class Request { public: @@ -231,12 +241,7 @@ class Request { bool pending() const { return handle_ != NULL; } bool HasAddress(const std::string& address, int port) const { - IPAddressNumber ip; - bool rv = ParseIPLiteralToNumber(address, &ip); - DCHECK(rv); - return std::find(list_.begin(), - list_.end(), - IPEndPoint(ip, port)) != list_.end(); + return AddressListContains(list_, address, port); } // Returns the number of addresses in |list_|. @@ -1482,4 +1487,67 @@ TEST_F(HostResolverImplDnsTest, DontDisableDnsClientOnSporadicFailure) { EXPECT_EQ(OK, req->WaitForResult()); } +// Confirm that resolving "localhost" is unrestricted even if there are no +// global IPv6 address. See SystemHostResolverCall for rationale. +// Test both the DnsClient and system host resolver paths. +TEST_F(HostResolverImplDnsTest, DualFamilyLocalhost) { + // Use regular SystemHostResolverCall! + scoped_refptr<HostResolverProc> proc(new SystemHostResolverProc()); + resolver_.reset(new HostResolverImpl(HostCache::CreateDefaultCache(), + DefaultLimits(), + DefaultParams(proc), + NULL)); + resolver_->SetDnsClient(CreateMockDnsClient(DnsConfig(), dns_rules_)); + resolver_->SetDefaultAddressFamily(ADDRESS_FAMILY_IPV4); + + // Get the expected output. + AddressList addrlist; + int rv = proc->Resolve("localhost", ADDRESS_FAMILY_UNSPECIFIED, 0, &addrlist, + NULL); + if (rv != OK) + return; + + for (unsigned i = 0; i < addrlist.size(); ++i) + LOG(WARNING) << addrlist[i].ToString(); + + bool saw_ipv4 = AddressListContains(addrlist, "127.0.0.1", 0); + bool saw_ipv6 = AddressListContains(addrlist, "::1", 0); + if (!saw_ipv4 && !saw_ipv6) + return; + + HostResolver::RequestInfo info(HostPortPair("localhost", 80)); + info.set_address_family(ADDRESS_FAMILY_UNSPECIFIED); + info.set_host_resolver_flags(HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6); + + // Try without DnsClient. + ChangeDnsConfig(DnsConfig()); + Request* req = CreateRequest(info); + // It is resolved via getaddrinfo, so expect asynchronous result. + EXPECT_EQ(ERR_IO_PENDING, req->Resolve()); + EXPECT_EQ(OK, req->WaitForResult()); + + EXPECT_EQ(saw_ipv4, req->HasAddress("127.0.0.1", 80)); + EXPECT_EQ(saw_ipv6, req->HasAddress("::1", 80)); + + // Configure DnsClient with dual-host HOSTS file. + DnsConfig config = CreateValidDnsConfig(); + DnsHosts hosts; + IPAddressNumber local_ipv4, local_ipv6; + ASSERT_TRUE(ParseIPLiteralToNumber("127.0.0.1", &local_ipv4)); + ASSERT_TRUE(ParseIPLiteralToNumber("::1", &local_ipv6)); + if (saw_ipv4) + hosts[DnsHostsKey("localhost", ADDRESS_FAMILY_IPV4)] = local_ipv4; + if (saw_ipv6) + hosts[DnsHostsKey("localhost", ADDRESS_FAMILY_IPV6)] = local_ipv6; + config.hosts = hosts; + + ChangeDnsConfig(config); + req = CreateRequest(info); + // Expect synchronous resolution from DnsHosts. + EXPECT_EQ(OK, req->Resolve()); + + EXPECT_EQ(saw_ipv4, req->HasAddress("127.0.0.1", 80)); + EXPECT_EQ(saw_ipv6, req->HasAddress("::1", 80)); +} + } // namespace net diff --git a/net/dns/host_resolver_proc.h b/net/dns/host_resolver_proc.h index ee8777f..014a720 100644 --- a/net/dns/host_resolver_proc.h +++ b/net/dns/host_resolver_proc.h @@ -92,7 +92,7 @@ NET_EXPORT_PRIVATE int SystemHostResolverCall( int* os_error); // Wraps call to SystemHostResolverCall as an instance of HostResolverProc. -class SystemHostResolverProc : public HostResolverProc { +class NET_EXPORT_PRIVATE SystemHostResolverProc : public HostResolverProc { public: SystemHostResolverProc(); virtual int Resolve(const std::string& hostname, |