Commit ec666ab2 authored by szym@chromium.org's avatar szym@chromium.org

[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
parent b9317188
......@@ -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,
......
......@@ -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
......@@ -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,
......
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