blob: e1ffb1a1f95808b45b28e29f75b6e2b627cd7c9f [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/dns/context_host_resolver.h"
#include <memory>
#include <utility>
#include "base/containers/fixed_flat_map.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/simple_test_tick_clock.h"
#include "base/test/task_environment.h"
#include "base/time/time.h"
#include "net/base/features.h"
#include "net/base/host_port_pair.h"
#include "net/base/ip_address.h"
#include "net/base/ip_endpoint.h"
#include "net/base/mock_network_change_notifier.h"
#include "net/base/net_errors.h"
#include "net/base/network_isolation_key.h"
#include "net/base/schemeful_site.h"
#include "net/base/test_completion_callback.h"
#include "net/dns/dns_config.h"
#include "net/dns/dns_test_util.h"
#include "net/dns/dns_util.h"
#include "net/dns/host_cache.h"
#include "net/dns/host_resolver.h"
#include "net/dns/host_resolver_manager.h"
#include "net/dns/host_resolver_system_task.h"
#include "net/dns/mock_host_resolver.h"
#include "net/dns/public/dns_over_https_config.h"
#include "net/dns/public/dns_protocol.h"
#include "net/dns/public/host_resolver_source.h"
#include "net/dns/public/resolve_error_info.h"
#include "net/dns/resolve_context.h"
#include "net/log/net_log_with_source.h"
#include "net/test/gtest_util.h"
#include "net/test/test_with_task_environment.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_builder.h"
#include "net/url_request/url_request_test_util.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "url/gurl.h"
#include "url/scheme_host_port.h"
#if BUILDFLAG(IS_ANDROID)
#include "base/android/build_info.h"
#include "net/android/network_change_notifier_factory_android.h"
#endif // BUILDFLAG(IS_ANDROID)
namespace net {
namespace {
const IPEndPoint kEndpoint(IPAddress(1, 2, 3, 4), 100);
}
class ContextHostResolverTest : public ::testing::Test,
public WithTaskEnvironment {
protected:
// Use mock time to prevent the HostResolverManager's injected IPv6 probe
// result from timing out.
ContextHostResolverTest()
: WithTaskEnvironment(
base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
~ContextHostResolverTest() override = default;
void SetUp() override {
manager_ = std::make_unique<HostResolverManager>(
HostResolver::ManagerOptions(),
nullptr /* system_dns_config_notifier */, nullptr /* net_log */);
manager_->SetLastIPv6ProbeResultForTesting(true);
}
void SetMockDnsRules(MockDnsClientRuleList rules) {
IPAddress dns_ip(192, 168, 1, 0);
DnsConfig config;
config.nameservers.emplace_back(dns_ip, dns_protocol::kDefaultPort);
config.doh_config = *DnsOverHttpsConfig::FromString("https://example.com");
EXPECT_TRUE(config.IsValid());
auto dns_client =
std::make_unique<MockDnsClient>(std::move(config), std::move(rules));
dns_client->set_ignore_system_config_changes(true);
dns_client_ = dns_client.get();
manager_->SetDnsClientForTesting(std::move(dns_client));
manager_->SetInsecureDnsClientEnabled(
/*enabled=*/true,
/*additional_dns_types_enabled=*/true);
// Ensure DnsClient is fully usable.
EXPECT_TRUE(dns_client_->CanUseInsecureDnsTransactions());
EXPECT_FALSE(dns_client_->FallbackFromInsecureTransactionPreferred());
EXPECT_TRUE(dns_client_->GetEffectiveConfig());
scoped_refptr<HostResolverProc> proc = CreateCatchAllHostResolverProc();
manager_->set_host_resolver_system_params_for_test(
HostResolverSystemTask::Params(proc, 1u));
}
raw_ptr<MockDnsClient> dns_client_;
std::unique_ptr<HostResolverManager> manager_;
};
TEST_F(ContextHostResolverTest, Resolve) {
auto context = CreateTestURLRequestContextBuilder()->Build();
MockDnsClientRuleList rules;
rules.emplace_back("example.com", dns_protocol::kTypeA, false /* secure */,
MockDnsClientRule::Result(BuildTestDnsAddressResponse(
"example.com", kEndpoint.address())),
false /* delay */, context.get());
rules.emplace_back(
"example.com", dns_protocol::kTypeAAAA, false /* secure */,
MockDnsClientRule::Result(MockDnsClientRule::ResultType::kEmpty),
false /* delay */, context.get());
SetMockDnsRules(std::move(rules));
auto resolve_context = std::make_unique<ResolveContext>(
context.get(), false /* enable_caching */);
auto resolver = std::make_unique<ContextHostResolver>(
manager_.get(), std::move(resolve_context));
std::unique_ptr<HostResolver::ResolveHostRequest> request =
resolver->CreateRequest(HostPortPair("example.com", 100),
NetworkAnonymizationKey(), NetLogWithSource(),
absl::nullopt);
TestCompletionCallback callback;
int rv = request->Start(callback.callback());
EXPECT_THAT(callback.GetResult(rv), test::IsOk());
EXPECT_THAT(request->GetResolveErrorInfo().error, test::IsError(net::OK));
EXPECT_THAT(request->GetAddressResults()->endpoints(),
testing::ElementsAre(kEndpoint));
}
TEST_F(ContextHostResolverTest, ResolveWithScheme) {
auto context = CreateTestURLRequestContextBuilder()->Build();
MockDnsClientRuleList rules;
rules.emplace_back("example.com", dns_protocol::kTypeA, false /* secure */,
MockDnsClientRule::Result(BuildTestDnsAddressResponse(
"example.com", kEndpoint.address())),
false /* delay */, context.get());
rules.emplace_back(
"example.com", dns_protocol::kTypeAAAA, false /* secure */,
MockDnsClientRule::Result(MockDnsClientRule::ResultType::kEmpty),
false /* delay */, context.get());
SetMockDnsRules(std::move(rules));
auto resolve_context = std::make_unique<ResolveContext>(
context.get(), false /* enable_caching */);
auto resolver = std::make_unique<ContextHostResolver>(
manager_.get(), std::move(resolve_context));
std::unique_ptr<HostResolver::ResolveHostRequest> request =
resolver->CreateRequest(
url::SchemeHostPort(url::kHttpsScheme, "example.com", 100),
NetworkAnonymizationKey(), NetLogWithSource(), absl::nullopt);
TestCompletionCallback callback;
int rv = request->Start(callback.callback());
EXPECT_THAT(callback.GetResult(rv), test::IsOk());
EXPECT_THAT(request->GetResolveErrorInfo().error, test::IsError(net::OK));
EXPECT_THAT(request->GetAddressResults()->endpoints(),
testing::ElementsAre(kEndpoint));
}
TEST_F(ContextHostResolverTest, ResolveWithSchemeAndIpLiteral) {
auto context = CreateTestURLRequestContextBuilder()->Build();
IPAddress expected_address;
ASSERT_TRUE(expected_address.AssignFromIPLiteral("1234::5678"));
auto resolve_context = std::make_unique<ResolveContext>(
context.get(), false /* enable_caching */);
auto resolver = std::make_unique<ContextHostResolver>(
manager_.get(), std::move(resolve_context));
std::unique_ptr<HostResolver::ResolveHostRequest> request =
resolver->CreateRequest(
url::SchemeHostPort(url::kHttpsScheme, "[1234::5678]", 100),
NetworkAnonymizationKey(), NetLogWithSource(), absl::nullopt);
TestCompletionCallback callback;
int rv = request->Start(callback.callback());
EXPECT_THAT(callback.GetResult(rv), test::IsOk());
EXPECT_THAT(request->GetResolveErrorInfo().error, test::IsError(net::OK));
EXPECT_THAT(request->GetAddressResults()->endpoints(),
testing::ElementsAre(IPEndPoint(expected_address, 100)));
}
// Test that destroying a request silently cancels that request.
TEST_F(ContextHostResolverTest, DestroyRequest) {
// Set up delayed results for "example.com".
MockDnsClientRuleList rules;
rules.emplace_back("example.com", dns_protocol::kTypeA, false /* secure */,
MockDnsClientRule::Result(BuildTestDnsAddressResponse(
"example.com", IPAddress(1, 2, 3, 4))),
true /* delay */);
rules.emplace_back(
"example.com", dns_protocol::kTypeAAAA, false /* secure */,
MockDnsClientRule::Result(MockDnsClientRule::ResultType::kEmpty),
false /* delay */);
SetMockDnsRules(std::move(rules));
auto resolver = std::make_unique<ContextHostResolver>(
manager_.get(),
std::make_unique<ResolveContext>(nullptr /* url_request_context */,
false /* enable_caching */));
std::unique_ptr<HostResolver::ResolveHostRequest> request =
resolver->CreateRequest(HostPortPair("example.com", 100),
NetworkAnonymizationKey(), NetLogWithSource(),
absl::nullopt);
TestCompletionCallback callback;
int rv = request->Start(callback.callback());
// Cancel |request| before allowing delayed result to complete.
request = nullptr;
dns_client_->CompleteDelayedTransactions();
// Ensure |request| never completes.
base::RunLoop().RunUntilIdle();
EXPECT_THAT(rv, test::IsError(ERR_IO_PENDING));
EXPECT_FALSE(callback.have_result());
}
TEST_F(ContextHostResolverTest, DohProbeRequest) {
// Set empty MockDnsClient rules to ensure DnsClient is mocked out.
MockDnsClientRuleList rules;
SetMockDnsRules(std::move(rules));
auto context = CreateTestURLRequestContextBuilder()->Build();
auto resolve_context = std::make_unique<ResolveContext>(
context.get(), true /* enable caching */);
auto resolver = std::make_unique<ContextHostResolver>(
manager_.get(), std::move(resolve_context));
std::unique_ptr<HostResolver::ProbeRequest> request =
resolver->CreateDohProbeRequest();
ASSERT_FALSE(dns_client_->factory()->doh_probes_running());
EXPECT_THAT(request->Start(), test::IsError(ERR_IO_PENDING));
EXPECT_TRUE(dns_client_->factory()->doh_probes_running());
request.reset();
EXPECT_FALSE(dns_client_->factory()->doh_probes_running());
}
TEST_F(ContextHostResolverTest, DohProbesFromSeparateContexts) {
// Set empty MockDnsClient rules to ensure DnsClient is mocked out.
MockDnsClientRuleList rules;
SetMockDnsRules(std::move(rules));
auto resolve_context1 = std::make_unique<ResolveContext>(
nullptr /* url_request_context */, false /* enable_caching */);
auto resolver1 = std::make_unique<ContextHostResolver>(
manager_.get(), std::move(resolve_context1));
std::unique_ptr<HostResolver::ProbeRequest> request1 =
resolver1->CreateDohProbeRequest();
auto resolve_context2 = std::make_unique<ResolveContext>(
nullptr /* url_request_context */, false /* enable_caching */);
auto resolver2 = std::make_unique<ContextHostResolver>(
manager_.get(), std::move(resolve_context2));
std::unique_ptr<HostResolver::ProbeRequest> request2 =
resolver2->CreateDohProbeRequest();
EXPECT_FALSE(dns_client_->factory()->doh_probes_running());
EXPECT_THAT(request1->Start(), test::IsError(ERR_IO_PENDING));
EXPECT_THAT(request2->Start(), test::IsError(ERR_IO_PENDING));
EXPECT_TRUE(dns_client_->factory()->doh_probes_running());
request1.reset();
EXPECT_TRUE(dns_client_->factory()->doh_probes_running());
request2.reset();
EXPECT_FALSE(dns_client_->factory()->doh_probes_running());
}
// Test that cancelling a resolver cancels its (and only its) requests.
TEST_F(ContextHostResolverTest, DestroyResolver) {
// Set up delayed results for "example.com" and "google.com".
MockDnsClientRuleList rules;
rules.emplace_back("example.com", dns_protocol::kTypeA, false /* secure */,
MockDnsClientRule::Result(BuildTestDnsAddressResponse(
"example.com", IPAddress(2, 3, 4, 5))),
true /* delay */);
rules.emplace_back(
"example.com", dns_protocol::kTypeAAAA, false /* secure */,
MockDnsClientRule::Result(MockDnsClientRule::ResultType::kEmpty),
false /* delay */);
rules.emplace_back("google.com", dns_protocol::kTypeA, false /* secure */,
MockDnsClientRule::Result(BuildTestDnsAddressResponse(
"google.com", kEndpoint.address())),
true /* delay */);
rules.emplace_back(
"google.com", dns_protocol::kTypeAAAA, false /* secure */,
MockDnsClientRule::Result(MockDnsClientRule::ResultType::kEmpty),
false /* delay */);
SetMockDnsRules(std::move(rules));
auto resolver1 = std::make_unique<ContextHostResolver>(
manager_.get(),
std::make_unique<ResolveContext>(nullptr /* url_request_context */,
false /* enable_caching */));
std::unique_ptr<HostResolver::ResolveHostRequest> request1 =
resolver1->CreateRequest(HostPortPair("example.com", 100),
NetworkAnonymizationKey(), NetLogWithSource(),
absl::nullopt);
auto resolver2 = std::make_unique<ContextHostResolver>(
manager_.get(),
std::make_unique<ResolveContext>(nullptr /* url_request_context */,
false /* enable_caching */));
std::unique_ptr<HostResolver::ResolveHostRequest> request2 =
resolver2->CreateRequest(HostPortPair("google.com", 100),
NetworkAnonymizationKey(), NetLogWithSource(),
absl::nullopt);
TestCompletionCallback callback1;
int rv1 = request1->Start(callback1.callback());
TestCompletionCallback callback2;
int rv2 = request2->Start(callback2.callback());
EXPECT_EQ(2u, manager_->num_jobs_for_testing());
// Cancel |resolver1| before allowing delayed requests to complete.
resolver1 = nullptr;
dns_client_->CompleteDelayedTransactions();
EXPECT_THAT(callback2.GetResult(rv2), test::IsOk());
EXPECT_THAT(request2->GetAddressResults()->endpoints(),
testing::ElementsAre(kEndpoint));
// Ensure |request1| never completes.
base::RunLoop().RunUntilIdle();
EXPECT_THAT(rv1, test::IsError(ERR_IO_PENDING));
EXPECT_FALSE(callback1.have_result());
}
TEST_F(ContextHostResolverTest, DestroyResolver_CompletedRequests) {
MockDnsClientRuleList rules;
rules.emplace_back("example.com", dns_protocol::kTypeA, false /* secure */,
MockDnsClientRule::Result(BuildTestDnsAddressResponse(
"example.com", kEndpoint.address())),
false /* delay */);
rules.emplace_back(
"example.com", dns_protocol::kTypeAAAA, false /* secure */,
MockDnsClientRule::Result(MockDnsClientRule::ResultType::kEmpty),
false /* delay */);
SetMockDnsRules(std::move(rules));
auto resolver = std::make_unique<ContextHostResolver>(
manager_.get(),
std::make_unique<ResolveContext>(nullptr /* url_request_context */,
false /* enable_caching */));
std::unique_ptr<HostResolver::ResolveHostRequest> request =
resolver->CreateRequest(HostPortPair("example.com", 100),
NetworkAnonymizationKey(), NetLogWithSource(),
absl::nullopt);
// Complete request and then destroy the resolver.
TestCompletionCallback callback;
int rv = request->Start(callback.callback());
ASSERT_THAT(callback.GetResult(rv), test::IsOk());
resolver = nullptr;
// Expect completed results are still available.
EXPECT_THAT(request->GetResolveErrorInfo().error, test::IsError(net::OK));
EXPECT_THAT(request->GetAddressResults()->endpoints(),
testing::ElementsAre(kEndpoint));
}
// Test a request created before resolver destruction but not yet started.
TEST_F(ContextHostResolverTest, DestroyResolver_DelayedStartRequest) {
// Set up delayed result for "example.com".
MockDnsClientRuleList rules;
rules.emplace_back("example.com", dns_protocol::kTypeA, false /* secure */,
MockDnsClientRule::Result(BuildTestDnsAddressResponse(
"example.com", IPAddress(2, 3, 4, 5))),
true /* delay */);
rules.emplace_back(
"example.com", dns_protocol::kTypeAAAA, false /* secure */,
MockDnsClientRule::Result(MockDnsClientRule::ResultType::kEmpty),
false /* delay */);
auto resolver = std::make_unique<ContextHostResolver>(
manager_.get(),
std::make_unique<ResolveContext>(nullptr /* url_request_context */,
false /* enable_caching */));
std::unique_ptr<HostResolver::ResolveHostRequest> request =
resolver->CreateRequest(HostPortPair("example.com", 100),
NetworkAnonymizationKey(), NetLogWithSource(),
absl::nullopt);
resolver = nullptr;
TestCompletionCallback callback;
int rv = request->Start(callback.callback());
EXPECT_THAT(callback.GetResult(rv), test::IsError(ERR_NAME_NOT_RESOLVED));
EXPECT_THAT(request->GetResolveErrorInfo().error,
test::IsError(ERR_CONTEXT_SHUT_DOWN));
EXPECT_FALSE(request->GetAddressResults());
}
TEST_F(ContextHostResolverTest, DestroyResolver_DelayedStartDohProbeRequest) {
// Set empty MockDnsClient rules to ensure DnsClient is mocked out.
MockDnsClientRuleList rules;
SetMockDnsRules(std::move(rules));
auto context = CreateTestURLRequestContextBuilder()->Build();
auto resolve_context = std::make_unique<ResolveContext>(
context.get(), false /* enable_caching */);
auto resolver = std::make_unique<ContextHostResolver>(
manager_.get(), std::move(resolve_context));
std::unique_ptr<HostResolver::ProbeRequest> request =
resolver->CreateDohProbeRequest();
resolver = nullptr;
EXPECT_THAT(request->Start(), test::IsError(ERR_CONTEXT_SHUT_DOWN));
EXPECT_FALSE(dns_client_->factory()->doh_probes_running());
}
TEST_F(ContextHostResolverTest, OnShutdown_PendingRequest) {
// Set up delayed result for "example.com".
MockDnsClientRuleList rules;
rules.emplace_back("example.com", dns_protocol::kTypeA, false /* secure */,
MockDnsClientRule::Result(BuildTestDnsAddressResponse(
"example.com", IPAddress(2, 3, 4, 5))),
true /* delay */);
rules.emplace_back(
"example.com", dns_protocol::kTypeAAAA, false /* secure */,
MockDnsClientRule::Result(MockDnsClientRule::ResultType::kEmpty),
false /* delay */);
SetMockDnsRules(std::move(rules));
auto context = CreateTestURLRequestContextBuilder()->Build();
auto resolve_context = std::make_unique<ResolveContext>(
context.get(), false /* enable_caching */);
auto resolver = std::make_unique<ContextHostResolver>(
manager_.get(), std::move(resolve_context));
std::unique_ptr<HostResolver::ResolveHostRequest> request =
resolver->CreateRequest(HostPortPair("example.com", 100),
NetworkAnonymizationKey(), NetLogWithSource(),
absl::nullopt);
TestCompletionCallback callback;
int rv = request->Start(callback.callback());
// Trigger shutdown before allowing request to complete.
resolver->OnShutdown();
dns_client_->CompleteDelayedTransactions();
// Ensure request never completes.
base::RunLoop().RunUntilIdle();
EXPECT_THAT(rv, test::IsError(ERR_IO_PENDING));
EXPECT_FALSE(callback.have_result());
}
TEST_F(ContextHostResolverTest, OnShutdown_CompletedRequests) {
MockDnsClientRuleList rules;
rules.emplace_back("example.com", dns_protocol::kTypeA, false /* secure */,
MockDnsClientRule::Result(BuildTestDnsAddressResponse(
"example.com", kEndpoint.address())),
false /* delay */);
rules.emplace_back(
"example.com", dns_protocol::kTypeAAAA, false /* secure */,
MockDnsClientRule::Result(MockDnsClientRule::ResultType::kEmpty),
false /* delay */);
SetMockDnsRules(std::move(rules));
auto context = CreateTestURLRequestContextBuilder()->Build();
auto resolve_context = std::make_unique<ResolveContext>(
context.get(), false /* enable_caching */);
auto resolver = std::make_unique<ContextHostResolver>(
manager_.get(), std::move(resolve_context));
std::unique_ptr<HostResolver::ResolveHostRequest> request =
resolver->CreateRequest(HostPortPair("example.com", 100),
NetworkAnonymizationKey(), NetLogWithSource(),
absl::nullopt);
// Complete request and then shutdown the resolver.
TestCompletionCallback callback;
int rv = request->Start(callback.callback());
ASSERT_THAT(callback.GetResult(rv), test::IsOk());
resolver->OnShutdown();
// Expect completed results are still available.
EXPECT_THAT(request->GetResolveErrorInfo().error, test::IsError(net::OK));
EXPECT_THAT(request->GetAddressResults()->endpoints(),
testing::ElementsAre(kEndpoint));
}
TEST_F(ContextHostResolverTest, OnShutdown_SubsequentRequests) {
auto context = CreateTestURLRequestContextBuilder()->Build();
auto resolve_context = std::make_unique<ResolveContext>(
context.get(), false /* enable_caching */);
auto resolver = std::make_unique<ContextHostResolver>(
manager_.get(), std::move(resolve_context));
resolver->OnShutdown();
std::unique_ptr<HostResolver::ResolveHostRequest> request1 =
resolver->CreateRequest(HostPortPair("example.com", 100),
NetworkAnonymizationKey(), NetLogWithSource(),
absl::nullopt);
std::unique_ptr<HostResolver::ResolveHostRequest> request2 =
resolver->CreateRequest(HostPortPair("127.0.0.1", 100),
NetworkAnonymizationKey(), NetLogWithSource(),
absl::nullopt);
TestCompletionCallback callback1;
int rv1 = request1->Start(callback1.callback());
TestCompletionCallback callback2;
int rv2 = request2->Start(callback2.callback());
EXPECT_THAT(callback1.GetResult(rv1), test::IsError(ERR_CONTEXT_SHUT_DOWN));
EXPECT_THAT(request1->GetResolveErrorInfo().error,
test::IsError(ERR_CONTEXT_SHUT_DOWN));
EXPECT_FALSE(request1->GetAddressResults());
EXPECT_THAT(callback2.GetResult(rv2), test::IsError(ERR_CONTEXT_SHUT_DOWN));
EXPECT_THAT(request2->GetResolveErrorInfo().error,
test::IsError(ERR_CONTEXT_SHUT_DOWN));
EXPECT_FALSE(request2->GetAddressResults());
}
TEST_F(ContextHostResolverTest, OnShutdown_SubsequentDohProbeRequest) {
// Set empty MockDnsClient rules to ensure DnsClient is mocked out.
MockDnsClientRuleList rules;
SetMockDnsRules(std::move(rules));
auto context = CreateTestURLRequestContextBuilder()->Build();
auto resolve_context = std::make_unique<ResolveContext>(
context.get(), false /* enable_caching */);
auto resolver = std::make_unique<ContextHostResolver>(
manager_.get(), std::move(resolve_context));
resolver->OnShutdown();
std::unique_ptr<HostResolver::ProbeRequest> request =
resolver->CreateDohProbeRequest();
EXPECT_THAT(request->Start(), test::IsError(ERR_CONTEXT_SHUT_DOWN));
EXPECT_FALSE(dns_client_->factory()->doh_probes_running());
}
// Test a request created before shutdown but not yet started.
TEST_F(ContextHostResolverTest, OnShutdown_DelayedStartRequest) {
// Set up delayed result for "example.com".
MockDnsClientRuleList rules;
rules.emplace_back("example.com", dns_protocol::kTypeA, false /* secure */,
MockDnsClientRule::Result(BuildTestDnsAddressResponse(
"example.com", IPAddress(2, 3, 4, 5))),
true /* delay */);
rules.emplace_back(
"example.com", dns_protocol::kTypeAAAA, false /* secure */,
MockDnsClientRule::Result(MockDnsClientRule::ResultType::kEmpty),
false /* delay */);
auto context = CreateTestURLRequestContextBuilder()->Build();
auto resolve_context = std::make_unique<ResolveContext>(
context.get(), false /* enable_caching */);
auto resolver = std::make_unique<ContextHostResolver>(
manager_.get(), std::move(resolve_context));
std::unique_ptr<HostResolver::ResolveHostRequest> request =
resolver->CreateRequest(HostPortPair("example.com", 100),
NetworkAnonymizationKey(), NetLogWithSource(),
absl::nullopt);
resolver->OnShutdown();
TestCompletionCallback callback;
int rv = request->Start(callback.callback());
EXPECT_THAT(callback.GetResult(rv), test::IsError(ERR_NAME_NOT_RESOLVED));
EXPECT_THAT(request->GetResolveErrorInfo().error,
test::IsError(ERR_CONTEXT_SHUT_DOWN));
EXPECT_FALSE(request->GetAddressResults());
}
TEST_F(ContextHostResolverTest, OnShutdown_DelayedStartDohProbeRequest) {
// Set empty MockDnsClient rules to ensure DnsClient is mocked out.
MockDnsClientRuleList rules;
SetMockDnsRules(std::move(rules));
auto context = CreateTestURLRequestContextBuilder()->Build();
auto resolve_context = std::make_unique<ResolveContext>(
context.get(), false /* enable_caching */);
auto resolver = std::make_unique<ContextHostResolver>(
manager_.get(), std::move(resolve_context));
std::unique_ptr<HostResolver::ProbeRequest> request =
resolver->CreateDohProbeRequest();
resolver->OnShutdown();
EXPECT_THAT(request->Start(), test::IsError(ERR_CONTEXT_SHUT_DOWN));
EXPECT_FALSE(dns_client_->factory()->doh_probes_running());
}
TEST_F(ContextHostResolverTest, ResolveFromCache) {
auto resolve_context = std::make_unique<ResolveContext>(
nullptr /* url_request_context */, true /* enable_caching */);
HostCache* host_cache = resolve_context->host_cache();
auto resolver = std::make_unique<ContextHostResolver>(
manager_.get(), std::move(resolve_context));
// Create the cache entry after creating the ContextHostResolver, as
// registering into the HostResolverManager initializes and invalidates the
// cache.
base::SimpleTestTickClock clock;
clock.Advance(base::Days(62)); // Arbitrary non-zero time.
std::vector<IPEndPoint> expected({kEndpoint});
host_cache->Set(
HostCache::Key("example.com", DnsQueryType::UNSPECIFIED,
0 /* host_resolver_flags */, HostResolverSource::ANY,
NetworkAnonymizationKey()),
HostCache::Entry(OK, expected,
/*aliases=*/std::set<std::string>({"example.com"}),
HostCache::Entry::SOURCE_DNS, base::Days(1)),
clock.NowTicks(), base::Days(1));
resolver->SetTickClockForTesting(&clock);
// Allow stale results and then confirm the result is not stale in order to
// make the issue more clear if something is invalidating the cache.
HostResolver::ResolveHostParameters parameters;
parameters.source = HostResolverSource::LOCAL_ONLY;
parameters.cache_usage =
HostResolver::ResolveHostParameters::CacheUsage::STALE_ALLOWED;
std::unique_ptr<HostResolver::ResolveHostRequest> request =
resolver->CreateRequest(HostPortPair("example.com", 100),
NetworkAnonymizationKey(), NetLogWithSource(),
parameters);
TestCompletionCallback callback;
int rv = request->Start(callback.callback());
EXPECT_THAT(callback.GetResult(rv), test::IsOk());
EXPECT_THAT(request->GetResolveErrorInfo().error, test::IsError(net::OK));
EXPECT_THAT(request->GetAddressResults()->endpoints(),
testing::ElementsAre(kEndpoint));
ASSERT_TRUE(request->GetStaleInfo());
EXPECT_EQ(0, request->GetStaleInfo().value().network_changes);
EXPECT_FALSE(request->GetStaleInfo().value().is_stale());
}
TEST_F(ContextHostResolverTest, ResultsAddedToCache) {
MockDnsClientRuleList rules;
rules.emplace_back("example.com", dns_protocol::kTypeA, false /* secure */,
MockDnsClientRule::Result(BuildTestDnsAddressResponse(
"example.com", kEndpoint.address())),
false /* delay */);
rules.emplace_back(
"example.com", dns_protocol::kTypeAAAA, false /* secure */,
MockDnsClientRule::Result(MockDnsClientRule::ResultType::kEmpty),
false /* delay */);
SetMockDnsRules(std::move(rules));
auto resolve_context = std::make_unique<ResolveContext>(
nullptr /* url_request_context */, true /* enable_caching */);
auto resolver = std::make_unique<ContextHostResolver>(
manager_.get(), std::move(resolve_context));
std::unique_ptr<HostResolver::ResolveHostRequest> caching_request =
resolver->CreateRequest(HostPortPair("example.com", 103),
NetworkAnonymizationKey(), NetLogWithSource(),
absl::nullopt);
TestCompletionCallback caching_callback;
int rv = caching_request->Start(caching_callback.callback());
EXPECT_THAT(caching_callback.GetResult(rv), test::IsOk());
HostResolver::ResolveHostParameters local_resolve_parameters;
local_resolve_parameters.source = HostResolverSource::LOCAL_ONLY;
std::unique_ptr<HostResolver::ResolveHostRequest> cached_request =
resolver->CreateRequest(HostPortPair("example.com", 100),
NetworkAnonymizationKey(), NetLogWithSource(),
local_resolve_parameters);
TestCompletionCallback callback;
rv = cached_request->Start(callback.callback());
EXPECT_THAT(callback.GetResult(rv), test::IsOk());
EXPECT_THAT(cached_request->GetResolveErrorInfo().error,
test::IsError(net::OK));
EXPECT_THAT(cached_request->GetAddressResults()->endpoints(),
testing::ElementsAre(kEndpoint));
}
// Do a lookup with a NetworkIsolationKey, and then make sure the entry added to
// the cache is in fact using that NetworkIsolationKey.
TEST_F(ContextHostResolverTest, ResultsAddedToCacheWithNetworkIsolationKey) {
const SchemefulSite kSite(GURL("https://origin.test/"));
const NetworkIsolationKey kNetworkIsolationKey(kSite, kSite);
auto kNetworkAnonymizationKey =
net::NetworkAnonymizationKey::CreateSameSite(kSite);
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(
features::kSplitHostCacheByNetworkIsolationKey);
MockDnsClientRuleList rules;
rules.emplace_back("example.com", dns_protocol::kTypeA, false /* secure */,
MockDnsClientRule::Result(BuildTestDnsAddressResponse(
"example.com", kEndpoint.address())),
false /* delay */);
rules.emplace_back(
"example.com", dns_protocol::kTypeAAAA, false /* secure */,
MockDnsClientRule::Result(MockDnsClientRule::ResultType::kEmpty),
false /* delay */);
SetMockDnsRules(std::move(rules));
auto resolve_context = std::make_unique<ResolveContext>(
nullptr /* url_request_context */, true /* enable_caching */);
auto resolver = std::make_unique<ContextHostResolver>(
manager_.get(), std::move(resolve_context));
std::unique_ptr<HostResolver::ResolveHostRequest> caching_request =
resolver->CreateRequest(HostPortPair("example.com", 103),
kNetworkAnonymizationKey, NetLogWithSource(),
absl::nullopt);
TestCompletionCallback caching_callback;
int rv = caching_request->Start(caching_callback.callback());
EXPECT_THAT(caching_callback.GetResult(rv), test::IsOk());
HostCache::Key cache_key("example.com", DnsQueryType::UNSPECIFIED,
0 /* host_resolver_flags */, HostResolverSource::ANY,
kNetworkAnonymizationKey);
EXPECT_TRUE(
resolver->GetHostCache()->Lookup(cache_key, base::TimeTicks::Now()));
HostCache::Key cache_key_with_empty_nak(
"example.com", DnsQueryType::UNSPECIFIED, 0 /* host_resolver_flags */,
HostResolverSource::ANY, NetworkAnonymizationKey());
EXPECT_FALSE(resolver->GetHostCache()->Lookup(cache_key_with_empty_nak,
base::TimeTicks::Now()));
}
// Test that the underlying HostCache can receive invalidations from the manager
// and that it safely does not receive invalidations after the resolver (and the
// HostCache) is destroyed.
TEST_F(ContextHostResolverTest, HostCacheInvalidation) {
// Set empty MockDnsClient rules to ensure DnsClient is mocked out.
MockDnsClientRuleList rules;
SetMockDnsRules(std::move(rules));
auto resolve_context = std::make_unique<ResolveContext>(
nullptr /* url_request_context */, true /* enable_caching */);
ResolveContext* resolve_context_ptr = resolve_context.get();
auto resolver = std::make_unique<ContextHostResolver>(
manager_.get(), std::move(resolve_context));
// No invalidations yet (other than the initialization "invalidation" from
// registering the context).
ASSERT_EQ(resolve_context_ptr->current_session_for_testing(),
dns_client_->GetCurrentSession());
ASSERT_EQ(resolve_context_ptr->host_cache()->network_changes(), 1);
manager_->InvalidateCachesForTesting();
EXPECT_EQ(resolve_context_ptr->current_session_for_testing(),
dns_client_->GetCurrentSession());
EXPECT_EQ(resolve_context_ptr->host_cache()->network_changes(), 2);
// Expect manager to be able to safely do invalidations after an individual
// ContextHostResolver has been destroyed (and deregisters its ResolveContext)
resolver = nullptr;
manager_->InvalidateCachesForTesting();
}
class NetworkBoundResolveContext : public ResolveContext {
public:
NetworkBoundResolveContext(URLRequestContext* url_request_context,
bool enable_caching,
handles::NetworkHandle target_network)
: ResolveContext(url_request_context, enable_caching),
target_network_(target_network) {}
handles::NetworkHandle GetTargetNetwork() const override {
return target_network_;
}
private:
const handles::NetworkHandle target_network_;
};
// A mock HostResolverProc which returns different IP addresses based on the
// `network` parameter received.
class NetworkAwareHostResolverProc : public HostResolverProc {
public:
NetworkAwareHostResolverProc() : HostResolverProc(nullptr) {}
NetworkAwareHostResolverProc(const NetworkAwareHostResolverProc&) = delete;
NetworkAwareHostResolverProc& operator=(const NetworkAwareHostResolverProc&) =
delete;
int Resolve(const std::string& host,
AddressFamily address_family,
HostResolverFlags host_resolver_flags,
AddressList* addrlist,
int* os_error,
handles::NetworkHandle network) override {
// Presume failure
*os_error = 1;
const auto* iter = kResults.find(network);
if (iter == kResults.end())
return ERR_NETWORK_CHANGED;
*os_error = 0;
*addrlist = AddressList();
addrlist->push_back(ToIPEndPoint(iter->second));
return OK;
}
int Resolve(const std::string& host,
AddressFamily address_family,
HostResolverFlags host_resolver_flags,
AddressList* addrlist,
int* os_error) override {
return Resolve(host, address_family, host_resolver_flags, addrlist,
os_error, handles::kInvalidNetworkHandle);
}
struct IPv4 {
uint8_t a;
uint8_t b;
uint8_t c;
uint8_t d;
};
static constexpr int kPort = 100;
static constexpr auto kResults =
base::MakeFixedFlatMap<handles::NetworkHandle, IPv4>(
{{1, IPv4{1, 2, 3, 4}}, {2, IPv4{8, 8, 8, 8}}});
static IPEndPoint ToIPEndPoint(const IPv4& ipv4) {
return IPEndPoint(IPAddress(ipv4.a, ipv4.b, ipv4.c, ipv4.d), kPort);
}
protected:
~NetworkAwareHostResolverProc() override = default;
};
TEST_F(ContextHostResolverTest, ExistingNetworkBoundLookup) {
#if BUILDFLAG(IS_ANDROID)
auto scoped_mock_network_change_notifier =
std::make_unique<test::ScopedMockNetworkChangeNotifier>();
scoped_mock_network_change_notifier->mock_network_change_notifier()
->ForceNetworkHandlesSupported();
const url::SchemeHostPort host(url::kHttpsScheme, "example.com",
NetworkAwareHostResolverProc::kPort);
auto resolver_proc = base::MakeRefCounted<NetworkAwareHostResolverProc>();
ScopedDefaultHostResolverProc scoped_default_host_resolver;
scoped_default_host_resolver.Init(resolver_proc.get());
// ResolveContexts bound to a specific network should end up in a call to
// Resolve with `network` == context.GetTargetNetwork(). Confirm that we do
// indeed receive the IP address associated with that network.
for (const auto& iter : NetworkAwareHostResolverProc::kResults) {
auto network = iter.first;
auto expected_ipv4 = iter.second;
auto resolve_context = std::make_unique<NetworkBoundResolveContext>(
nullptr /* url_request_context */, false /* enable_caching */, network);
// DNS lookups originated from network-bound ResolveContexts must be
// resolved through a HostResolverManager bound to the same network.
auto manager = HostResolverManager::CreateNetworkBoundHostResolverManager(
HostResolver::ManagerOptions(), network, nullptr /* net_log */);
auto resolver = std::make_unique<ContextHostResolver>(
manager.get(), std::move(resolve_context));
std::unique_ptr<HostResolver::ResolveHostRequest> request =
resolver->CreateRequest(host, NetworkAnonymizationKey(),
NetLogWithSource(), absl::nullopt);
TestCompletionCallback callback;
int rv = request->Start(callback.callback());
EXPECT_THAT(callback.GetResult(rv), test::IsOk());
EXPECT_THAT(request->GetResolveErrorInfo().error, test::IsError(net::OK));
ASSERT_EQ(1u, request->GetAddressResults()->endpoints().size());
EXPECT_THAT(request->GetAddressResults()->endpoints(),
testing::ElementsAre(
NetworkAwareHostResolverProc::ToIPEndPoint(expected_ipv4)));
}
#else // !BUILDFLAG(IS_ANDROID)
GTEST_SKIP()
<< "Network-bound HostResolverManager are supported only on Android.";
#endif // BUILDFLAG(IS_ANDROID)
}
TEST_F(ContextHostResolverTest, NotExistingNetworkBoundLookup) {
const url::SchemeHostPort host(url::kHttpsScheme, "example.com",
NetworkAwareHostResolverProc::kPort);
auto resolver_proc = base::MakeRefCounted<NetworkAwareHostResolverProc>();
ScopedDefaultHostResolverProc scoped_default_host_resolver;
scoped_default_host_resolver.Init(resolver_proc.get());
// Non-bound ResolveContexts should end up with a call to Resolve with
// `network` == kInvalidNetwork, which NetworkAwareHostResolverProc fails to
// resolve.
auto resolve_context = std::make_unique<ResolveContext>(
nullptr /* url_request_context */, false /* enable_caching */);
auto resolver = std::make_unique<ContextHostResolver>(
manager_.get(), std::move(resolve_context));
std::unique_ptr<HostResolver::ResolveHostRequest> request =
resolver->CreateRequest(host, NetworkAnonymizationKey(),
NetLogWithSource(), absl::nullopt);
TestCompletionCallback callback;
int rv = request->Start(callback.callback());
EXPECT_THAT(callback.GetResult(rv),
test::IsError(net::ERR_NAME_NOT_RESOLVED));
EXPECT_THAT(request->GetResolveErrorInfo().error,
test::IsError(net::ERR_NETWORK_CHANGED));
}
// Test that the underlying HostCache does not receive invalidations when its
// ResolveContext/HostResolverManager is bound to a network.
TEST_F(ContextHostResolverTest, NetworkBoundResolverCacheInvalidation) {
#if BUILDFLAG(IS_ANDROID)
auto scoped_mock_network_change_notifier =
std::make_unique<test::ScopedMockNetworkChangeNotifier>();
test::MockNetworkChangeNotifier* mock_ncn =
scoped_mock_network_change_notifier->mock_network_change_notifier();
mock_ncn->ForceNetworkHandlesSupported();
// The actual network handle doesn't really matter, this test just wants to
// check that all the pieces are in place and configured correctly.
constexpr handles::NetworkHandle network = 2;
manager_ = HostResolverManager::CreateNetworkBoundHostResolverManager(
HostResolver::ManagerOptions(), network, nullptr /* net_log */);
manager_->SetLastIPv6ProbeResultForTesting(true);
// Set empty MockDnsClient rules to ensure DnsClient is mocked out.
MockDnsClientRuleList rules;
SetMockDnsRules(std::move(rules));
auto resolve_context = std::make_unique<NetworkBoundResolveContext>(
nullptr /* url_request_context */, true /* enable_caching */, network);
ResolveContext* resolve_context_ptr = resolve_context.get();
auto resolver = std::make_unique<ContextHostResolver>(
manager_.get(), std::move(resolve_context));
// Network events should not trigger cache invalidations
auto network_changes_before_events =
resolve_context_ptr->host_cache()->network_changes();
NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
NetworkChangeNotifier::NotifyObserversOfConnectionTypeChangeForTests(
NetworkChangeNotifier::CONNECTION_NONE);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(network_changes_before_events,
resolve_context_ptr->host_cache()->network_changes());
#else // !BUILDFLAG(IS_ANDROID)
GTEST_SKIP()
<< "Network-bound HostResolverManagers are supported only on Android";
#endif // BUILDFLAG(IS_ANDROID)
}
} // namespace net