// Copyright 2023 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/host_resolver_cache.h" #include #include #include #include #include "base/test/simple_test_clock.h" #include "base/test/simple_test_tick_clock.h" #include "base/time/time.h" #include "net/base/connection_endpoint_metadata.h" #include "net/base/connection_endpoint_metadata_test_util.h" #include "net/base/ip_address.h" #include "net/base/ip_endpoint.h" #include "net/base/net_errors.h" #include "net/base/network_anonymization_key.h" #include "net/base/schemeful_site.h" #include "net/dns/host_resolver_internal_result.h" #include "net/dns/host_resolver_internal_result_test_util.h" #include "net/dns/public/dns_query_type.h" #include "net/dns/public/host_resolver_source.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/gurl.h" namespace net { namespace { using ::testing::ElementsAre; using ::testing::Eq; using ::testing::IsEmpty; using ::testing::Ne; using ::testing::Optional; using ::testing::Pair; using ::testing::Pointee; MATCHER(IsNotStale, "") { return !arg.IsStale() && !arg.expired_by.has_value() && !arg.stale_by_generation; } MATCHER_P(IsNotStale, result_matcher, "") { return !arg.IsStale() && !arg.expired_by.has_value() && !arg.stale_by_generation && ExplainMatchResult(result_matcher, arg.result.get(), result_listener); } // Fudge TimeDelta matching by a couple milliseconds because it is not important // whether something is considered expired at or just after expiration because // TTLs come at second-wide precision anyway. MATCHER_P(TimeDeltaIsApproximately, approximate_expectation, "") { return arg - base::Milliseconds(3) <= approximate_expectation && arg + base::Milliseconds(3) >= approximate_expectation; } MATCHER_P2(IsStale, expired_by_matcher, expected_stale_by_generation, "") { return arg.IsStale() && ExplainMatchResult(expired_by_matcher, arg.expired_by, result_listener) && arg.stale_by_generation == expected_stale_by_generation; } MATCHER_P3(IsStale, result_matcher, expired_by_matcher, expected_stale_by_generation, "") { return arg.IsStale() && ExplainMatchResult(result_matcher, arg.result.get(), result_listener) && ExplainMatchResult(expired_by_matcher, arg.expired_by, result_listener) && arg.stale_by_generation == expected_stale_by_generation; } class HostResolverCacheTest : public ::testing::Test { protected: const size_t kMaxResults = 10; base::SimpleTestClock clock_; base::SimpleTestTickClock tick_clock_; }; TEST_F(HostResolverCacheTest, CacheAResult) { HostResolverCache cache(kMaxResults, clock_, tick_clock_); const std::string kName = "foo.test"; const base::TimeDelta kTtl = base::Minutes(2); const std::vector kEndpoints = { IPEndPoint(IPAddress(1, 2, 3, 4), /*port=*/0), IPEndPoint(IPAddress(2, 3, 4, 5), /*port=*/0)}; auto result = std::make_unique( kName, DnsQueryType::A, tick_clock_.NowTicks() + kTtl, clock_.Now() + kTtl, HostResolverInternalResult::Source::kDns, kEndpoints, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); const NetworkAnonymizationKey anonymization_key; cache.Set(std::move(result), anonymization_key, HostResolverSource::DNS, /*secure=*/false); auto matcher = Pointee(ExpectHostResolverInternalDataResult( kName, DnsQueryType::A, HostResolverInternalResult::Source::kDns, Optional(tick_clock_.NowTicks() + kTtl), Optional(clock_.Now() + kTtl), kEndpoints)); EXPECT_THAT(cache.Lookup(kName, anonymization_key, DnsQueryType::A, HostResolverSource::DNS, /*secure=*/false), matcher); EXPECT_THAT(cache.Lookup(kName, anonymization_key, DnsQueryType::UNSPECIFIED, HostResolverSource::DNS, /*secure=*/false), matcher); EXPECT_THAT(cache.Lookup(kName, anonymization_key, DnsQueryType::A, HostResolverSource::ANY, /*secure=*/false), matcher); EXPECT_THAT(cache.Lookup(kName, anonymization_key, DnsQueryType::A, HostResolverSource::DNS, /*secure=*/std::nullopt), matcher); EXPECT_EQ(cache.Lookup(kName, anonymization_key, DnsQueryType::AAAA, HostResolverSource::DNS, /*secure=*/false), nullptr); EXPECT_EQ(cache.Lookup(kName, anonymization_key, DnsQueryType::A, HostResolverSource::SYSTEM, /*secure=*/false), nullptr); EXPECT_EQ(cache.Lookup(kName, anonymization_key, DnsQueryType::A, HostResolverSource::DNS, /*secure=*/true), nullptr); auto stale_result_matcher = Optional(IsNotStale(ExpectHostResolverInternalDataResult( kName, DnsQueryType::A, HostResolverInternalResult::Source::kDns, Optional(tick_clock_.NowTicks() + kTtl), Optional(clock_.Now() + kTtl), kEndpoints))); EXPECT_THAT(cache.LookupStale(kName, anonymization_key, DnsQueryType::A, HostResolverSource::DNS, /*secure=*/false), stale_result_matcher); EXPECT_THAT( cache.LookupStale(kName, anonymization_key, DnsQueryType::UNSPECIFIED, HostResolverSource::DNS, /*secure=*/false), stale_result_matcher); EXPECT_THAT(cache.LookupStale(kName, anonymization_key, DnsQueryType::A, HostResolverSource::ANY, /*secure=*/false), stale_result_matcher); EXPECT_THAT( cache.LookupStale(kName, anonymization_key, DnsQueryType::A, HostResolverSource::DNS, /*secure=*/std::nullopt), stale_result_matcher); EXPECT_EQ(cache.LookupStale(kName, anonymization_key, DnsQueryType::AAAA, HostResolverSource::DNS, /*secure=*/false), std::nullopt); EXPECT_EQ(cache.LookupStale(kName, anonymization_key, DnsQueryType::A, HostResolverSource::SYSTEM, /*secure=*/false), std::nullopt); EXPECT_EQ(cache.LookupStale(kName, anonymization_key, DnsQueryType::A, HostResolverSource::DNS, /*secure=*/true), std::nullopt); } TEST_F(HostResolverCacheTest, CacheAaaaResult) { HostResolverCache cache(kMaxResults, clock_, tick_clock_); const std::string kName = "foo.test"; const base::TimeDelta kTtl = base::Minutes(2); const std::vector kEndpoints = { IPEndPoint(IPAddress::FromIPLiteral("::1").value(), /*port=*/0), IPEndPoint(IPAddress::FromIPLiteral("2001:DB8::4").value(), /*port=*/0)}; auto result = std::make_unique( kName, DnsQueryType::AAAA, tick_clock_.NowTicks() + kTtl, clock_.Now() + kTtl, HostResolverInternalResult::Source::kDns, kEndpoints, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); const NetworkAnonymizationKey anonymization_key; cache.Set(std::move(result), anonymization_key, HostResolverSource::DNS, /*secure=*/false); auto matcher = Pointee(ExpectHostResolverInternalDataResult( kName, DnsQueryType::AAAA, HostResolverInternalResult::Source::kDns, Optional(tick_clock_.NowTicks() + kTtl), Optional(clock_.Now() + kTtl), kEndpoints)); EXPECT_THAT(cache.Lookup(kName, anonymization_key, DnsQueryType::AAAA, HostResolverSource::DNS, /*secure=*/false), matcher); EXPECT_THAT(cache.Lookup(kName, anonymization_key, DnsQueryType::UNSPECIFIED, HostResolverSource::DNS, /*secure=*/false), matcher); EXPECT_THAT(cache.Lookup(kName, anonymization_key, DnsQueryType::AAAA, HostResolverSource::ANY, /*secure=*/false), matcher); EXPECT_THAT(cache.Lookup(kName, anonymization_key, DnsQueryType::AAAA, HostResolverSource::DNS, /*secure=*/std::nullopt), matcher); EXPECT_EQ(cache.Lookup(kName, anonymization_key, DnsQueryType::A, HostResolverSource::DNS, /*secure=*/false), nullptr); EXPECT_EQ(cache.Lookup(kName, anonymization_key, DnsQueryType::AAAA, HostResolverSource::SYSTEM, /*secure=*/false), nullptr); EXPECT_EQ(cache.Lookup(kName, anonymization_key, DnsQueryType::AAAA, HostResolverSource::DNS, /*secure=*/true), nullptr); auto stale_result_matcher = Optional(IsNotStale(ExpectHostResolverInternalDataResult( kName, DnsQueryType::AAAA, HostResolverInternalResult::Source::kDns, Optional(tick_clock_.NowTicks() + kTtl), Optional(clock_.Now() + kTtl), kEndpoints))); EXPECT_THAT(cache.LookupStale(kName, anonymization_key, DnsQueryType::AAAA, HostResolverSource::DNS, /*secure=*/false), stale_result_matcher); EXPECT_THAT( cache.LookupStale(kName, anonymization_key, DnsQueryType::UNSPECIFIED, HostResolverSource::DNS, /*secure=*/false), stale_result_matcher); EXPECT_THAT(cache.LookupStale(kName, anonymization_key, DnsQueryType::AAAA, HostResolverSource::ANY, /*secure=*/false), stale_result_matcher); EXPECT_THAT( cache.LookupStale(kName, anonymization_key, DnsQueryType::AAAA, HostResolverSource::DNS, /*secure=*/std::nullopt), stale_result_matcher); EXPECT_EQ(cache.LookupStale(kName, anonymization_key, DnsQueryType::A, HostResolverSource::DNS, /*secure=*/false), std::nullopt); EXPECT_EQ(cache.LookupStale(kName, anonymization_key, DnsQueryType::AAAA, HostResolverSource::SYSTEM, /*secure=*/false), std::nullopt); EXPECT_EQ(cache.LookupStale(kName, anonymization_key, DnsQueryType::AAAA, HostResolverSource::DNS, /*secure=*/true), std::nullopt); } TEST_F(HostResolverCacheTest, CacheHttpsResult) { HostResolverCache cache(kMaxResults, clock_, tick_clock_); const std::string kName = "foo.test"; const base::TimeDelta kTtl = base::Minutes(2); const std::multimap kMetadatas = { {2, ConnectionEndpointMetadata({"h2", "h3"}, /*ech_config_list=*/{}, kName)}, {1, ConnectionEndpointMetadata({"h2"}, /*ech_config_list=*/{}, kName)}}; auto result = std::make_unique( kName, DnsQueryType::HTTPS, tick_clock_.NowTicks() + kTtl, clock_.Now() + kTtl, HostResolverInternalResult::Source::kDns, kMetadatas); const NetworkAnonymizationKey anonymization_key; cache.Set(std::move(result), anonymization_key, HostResolverSource::DNS, /*secure=*/false); auto matcher = Pointee(ExpectHostResolverInternalMetadataResult( kName, DnsQueryType::HTTPS, HostResolverInternalResult::Source::kDns, Optional(tick_clock_.NowTicks() + kTtl), Optional(clock_.Now() + kTtl), kMetadatas)); EXPECT_THAT(cache.Lookup(kName, anonymization_key, DnsQueryType::HTTPS, HostResolverSource::DNS, /*secure=*/false), matcher); EXPECT_THAT(cache.Lookup(kName, anonymization_key, DnsQueryType::UNSPECIFIED, HostResolverSource::DNS, /*secure=*/false), matcher); EXPECT_THAT(cache.Lookup(kName, anonymization_key, DnsQueryType::HTTPS, HostResolverSource::ANY, /*secure=*/false), matcher); EXPECT_THAT(cache.Lookup(kName, anonymization_key, DnsQueryType::HTTPS, HostResolverSource::DNS, /*secure=*/std::nullopt), matcher); EXPECT_EQ(cache.Lookup(kName, anonymization_key, DnsQueryType::A, HostResolverSource::DNS, /*secure=*/false), nullptr); EXPECT_EQ(cache.Lookup(kName, anonymization_key, DnsQueryType::HTTPS, HostResolverSource::SYSTEM, /*secure=*/false), nullptr); EXPECT_EQ(cache.Lookup(kName, anonymization_key, DnsQueryType::HTTPS, HostResolverSource::DNS, /*secure=*/true), nullptr); auto stale_result_matcher = Optional(IsNotStale(ExpectHostResolverInternalMetadataResult( kName, DnsQueryType::HTTPS, HostResolverInternalResult::Source::kDns, Optional(tick_clock_.NowTicks() + kTtl), Optional(clock_.Now() + kTtl), kMetadatas))); EXPECT_THAT(cache.LookupStale(kName, anonymization_key, DnsQueryType::HTTPS, HostResolverSource::DNS, /*secure=*/false), stale_result_matcher); EXPECT_THAT( cache.LookupStale(kName, anonymization_key, DnsQueryType::UNSPECIFIED, HostResolverSource::DNS, /*secure=*/false), stale_result_matcher); EXPECT_THAT(cache.LookupStale(kName, anonymization_key, DnsQueryType::HTTPS, HostResolverSource::ANY, /*secure=*/false), stale_result_matcher); EXPECT_THAT( cache.LookupStale(kName, anonymization_key, DnsQueryType::HTTPS, HostResolverSource::DNS, /*secure=*/std::nullopt), stale_result_matcher); EXPECT_EQ(cache.LookupStale(kName, anonymization_key, DnsQueryType::A, HostResolverSource::DNS, /*secure=*/false), std::nullopt); EXPECT_EQ(cache.LookupStale(kName, anonymization_key, DnsQueryType::HTTPS, HostResolverSource::SYSTEM, /*secure=*/false), std::nullopt); EXPECT_EQ(cache.LookupStale(kName, anonymization_key, DnsQueryType::HTTPS, HostResolverSource::DNS, /*secure=*/true), std::nullopt); } // Domain names containing scheme/port are not expected to be handled any // differently from other domain names. That is, if an entry is cached with // a domain name containing scheme or port, it can only be looked up using the // exact same domain name containing scheme and port. Testing the case simply // because such things were handled differently in a previous version of the // cache. TEST_F(HostResolverCacheTest, RespectsSchemeAndPortInName) { HostResolverCache cache(kMaxResults, clock_, tick_clock_); const std::string kNameWithScheme = "_411._https.foo.test"; const base::TimeDelta kTtl = base::Minutes(2); const std::string kAlpn1 = "foo"; auto result1 = std::make_unique( kNameWithScheme, DnsQueryType::HTTPS, tick_clock_.NowTicks() + kTtl, clock_.Now() + kTtl, HostResolverInternalResult::Source::kDns, std::multimap{ {4, ConnectionEndpointMetadata({kAlpn1}, /*ech_config_list=*/{}, kNameWithScheme)}}); const std::string kNameWithoutScheme = "foo.test"; const std::string kAlpn2 = "bar"; auto result2 = std::make_unique( kNameWithoutScheme, DnsQueryType::HTTPS, tick_clock_.NowTicks() + kTtl, clock_.Now() + kTtl, HostResolverInternalResult::Source::kDns, std::multimap{ {7, ConnectionEndpointMetadata({kAlpn2}, /*ech_config_list=*/{}, kNameWithoutScheme)}}); const NetworkAnonymizationKey anonymization_key; cache.Set(std::move(result1), anonymization_key, HostResolverSource::DNS, /*secure=*/false); cache.Set(std::move(result2), anonymization_key, HostResolverSource::DNS, /*secure=*/false); EXPECT_THAT(cache.Lookup(kNameWithScheme, anonymization_key), Pointee(ExpectHostResolverInternalMetadataResult( kNameWithScheme, DnsQueryType::HTTPS, HostResolverInternalResult::Source::kDns, /*expiration_matcher=*/Ne(std::nullopt), /*timed_expiration_matcher=*/Ne(std::nullopt), ElementsAre(Pair(4, ExpectConnectionEndpointMetadata( ElementsAre(kAlpn1), IsEmpty(), kNameWithScheme)))))); EXPECT_THAT(cache.Lookup(kNameWithoutScheme, anonymization_key), Pointee(ExpectHostResolverInternalMetadataResult( kNameWithoutScheme, DnsQueryType::HTTPS, HostResolverInternalResult::Source::kDns, /*expiration_matcher=*/Ne(std::nullopt), /*timed_expiration_matcher=*/Ne(std::nullopt), ElementsAre(Pair(7, ExpectConnectionEndpointMetadata( ElementsAre(kAlpn2), IsEmpty(), kNameWithoutScheme)))))); } TEST_F(HostResolverCacheTest, CacheHttpsAliasResult) { HostResolverCache cache(kMaxResults, clock_, tick_clock_); const std::string kName = "foo.test"; const base::TimeDelta kTtl = base::Minutes(2); const std::string kTarget = "target.test"; auto result = std::make_unique( kName, DnsQueryType::HTTPS, tick_clock_.NowTicks() + kTtl, clock_.Now() + kTtl, HostResolverInternalResult::Source::kDns, kTarget); const NetworkAnonymizationKey anonymization_key; cache.Set(std::move(result), anonymization_key, HostResolverSource::DNS, /*secure=*/false); auto matcher = Pointee(ExpectHostResolverInternalAliasResult( kName, DnsQueryType::HTTPS, HostResolverInternalResult::Source::kDns, Optional(tick_clock_.NowTicks() + kTtl), Optional(clock_.Now() + kTtl), kTarget)); EXPECT_THAT(cache.Lookup(kName, anonymization_key, DnsQueryType::HTTPS, HostResolverSource::DNS, /*secure=*/false), matcher); EXPECT_THAT(cache.Lookup(kName, anonymization_key, DnsQueryType::UNSPECIFIED, HostResolverSource::DNS, /*secure=*/false), matcher); EXPECT_THAT(cache.Lookup(kName, anonymization_key, DnsQueryType::HTTPS, HostResolverSource::ANY, /*secure=*/false), matcher); EXPECT_THAT(cache.Lookup(kName, anonymization_key, DnsQueryType::HTTPS, HostResolverSource::DNS, /*secure=*/std::nullopt), matcher); EXPECT_EQ(cache.Lookup(kName, anonymization_key, DnsQueryType::A, HostResolverSource::DNS, /*secure=*/false), nullptr); EXPECT_EQ(cache.Lookup(kName, anonymization_key, DnsQueryType::HTTPS, HostResolverSource::SYSTEM, /*secure=*/false), nullptr); EXPECT_EQ(cache.Lookup(kName, anonymization_key, DnsQueryType::HTTPS, HostResolverSource::DNS, /*secure=*/true), nullptr); } TEST_F(HostResolverCacheTest, CacheCnameAliasResult) { HostResolverCache cache(kMaxResults, clock_, tick_clock_); const std::string kName = "foo.test"; const base::TimeDelta kTtl = base::Minutes(2); const std::string kTarget = "target.test"; // CNAME results are not typically queried directly, but received as part of // the results for queries for other query types. Thus except in the weird // cases where it is queried directly, CNAME results should be cached for the // queried type (or as a wildcard UNSPECIFIED type), rather than type CNAME. // Here, test the case where it is cached under the AAAA query type. auto result = std::make_unique( kName, DnsQueryType::AAAA, tick_clock_.NowTicks() + kTtl, clock_.Now() + kTtl, HostResolverInternalResult::Source::kDns, kTarget); const NetworkAnonymizationKey anonymization_key; cache.Set(std::move(result), anonymization_key, HostResolverSource::DNS, /*secure=*/false); auto matcher = Pointee(ExpectHostResolverInternalAliasResult( kName, DnsQueryType::AAAA, HostResolverInternalResult::Source::kDns, Optional(tick_clock_.NowTicks() + kTtl), Optional(clock_.Now() + kTtl), kTarget)); EXPECT_THAT(cache.Lookup(kName, anonymization_key, DnsQueryType::AAAA, HostResolverSource::DNS, /*secure=*/false), matcher); EXPECT_THAT(cache.Lookup(kName, anonymization_key, DnsQueryType::UNSPECIFIED, HostResolverSource::DNS, /*secure=*/false), matcher); EXPECT_THAT(cache.Lookup(kName, anonymization_key, DnsQueryType::AAAA, HostResolverSource::ANY, /*secure=*/false), matcher); EXPECT_THAT(cache.Lookup(kName, anonymization_key, DnsQueryType::AAAA, HostResolverSource::DNS, /*secure=*/std::nullopt), matcher); EXPECT_EQ(cache.Lookup(kName, anonymization_key, DnsQueryType::A, HostResolverSource::DNS, /*secure=*/false), nullptr); EXPECT_EQ(cache.Lookup(kName, anonymization_key, DnsQueryType::AAAA, HostResolverSource::SYSTEM, /*secure=*/false), nullptr); EXPECT_EQ(cache.Lookup(kName, anonymization_key, DnsQueryType::AAAA, HostResolverSource::DNS, /*secure=*/true), nullptr); } TEST_F(HostResolverCacheTest, CacheWildcardAlias) { HostResolverCache cache(kMaxResults, clock_, tick_clock_); const std::string kName = "foo.test"; const std::string kAliasTarget = "target.test"; const base::TimeDelta kTtl = base::Minutes(2); auto result = std::make_unique( kName, DnsQueryType::UNSPECIFIED, tick_clock_.NowTicks() + kTtl, clock_.Now() + kTtl, HostResolverInternalResult::Source::kDns, kAliasTarget); const NetworkAnonymizationKey anonymization_key; cache.Set(std::move(result), anonymization_key, HostResolverSource::DNS, /*secure=*/false); auto matcher = Pointee(ExpectHostResolverInternalAliasResult( kName, DnsQueryType::UNSPECIFIED, HostResolverInternalResult::Source::kDns, Optional(tick_clock_.NowTicks() + kTtl), Optional(clock_.Now() + kTtl), kAliasTarget)); EXPECT_THAT(cache.Lookup(kName, anonymization_key, DnsQueryType::UNSPECIFIED), matcher); EXPECT_THAT(cache.Lookup(kName, anonymization_key, DnsQueryType::A), matcher); EXPECT_THAT(cache.Lookup(kName, anonymization_key, DnsQueryType::AAAA), matcher); EXPECT_THAT(cache.Lookup(kName, anonymization_key, DnsQueryType::HTTPS), matcher); EXPECT_THAT(cache.Lookup(kName, anonymization_key, DnsQueryType::TXT), matcher); } TEST_F(HostResolverCacheTest, CacheErrorResult) { HostResolverCache cache(kMaxResults, clock_, tick_clock_); const std::string kName = "foo.test"; const base::TimeDelta kTtl = base::Minutes(2); auto result = std::make_unique( kName, DnsQueryType::AAAA, tick_clock_.NowTicks() + kTtl, clock_.Now() + kTtl, HostResolverInternalResult::Source::kDns, ERR_NAME_NOT_RESOLVED); const NetworkAnonymizationKey anonymization_key; cache.Set(std::move(result), anonymization_key, HostResolverSource::DNS, /*secure=*/false); auto matcher = Pointee(ExpectHostResolverInternalErrorResult( kName, DnsQueryType::AAAA, HostResolverInternalResult::Source::kDns, Optional(tick_clock_.NowTicks() + kTtl), Optional(clock_.Now() + kTtl), ERR_NAME_NOT_RESOLVED)); EXPECT_THAT(cache.Lookup(kName, anonymization_key, DnsQueryType::AAAA, HostResolverSource::DNS, /*secure=*/false), matcher); EXPECT_THAT(cache.Lookup(kName, anonymization_key, DnsQueryType::UNSPECIFIED, HostResolverSource::DNS, /*secure=*/false), matcher); EXPECT_THAT(cache.Lookup(kName, anonymization_key, DnsQueryType::AAAA, HostResolverSource::ANY, /*secure=*/false), matcher); EXPECT_THAT(cache.Lookup(kName, anonymization_key, DnsQueryType::AAAA, HostResolverSource::DNS, /*secure=*/std::nullopt), matcher); EXPECT_EQ(cache.Lookup(kName, anonymization_key, DnsQueryType::A, HostResolverSource::DNS, /*secure=*/false), nullptr); EXPECT_EQ(cache.Lookup(kName, anonymization_key, DnsQueryType::AAAA, HostResolverSource::SYSTEM, /*secure=*/false), nullptr); EXPECT_EQ(cache.Lookup(kName, anonymization_key, DnsQueryType::AAAA, HostResolverSource::DNS, /*secure=*/true), nullptr); } TEST_F(HostResolverCacheTest, ResultsCanBeUpdated) { HostResolverCache cache(kMaxResults, clock_, tick_clock_); const std::string kName = "foo.test"; const base::TimeDelta kTtl = base::Minutes(2); const std::vector kEndpoints1 = { IPEndPoint(IPAddress::FromIPLiteral("::1").value(), /*port=*/0)}; auto result1 = std::make_unique( kName, DnsQueryType::AAAA, tick_clock_.NowTicks() + kTtl, clock_.Now() + kTtl, HostResolverInternalResult::Source::kDns, kEndpoints1, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); const std::string kName2 = "goo.test"; auto result2 = std::make_unique( kName2, DnsQueryType::AAAA, tick_clock_.NowTicks() + kTtl, clock_.Now() + kTtl, HostResolverInternalResult::Source::kDns, kEndpoints1, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); const NetworkAnonymizationKey anonymization_key; cache.Set(std::move(result1), anonymization_key, HostResolverSource::DNS, /*secure=*/false); cache.Set(std::move(result2), anonymization_key, HostResolverSource::DNS, /*secure=*/false); EXPECT_THAT( cache.Lookup(kName, anonymization_key), Pointee(ExpectHostResolverInternalDataResult( kName, DnsQueryType::AAAA, HostResolverInternalResult::Source::kDns, Optional(tick_clock_.NowTicks() + kTtl), Optional(clock_.Now() + kTtl), kEndpoints1))); EXPECT_THAT( cache.Lookup(kName2, anonymization_key), Pointee(ExpectHostResolverInternalDataResult( kName2, DnsQueryType::AAAA, HostResolverInternalResult::Source::kDns, Optional(tick_clock_.NowTicks() + kTtl), Optional(clock_.Now() + kTtl), kEndpoints1))); const std::vector kEndpoints2 = { IPEndPoint(IPAddress::FromIPLiteral("2001:DB8::4").value(), /*port=*/0)}; auto result3 = std::make_unique( kName, DnsQueryType::AAAA, tick_clock_.NowTicks() + kTtl, clock_.Now() + kTtl, HostResolverInternalResult::Source::kDns, kEndpoints2, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); cache.Set(std::move(result3), anonymization_key, HostResolverSource::DNS, /*secure=*/false); EXPECT_THAT( cache.Lookup(kName, anonymization_key), Pointee(ExpectHostResolverInternalDataResult( kName, DnsQueryType::AAAA, HostResolverInternalResult::Source::kDns, Optional(tick_clock_.NowTicks() + kTtl), Optional(clock_.Now() + kTtl), kEndpoints2))); EXPECT_THAT( cache.Lookup(kName2, anonymization_key), Pointee(ExpectHostResolverInternalDataResult( kName2, DnsQueryType::AAAA, HostResolverInternalResult::Source::kDns, Optional(tick_clock_.NowTicks() + kTtl), Optional(clock_.Now() + kTtl), kEndpoints1))); } TEST_F(HostResolverCacheTest, UpdateCanReplaceWildcard) { HostResolverCache cache(kMaxResults, clock_, tick_clock_); const std::string kName = "foo.test"; const std::string kAliasTarget1 = "target1.test"; const base::TimeDelta kTtl = base::Minutes(2); auto result1 = std::make_unique( kName, DnsQueryType::UNSPECIFIED, tick_clock_.NowTicks() + kTtl, clock_.Now() + kTtl, HostResolverInternalResult::Source::kDns, kAliasTarget1); const NetworkAnonymizationKey anonymization_key; cache.Set(std::move(result1), anonymization_key, HostResolverSource::DNS, /*secure=*/false); EXPECT_NE(cache.Lookup(kName, anonymization_key, DnsQueryType::A), nullptr); EXPECT_NE(cache.Lookup(kName, anonymization_key, DnsQueryType::AAAA), nullptr); const std::string kAliasTarget2 = "target2.test"; auto result2 = std::make_unique( kName, DnsQueryType::A, tick_clock_.NowTicks() + kTtl, clock_.Now() + kTtl, HostResolverInternalResult::Source::kDns, kAliasTarget2); cache.Set(std::move(result2), anonymization_key, HostResolverSource::DNS, /*secure=*/false); // After update, because most recent entry is not wildcard, expect lookup to // only succeed for the specific type. EXPECT_THAT( cache.Lookup(kName, anonymization_key, DnsQueryType::A), Pointee(ExpectHostResolverInternalAliasResult( kName, DnsQueryType::A, HostResolverInternalResult::Source::kDns, Optional(tick_clock_.NowTicks() + kTtl), Optional(clock_.Now() + kTtl), kAliasTarget2))); EXPECT_EQ(cache.Lookup(kName, anonymization_key, DnsQueryType::AAAA), nullptr); } TEST_F(HostResolverCacheTest, WildcardUpdateCanReplaceSpecifics) { HostResolverCache cache(kMaxResults, clock_, tick_clock_); const std::string kName = "foo.test"; const std::string kAliasTarget1 = "target1.test"; const base::TimeDelta kTtl = base::Minutes(2); auto result1 = std::make_unique( kName, DnsQueryType::A, tick_clock_.NowTicks() + kTtl, clock_.Now() + kTtl, HostResolverInternalResult::Source::kDns, kAliasTarget1); const std::string kAliasTarget2 = "target2.test"; auto result2 = std::make_unique( kName, DnsQueryType::AAAA, tick_clock_.NowTicks() + kTtl, clock_.Now() + kTtl, HostResolverInternalResult::Source::kDns, kAliasTarget2); const NetworkAnonymizationKey anonymization_key; cache.Set(std::move(result1), anonymization_key, HostResolverSource::DNS, /*secure=*/false); cache.Set(std::move(result2), anonymization_key, HostResolverSource::DNS, /*secure=*/false); EXPECT_THAT( cache.Lookup(kName, anonymization_key, DnsQueryType::A), Pointee(ExpectHostResolverInternalAliasResult( kName, DnsQueryType::A, HostResolverInternalResult::Source::kDns, Optional(tick_clock_.NowTicks() + kTtl), Optional(clock_.Now() + kTtl), kAliasTarget1))); EXPECT_THAT( cache.Lookup(kName, anonymization_key, DnsQueryType::AAAA), Pointee(ExpectHostResolverInternalAliasResult( kName, DnsQueryType::AAAA, HostResolverInternalResult::Source::kDns, Optional(tick_clock_.NowTicks() + kTtl), Optional(clock_.Now() + kTtl), kAliasTarget2))); EXPECT_EQ(cache.Lookup(kName, anonymization_key, DnsQueryType::HTTPS), nullptr); const std::string kAliasTarget3 = "target3.test"; auto result3 = std::make_unique( kName, DnsQueryType::UNSPECIFIED, tick_clock_.NowTicks() + kTtl, clock_.Now() + kTtl, HostResolverInternalResult::Source::kDns, kAliasTarget3); cache.Set(std::move(result3), anonymization_key, HostResolverSource::DNS, /*secure=*/false); EXPECT_THAT(cache.Lookup(kName, anonymization_key, DnsQueryType::A), Pointee(ExpectHostResolverInternalAliasResult( kName, DnsQueryType::UNSPECIFIED, HostResolverInternalResult::Source::kDns, Optional(tick_clock_.NowTicks() + kTtl), Optional(clock_.Now() + kTtl), kAliasTarget3))); EXPECT_THAT(cache.Lookup(kName, anonymization_key, DnsQueryType::AAAA), Pointee(ExpectHostResolverInternalAliasResult( kName, DnsQueryType::UNSPECIFIED, HostResolverInternalResult::Source::kDns, Optional(tick_clock_.NowTicks() + kTtl), Optional(clock_.Now() + kTtl), kAliasTarget3))); EXPECT_THAT(cache.Lookup(kName, anonymization_key, DnsQueryType::HTTPS), Pointee(ExpectHostResolverInternalAliasResult( kName, DnsQueryType::UNSPECIFIED, HostResolverInternalResult::Source::kDns, Optional(tick_clock_.NowTicks() + kTtl), Optional(clock_.Now() + kTtl), kAliasTarget3))); } TEST_F(HostResolverCacheTest, LookupNameIsCanonicalized) { HostResolverCache cache(kMaxResults, clock_, tick_clock_); const std::string kName = "fOO.test"; const base::TimeDelta kTtl = base::Minutes(2); const std::vector kEndpoints = { IPEndPoint(IPAddress::FromIPLiteral("2001:DB8::4").value(), /*port=*/0)}; auto result = std::make_unique( kName, DnsQueryType::AAAA, tick_clock_.NowTicks() + kTtl, clock_.Now() + kTtl, HostResolverInternalResult::Source::kDns, kEndpoints, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); const NetworkAnonymizationKey anonymization_key; cache.Set(std::move(result), anonymization_key, HostResolverSource::DNS, /*secure=*/false); EXPECT_NE(cache.Lookup("FOO.TEST", anonymization_key), nullptr); } TEST_F(HostResolverCacheTest, LookupIgnoresExpiredResults) { HostResolverCache cache(kMaxResults, clock_, tick_clock_); const std::string kName1 = "foo.test"; const base::TimeDelta kTtl1 = base::Minutes(2); const std::vector kEndpoints1 = { IPEndPoint(IPAddress::FromIPLiteral("::1").value(), /*port=*/0)}; auto result1 = std::make_unique( kName1, DnsQueryType::AAAA, tick_clock_.NowTicks() + kTtl1, clock_.Now() + kTtl1, HostResolverInternalResult::Source::kDns, kEndpoints1, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); const std::string kName2 = "bar.test"; const base::TimeDelta kTtl2 = base::Minutes(4); const std::vector kEndpoints2 = { IPEndPoint(IPAddress::FromIPLiteral("2001:DB8::4").value(), /*port=*/0)}; auto result2 = std::make_unique( kName2, DnsQueryType::AAAA, tick_clock_.NowTicks() + kTtl2, clock_.Now() + kTtl2, HostResolverInternalResult::Source::kDns, kEndpoints2, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); const NetworkAnonymizationKey anonymization_key; cache.Set(std::move(result1), anonymization_key, HostResolverSource::DNS, /*secure=*/false); cache.Set(std::move(result2), anonymization_key, HostResolverSource::DNS, /*secure=*/false); EXPECT_THAT( cache.Lookup(kName1, anonymization_key), Pointee(ExpectHostResolverInternalDataResult( kName1, DnsQueryType::AAAA, HostResolverInternalResult::Source::kDns, Optional(tick_clock_.NowTicks() + kTtl1), Optional(clock_.Now() + kTtl1), kEndpoints1))); EXPECT_THAT(cache.LookupStale(kName1, anonymization_key), Optional(IsNotStale())); EXPECT_THAT( cache.Lookup(kName2, anonymization_key), Pointee(ExpectHostResolverInternalDataResult( kName2, DnsQueryType::AAAA, HostResolverInternalResult::Source::kDns, Optional(tick_clock_.NowTicks() + kTtl2), Optional(clock_.Now() + kTtl2), kEndpoints2))); EXPECT_THAT(cache.LookupStale(kName2, anonymization_key), Optional(IsNotStale())); // Advance time until just before first expiration. Expect both results still // active. clock_.Advance(kTtl1 - base::Milliseconds(1)); tick_clock_.Advance(kTtl1 - base::Milliseconds(1)); EXPECT_NE(cache.Lookup(kName1, anonymization_key), nullptr); EXPECT_THAT(cache.LookupStale(kName1, anonymization_key), Optional(IsNotStale())); EXPECT_NE(cache.Lookup(kName2, anonymization_key), nullptr); EXPECT_THAT(cache.LookupStale(kName2, anonymization_key), Optional(IsNotStale())); // Advance time until just after first expiration. Expect first result now // stale, but second result still valid. clock_.Advance(base::Milliseconds(2)); tick_clock_.Advance(base::Milliseconds(2)); EXPECT_EQ(cache.Lookup(kName1, anonymization_key), nullptr); EXPECT_THAT( cache.LookupStale(kName1, anonymization_key), Optional(IsStale( ExpectHostResolverInternalDataResult( kName1, DnsQueryType::AAAA, HostResolverInternalResult::Source::kDns, Ne(std::nullopt), Ne(std::nullopt), kEndpoints1), Optional(TimeDeltaIsApproximately(base::Milliseconds(1))), false))); EXPECT_NE(cache.Lookup(kName2, anonymization_key), nullptr); EXPECT_THAT(cache.LookupStale(kName2, anonymization_key), Optional(IsNotStale())); // Advance time util just before second expiration. Expect first still stale // and second still valid. clock_.Advance(kTtl2 - kTtl1 - base::Milliseconds(2)); tick_clock_.Advance(kTtl2 - kTtl1 - base::Milliseconds(2)); EXPECT_EQ(cache.Lookup(kName1, anonymization_key), nullptr); EXPECT_THAT(cache.LookupStale(kName1, anonymization_key), Optional(IsStale(Optional(TimeDeltaIsApproximately( base::Minutes(2) - base::Milliseconds(1))), false))); EXPECT_NE(cache.Lookup(kName2, anonymization_key), nullptr); EXPECT_THAT(cache.LookupStale(kName2, anonymization_key), Optional(IsNotStale())); // Advance time to after second expiration. Expect both results now stale. clock_.Advance(base::Milliseconds(2)); tick_clock_.Advance(base::Milliseconds(2)); EXPECT_EQ(cache.Lookup(kName1, anonymization_key), nullptr); EXPECT_THAT(cache.LookupStale(kName1, anonymization_key), Optional(IsStale(Optional(TimeDeltaIsApproximately( base::Minutes(2) + base::Milliseconds(1))), false))); EXPECT_EQ(cache.Lookup(kName2, anonymization_key), nullptr); EXPECT_THAT( cache.LookupStale(kName2, anonymization_key), Optional(IsStale( ExpectHostResolverInternalDataResult( kName2, DnsQueryType::AAAA, HostResolverInternalResult::Source::kDns, Ne(std::nullopt), Ne(std::nullopt), kEndpoints2), Optional(TimeDeltaIsApproximately(base::Milliseconds(1))), false))); } TEST_F(HostResolverCacheTest, ExpiredResultsCanBeUpdated) { HostResolverCache cache(kMaxResults, clock_, tick_clock_); const std::string kName = "foo.test"; const std::vector kEndpoints = { IPEndPoint(IPAddress::FromIPLiteral("::1").value(), /*port=*/0)}; auto result = std::make_unique( kName, DnsQueryType::AAAA, tick_clock_.NowTicks() - base::Milliseconds(1), clock_.Now() - base::Milliseconds(1), HostResolverInternalResult::Source::kDns, kEndpoints, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); const NetworkAnonymizationKey anonymization_key; cache.Set(std::move(result), anonymization_key, HostResolverSource::DNS, /*secure=*/false); // Expiration before Now, so expect entry to start expired. EXPECT_EQ(cache.Lookup(kName, anonymization_key), nullptr); EXPECT_THAT( cache.LookupStale(kName, anonymization_key), Optional(IsStale( Optional(TimeDeltaIsApproximately(base::Milliseconds(1))), false))); const base::TimeDelta kTtl = base::Seconds(45); auto update_result = std::make_unique( kName, DnsQueryType::AAAA, tick_clock_.NowTicks() + kTtl, clock_.Now() + kTtl, HostResolverInternalResult::Source::kDns, kEndpoints, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); cache.Set(std::move(update_result), anonymization_key, HostResolverSource::DNS, /*secure=*/false); EXPECT_NE(cache.Lookup(kName, anonymization_key), nullptr); EXPECT_THAT(cache.LookupStale(kName, anonymization_key), Optional(IsNotStale())); // Expect entry to still be expirable for new TTL. clock_.Advance(kTtl + base::Milliseconds(1)); tick_clock_.Advance(kTtl + base::Milliseconds(1)); EXPECT_EQ(cache.Lookup(kName, anonymization_key), nullptr); EXPECT_THAT( cache.LookupStale(kName, anonymization_key), Optional(IsStale( Optional(TimeDeltaIsApproximately(base::Milliseconds(1))), false))); } TEST_F(HostResolverCacheTest, LookupIgnoresResultsMarkedStale) { HostResolverCache cache(kMaxResults, clock_, tick_clock_); const std::string kName1 = "foo.test"; const base::TimeDelta kTtl = base::Minutes(2); const std::vector kEndpoints1 = { IPEndPoint(IPAddress::FromIPLiteral("::1").value(), /*port=*/0)}; auto result1 = std::make_unique( kName1, DnsQueryType::AAAA, tick_clock_.NowTicks() + kTtl, clock_.Now() + kTtl, HostResolverInternalResult::Source::kDns, kEndpoints1, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); const std::string kName2 = "bar.test"; const std::vector kEndpoints2 = { IPEndPoint(IPAddress::FromIPLiteral("2001:DB8::4").value(), /*port=*/0)}; auto result2 = std::make_unique( kName2, DnsQueryType::AAAA, tick_clock_.NowTicks() + kTtl, clock_.Now() + kTtl, HostResolverInternalResult::Source::kDns, kEndpoints2, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); const NetworkAnonymizationKey anonymization_key; cache.Set(std::move(result1), anonymization_key, HostResolverSource::DNS, /*secure=*/false); cache.Set(std::move(result2), anonymization_key, HostResolverSource::DNS, /*secure=*/false); EXPECT_NE(cache.Lookup(kName1, anonymization_key), nullptr); EXPECT_THAT(cache.LookupStale(kName1, anonymization_key), Optional(IsNotStale())); EXPECT_NE(cache.Lookup(kName2, anonymization_key), nullptr); EXPECT_THAT(cache.LookupStale(kName2, anonymization_key), Optional(IsNotStale())); cache.MakeAllResultsStale(); // Expect both entries to now be stale. EXPECT_EQ(cache.Lookup(kName1, anonymization_key), nullptr); EXPECT_THAT(cache.LookupStale(kName1, anonymization_key), Optional(IsStale(std::nullopt, true))); EXPECT_EQ(cache.Lookup(kName2, anonymization_key), nullptr); EXPECT_THAT(cache.LookupStale(kName2, anonymization_key), Optional(IsStale(std::nullopt, true))); const std::string kName3 = "foo3.test"; const std::vector kEndpoints3 = { IPEndPoint(IPAddress::FromIPLiteral("2001:DB8::2").value(), /*port=*/0)}; auto result3 = std::make_unique( kName3, DnsQueryType::AAAA, tick_clock_.NowTicks() + kTtl, clock_.Now() + kTtl, HostResolverInternalResult::Source::kDns, kEndpoints3, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); cache.Set(std::move(result3), anonymization_key, HostResolverSource::DNS, /*secure=*/false); EXPECT_EQ(cache.Lookup(kName1, anonymization_key), nullptr); EXPECT_THAT(cache.LookupStale(kName1, anonymization_key), Optional(IsStale(std::nullopt, true))); EXPECT_EQ(cache.Lookup(kName2, anonymization_key), nullptr); EXPECT_THAT(cache.LookupStale(kName2, anonymization_key), Optional(IsStale(std::nullopt, true))); EXPECT_THAT( cache.Lookup(kName3, anonymization_key), Pointee(ExpectHostResolverInternalDataResult( kName3, DnsQueryType::AAAA, HostResolverInternalResult::Source::kDns, Optional(tick_clock_.NowTicks() + kTtl), Optional(clock_.Now() + kTtl), kEndpoints3))); EXPECT_THAT(cache.LookupStale(kName3, anonymization_key), Optional(IsNotStale())); } TEST_F(HostResolverCacheTest, MarkedStaleResultsCanBeUpdated) { HostResolverCache cache(kMaxResults, clock_, tick_clock_); const std::string kName = "foo.test"; const base::TimeDelta kTtl = base::Minutes(6); const std::vector kEndpoints = { IPEndPoint(IPAddress::FromIPLiteral("::1").value(), /*port=*/0)}; auto result = std::make_unique( kName, DnsQueryType::AAAA, tick_clock_.NowTicks() + kTtl, clock_.Now() + kTtl, HostResolverInternalResult::Source::kDns, kEndpoints, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); const NetworkAnonymizationKey anonymization_key; cache.Set(std::move(result), anonymization_key, HostResolverSource::DNS, /*secure=*/false); cache.MakeAllResultsStale(); EXPECT_EQ(cache.Lookup(kName, anonymization_key), nullptr); EXPECT_THAT(cache.LookupStale(kName, anonymization_key), Optional(IsStale(std::nullopt, true))); auto update_result = std::make_unique( kName, DnsQueryType::AAAA, tick_clock_.NowTicks() + kTtl, clock_.Now() + kTtl, HostResolverInternalResult::Source::kDns, kEndpoints, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); cache.Set(std::move(update_result), anonymization_key, HostResolverSource::DNS, /*secure=*/false); EXPECT_NE(cache.Lookup(kName, anonymization_key), nullptr); EXPECT_THAT(cache.LookupStale(kName, anonymization_key), Optional(IsNotStale())); } TEST_F(HostResolverCacheTest, RespectsNetworkAnonymizationKey) { HostResolverCache cache(kMaxResults, clock_, tick_clock_); const std::string kName = "foo.test"; const base::TimeDelta kTtl = base::Minutes(5); const std::vector kEndpoints1 = { IPEndPoint(IPAddress::FromIPLiteral("2001:DB8::4").value(), /*port=*/0)}; auto result1 = std::make_unique( kName, DnsQueryType::AAAA, tick_clock_.NowTicks() + kTtl, clock_.Now() + kTtl, HostResolverInternalResult::Source::kDns, kEndpoints1, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); const std::vector kEndpoints2 = { IPEndPoint(IPAddress::FromIPLiteral("2001:DB8::10").value(), /*port=*/0)}; auto result2 = std::make_unique( kName, DnsQueryType::AAAA, tick_clock_.NowTicks() + kTtl, clock_.Now() + kTtl, HostResolverInternalResult::Source::kDns, kEndpoints2, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); const SchemefulSite kSite1(GURL("https://site1.test/")); const auto kNetworkAnonymizationKey1 = NetworkAnonymizationKey::CreateSameSite(kSite1); const SchemefulSite kSite2(GURL("https://site2.test/")); const auto kNetworkAnonymizationKey2 = NetworkAnonymizationKey::CreateSameSite(kSite2); cache.Set(std::move(result1), kNetworkAnonymizationKey1, HostResolverSource::DNS, /*secure=*/false); EXPECT_NE(cache.Lookup(kName, kNetworkAnonymizationKey1), nullptr); EXPECT_NE(cache.LookupStale(kName, kNetworkAnonymizationKey1), std::nullopt); EXPECT_EQ(cache.Lookup(kName, kNetworkAnonymizationKey2), nullptr); EXPECT_EQ(cache.LookupStale(kName, kNetworkAnonymizationKey2), std::nullopt); cache.Set(std::move(result2), kNetworkAnonymizationKey2, HostResolverSource::DNS, /*secure=*/false); EXPECT_THAT( cache.Lookup(kName, kNetworkAnonymizationKey1), Pointee(ExpectHostResolverInternalDataResult( kName, DnsQueryType::AAAA, HostResolverInternalResult::Source::kDns, Optional(tick_clock_.NowTicks() + kTtl), Optional(clock_.Now() + kTtl), kEndpoints1))); EXPECT_THAT( cache.LookupStale(kName, kNetworkAnonymizationKey1), Optional(IsNotStale(ExpectHostResolverInternalDataResult( kName, DnsQueryType::AAAA, HostResolverInternalResult::Source::kDns, Optional(tick_clock_.NowTicks() + kTtl), Optional(clock_.Now() + kTtl), kEndpoints1)))); EXPECT_THAT( cache.Lookup(kName, kNetworkAnonymizationKey2), Pointee(ExpectHostResolverInternalDataResult( kName, DnsQueryType::AAAA, HostResolverInternalResult::Source::kDns, Optional(tick_clock_.NowTicks() + kTtl), Optional(clock_.Now() + kTtl), kEndpoints2))); EXPECT_THAT( cache.LookupStale(kName, kNetworkAnonymizationKey2), Optional(IsNotStale(ExpectHostResolverInternalDataResult( kName, DnsQueryType::AAAA, HostResolverInternalResult::Source::kDns, Optional(tick_clock_.NowTicks() + kTtl), Optional(clock_.Now() + kTtl), kEndpoints2)))); } // Newly added entries are always considered to be the most up-to-date // information, so if an unexpired entry is updated with an expired entry, the // entry should now be expired. TEST_F(HostResolverCacheTest, UpdateToStale) { HostResolverCache cache(kMaxResults, clock_, tick_clock_); const std::string kName = "foo.test"; const std::vector kEndpoints = { IPEndPoint(IPAddress::FromIPLiteral("::1").value(), /*port=*/0)}; auto result = std::make_unique( kName, DnsQueryType::AAAA, tick_clock_.NowTicks() + base::Hours(2), clock_.Now() + base::Hours(2), HostResolverInternalResult::Source::kDns, kEndpoints, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); const NetworkAnonymizationKey anonymization_key; cache.Set(std::move(result), anonymization_key, HostResolverSource::DNS, /*secure=*/false); // Expect initial entry to be unexpired. EXPECT_NE(cache.Lookup(kName, anonymization_key), nullptr); EXPECT_THAT(cache.LookupStale(kName, anonymization_key), Optional(IsNotStale())); auto update_result = std::make_unique( kName, DnsQueryType::AAAA, tick_clock_.NowTicks() - base::Seconds(1), clock_.Now() - base::Seconds(1), HostResolverInternalResult::Source::kDns, kEndpoints, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); cache.Set(std::move(update_result), anonymization_key, HostResolverSource::DNS, /*secure=*/false); // Expect entry to be expired. EXPECT_EQ(cache.Lookup(kName, anonymization_key), nullptr); EXPECT_THAT( cache.LookupStale(kName, anonymization_key), Optional(IsStale(Optional(TimeDeltaIsApproximately(base::Seconds(1))), false))); } // If a wildcard lookup matches multiple result entries, all insecure, expect // lookup to return the most recently set result. TEST_F(HostResolverCacheTest, PreferMoreRecentInsecureResult) { HostResolverCache cache(kMaxResults, clock_, tick_clock_); const std::string kName = "foo.test"; const base::TimeDelta kTtl = base::Minutes(2); const std::vector kNewEndpoints = { IPEndPoint(IPAddress::FromIPLiteral("2001:DB8::8").value(), /*port=*/0)}; auto new_result = std::make_unique( kName, DnsQueryType::AAAA, tick_clock_.NowTicks() + kTtl, clock_.Now() + kTtl, HostResolverInternalResult::Source::kDns, kNewEndpoints, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); const std::vector kOldEndpoints = { IPEndPoint(IPAddress::FromIPLiteral("2001:DB8::7").value(), /*port=*/0)}; auto old_result = std::make_unique( kName, DnsQueryType::AAAA, tick_clock_.NowTicks() + kTtl, clock_.Now() + kTtl, HostResolverInternalResult::Source::kDns, kOldEndpoints, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); const NetworkAnonymizationKey anonymization_key; cache.Set(std::move(old_result), anonymization_key, HostResolverSource::SYSTEM, /*secure=*/false); cache.Set(std::move(new_result), anonymization_key, HostResolverSource::DNS, /*secure=*/false); EXPECT_THAT( cache.Lookup(kName, anonymization_key, DnsQueryType::AAAA, HostResolverSource::ANY, /*secure=*/false), Pointee(ExpectHostResolverInternalDataResult( kName, DnsQueryType::AAAA, HostResolverInternalResult::Source::kDns, Optional(tick_clock_.NowTicks() + kTtl), Optional(clock_.Now() + kTtl), kNewEndpoints))); // Other result still available for more specific lookups. EXPECT_THAT( cache.Lookup(kName, anonymization_key, DnsQueryType::AAAA, HostResolverSource::SYSTEM, /*secure=*/false), Pointee(ExpectHostResolverInternalDataResult( kName, DnsQueryType::AAAA, HostResolverInternalResult::Source::kDns, Optional(tick_clock_.NowTicks() + kTtl), Optional(clock_.Now() + kTtl), kOldEndpoints))); } // If a wildcard lookup matches multiple result entries, all secure, expect // lookup to return the most recently set result. TEST_F(HostResolverCacheTest, PreferMoreRecentSecureResult) { HostResolverCache cache(kMaxResults, clock_, tick_clock_); const std::string kName = "foo.test"; const base::TimeDelta kTtl = base::Minutes(2); const std::vector kNewEndpoints = { IPEndPoint(IPAddress::FromIPLiteral("2001:DB8::8").value(), /*port=*/0)}; auto new_result = std::make_unique( kName, DnsQueryType::AAAA, tick_clock_.NowTicks() + kTtl, clock_.Now() + kTtl, HostResolverInternalResult::Source::kDns, kNewEndpoints, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); const std::vector kOldEndpoints = { IPEndPoint(IPAddress::FromIPLiteral("2001:DB8::7").value(), /*port=*/0)}; auto old_result = std::make_unique( kName, DnsQueryType::AAAA, tick_clock_.NowTicks() + kTtl, clock_.Now() + kTtl, HostResolverInternalResult::Source::kDns, kOldEndpoints, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); const NetworkAnonymizationKey anonymization_key; cache.Set(std::move(old_result), anonymization_key, HostResolverSource::SYSTEM, /*secure=*/true); cache.Set(std::move(new_result), anonymization_key, HostResolverSource::DNS, /*secure=*/true); EXPECT_THAT( cache.Lookup(kName, anonymization_key, DnsQueryType::AAAA, HostResolverSource::ANY, /*secure=*/true), Pointee(ExpectHostResolverInternalDataResult( kName, DnsQueryType::AAAA, HostResolverInternalResult::Source::kDns, Optional(tick_clock_.NowTicks() + kTtl), Optional(clock_.Now() + kTtl), kNewEndpoints))); // Other result still available for more specific lookups. EXPECT_THAT( cache.Lookup(kName, anonymization_key, DnsQueryType::AAAA, HostResolverSource::SYSTEM, /*secure=*/true), Pointee(ExpectHostResolverInternalDataResult( kName, DnsQueryType::AAAA, HostResolverInternalResult::Source::kDns, Optional(tick_clock_.NowTicks() + kTtl), Optional(clock_.Now() + kTtl), kOldEndpoints))); } // If a wildcard lookup matches multiple result entries of mixed secureness, // expect lookup to return the most recently set secure result. TEST_F(HostResolverCacheTest, PreferMoreSecureResult) { HostResolverCache cache(kMaxResults, clock_, tick_clock_); const std::string kName = "foo.test"; const base::TimeDelta kTtl = base::Minutes(2); const std::vector kInsecureEndpoints = { IPEndPoint(IPAddress::FromIPLiteral("2001:DB8::4").value(), /*port=*/0)}; auto insecure_result = std::make_unique( kName, DnsQueryType::AAAA, tick_clock_.NowTicks() + kTtl, clock_.Now() + kTtl, HostResolverInternalResult::Source::kDns, kInsecureEndpoints, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); const std::vector kSecureEndpoints = { IPEndPoint(IPAddress::FromIPLiteral("2001:DB8::8").value(), /*port=*/0)}; auto secure_result = std::make_unique( kName, DnsQueryType::AAAA, tick_clock_.NowTicks() + kTtl, clock_.Now() + kTtl, HostResolverInternalResult::Source::kDns, kSecureEndpoints, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); const std::vector kOldSecureEndpoints = { IPEndPoint(IPAddress::FromIPLiteral("2001:DB8::7").value(), /*port=*/0)}; auto old_secure_result = std::make_unique( kName, DnsQueryType::AAAA, tick_clock_.NowTicks() + kTtl, clock_.Now() + kTtl, HostResolverInternalResult::Source::kDns, kOldSecureEndpoints, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); const NetworkAnonymizationKey anonymization_key; // Add in the secure results first to ensure they're not being selected by // being the most recently added result. cache.Set(std::move(old_secure_result), anonymization_key, HostResolverSource::SYSTEM, /*secure=*/true); cache.Set(std::move(secure_result), anonymization_key, HostResolverSource::DNS, /*secure=*/true); cache.Set(std::move(insecure_result), anonymization_key, HostResolverSource::DNS, /*secure=*/false); EXPECT_THAT( cache.Lookup(kName, anonymization_key, DnsQueryType::AAAA, HostResolverSource::ANY, /*secure=*/std::nullopt), Pointee(ExpectHostResolverInternalDataResult( kName, DnsQueryType::AAAA, HostResolverInternalResult::Source::kDns, Optional(tick_clock_.NowTicks() + kTtl), Optional(clock_.Now() + kTtl), kSecureEndpoints))); // Other results still available for more specific lookups. EXPECT_THAT( cache.Lookup(kName, anonymization_key, DnsQueryType::AAAA, HostResolverSource::ANY, /*secure=*/false), Pointee(ExpectHostResolverInternalDataResult( kName, DnsQueryType::AAAA, HostResolverInternalResult::Source::kDns, Optional(tick_clock_.NowTicks() + kTtl), Optional(clock_.Now() + kTtl), kInsecureEndpoints))); EXPECT_THAT( cache.Lookup(kName, anonymization_key, DnsQueryType::AAAA, HostResolverSource::SYSTEM, /*secure=*/std::nullopt), Pointee(ExpectHostResolverInternalDataResult( kName, DnsQueryType::AAAA, HostResolverInternalResult::Source::kDns, Optional(tick_clock_.NowTicks() + kTtl), Optional(clock_.Now() + kTtl), kOldSecureEndpoints))); } // Even though LookupStale() can return stale results, if a wildcard lookup // matches multiple result entries, expect the lookup to prefer a non-stale // result. TEST_F(HostResolverCacheTest, LookupStalePrefersNonStaleResult) { HostResolverCache cache(kMaxResults, clock_, tick_clock_); const std::string kName = "foo.test"; const std::vector kStaleEndpoints = { IPEndPoint(IPAddress::FromIPLiteral("2001:DB8::7").value(), /*port=*/0)}; auto stale_result = std::make_unique( kName, DnsQueryType::AAAA, tick_clock_.NowTicks() - base::Seconds(4), clock_.Now() - base::Seconds(4), HostResolverInternalResult::Source::kDns, kStaleEndpoints, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); const std::vector kActiveEndpoints = { IPEndPoint(IPAddress::FromIPLiteral("2001:DB8::8").value(), /*port=*/0)}; auto active_result = std::make_unique( kName, DnsQueryType::AAAA, tick_clock_.NowTicks() + base::Minutes(3), clock_.Now() + base::Minutes(3), HostResolverInternalResult::Source::kDns, kActiveEndpoints, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); const NetworkAnonymizationKey anonymization_key; cache.Set(std::move(active_result), anonymization_key, HostResolverSource::DNS, /*secure=*/false); cache.Set(std::move(stale_result), anonymization_key, HostResolverSource::SYSTEM, /*secure=*/true); EXPECT_THAT( cache.LookupStale(kName, anonymization_key, DnsQueryType::AAAA, HostResolverSource::ANY, /*secure=*/std::nullopt), Optional(IsNotStale(ExpectHostResolverInternalDataResult( kName, DnsQueryType::AAAA, HostResolverInternalResult::Source::kDns, Optional(tick_clock_.NowTicks() + base::Minutes(3)), Optional(clock_.Now() + base::Minutes(3)), kActiveEndpoints)))); // Other result still available for more specific lookups. EXPECT_THAT( cache.LookupStale(kName, anonymization_key, DnsQueryType::AAAA, HostResolverSource::SYSTEM, /*secure=*/std::nullopt), Optional(IsStale( ExpectHostResolverInternalDataResult( kName, DnsQueryType::AAAA, HostResolverInternalResult::Source::kDns, Optional(tick_clock_.NowTicks() - base::Seconds(4)), Optional(clock_.Now() - base::Seconds(4)), kStaleEndpoints), Ne(std::nullopt), false))); } // Same as LookupStalePrefersNonStaleResult except lookup criteria specifies // insecure. Expect same general behavior (prefers non-stale result) but // exercises slightly different logic because, if no secure results exist, no // other results need to be considered once a non-stale result is found TEST_F(HostResolverCacheTest, InsecureLookupStalePrefersNonStaleResult) { HostResolverCache cache(kMaxResults, clock_, tick_clock_); const std::string kName = "foo.test"; const std::vector kStaleEndpoints = { IPEndPoint(IPAddress::FromIPLiteral("2001:DB8::7").value(), /*port=*/0)}; auto stale_result = std::make_unique( kName, DnsQueryType::AAAA, tick_clock_.NowTicks() - base::Seconds(4), clock_.Now() - base::Seconds(4), HostResolverInternalResult::Source::kDns, kStaleEndpoints, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); const std::vector kActiveEndpoints = { IPEndPoint(IPAddress::FromIPLiteral("2001:DB8::8").value(), /*port=*/0)}; auto active_result = std::make_unique( kName, DnsQueryType::AAAA, tick_clock_.NowTicks() + base::Minutes(3), clock_.Now() + base::Minutes(3), HostResolverInternalResult::Source::kDns, kActiveEndpoints, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); const NetworkAnonymizationKey anonymization_key; cache.Set(std::move(stale_result), anonymization_key, HostResolverSource::DNS, /*secure=*/false); cache.Set(std::move(active_result), anonymization_key, HostResolverSource::SYSTEM, /*secure=*/false); EXPECT_THAT( cache.LookupStale(kName, anonymization_key, DnsQueryType::AAAA, HostResolverSource::ANY, /*secure=*/false), Optional(IsNotStale(ExpectHostResolverInternalDataResult( kName, DnsQueryType::AAAA, HostResolverInternalResult::Source::kDns, Optional(tick_clock_.NowTicks() + base::Minutes(3)), Optional(clock_.Now() + base::Minutes(3)), kActiveEndpoints)))); } TEST_F(HostResolverCacheTest, LookupStalePrefersLeastStaleByGeneration) { HostResolverCache cache(kMaxResults, clock_, tick_clock_); const std::string kName = "foo.test"; const std::vector kMoreStaleEndpoints = { IPEndPoint(IPAddress::FromIPLiteral("2001:DB8::7").value(), /*port=*/0)}; auto more_stale_result = std::make_unique( kName, DnsQueryType::AAAA, tick_clock_.NowTicks() + base::Seconds(4), clock_.Now() + base::Seconds(4), HostResolverInternalResult::Source::kDns, kMoreStaleEndpoints, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); const std::vector kLessStaleEndpoints = { IPEndPoint(IPAddress::FromIPLiteral("2001:DB8::8").value(), /*port=*/0)}; auto less_stale_result = std::make_unique( kName, DnsQueryType::AAAA, tick_clock_.NowTicks() - base::Minutes(3), clock_.Now() - base::Minutes(3), HostResolverInternalResult::Source::kDns, kLessStaleEndpoints, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); const NetworkAnonymizationKey anonymization_key; cache.Set(std::move(more_stale_result), anonymization_key, HostResolverSource::DNS, /*secure=*/true); cache.MakeAllResultsStale(); cache.Set(std::move(less_stale_result), anonymization_key, HostResolverSource::SYSTEM, /*secure=*/false); EXPECT_THAT( cache.LookupStale(kName, anonymization_key, DnsQueryType::AAAA, HostResolverSource::ANY, /*secure=*/std::nullopt), Optional(IsStale( ExpectHostResolverInternalDataResult( kName, DnsQueryType::AAAA, HostResolverInternalResult::Source::kDns, Optional(tick_clock_.NowTicks() - base::Minutes(3)), Optional(clock_.Now() - base::Minutes(3)), kLessStaleEndpoints), Ne(std::nullopt), false))); // Other result still available for more specific lookups. EXPECT_THAT( cache.LookupStale(kName, anonymization_key, DnsQueryType::AAAA, HostResolverSource::DNS, /*secure=*/std::nullopt), Optional(IsStale( ExpectHostResolverInternalDataResult( kName, DnsQueryType::AAAA, HostResolverInternalResult::Source::kDns, Optional(tick_clock_.NowTicks() + base::Seconds(4)), Optional(clock_.Now() + base::Seconds(4)), kMoreStaleEndpoints), std::nullopt, true))); } TEST_F(HostResolverCacheTest, LookupStalePrefersLeastStaleByExpiration) { HostResolverCache cache(kMaxResults, clock_, tick_clock_); const std::string kName = "foo.test"; const std::vector kLessStaleEndpoints = { IPEndPoint(IPAddress::FromIPLiteral("2001:DB8::8").value(), /*port=*/0)}; auto less_stale_result = std::make_unique( kName, DnsQueryType::AAAA, tick_clock_.NowTicks() - base::Minutes(3), clock_.Now() - base::Minutes(3), HostResolverInternalResult::Source::kDns, kLessStaleEndpoints, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); const std::vector kMoreStaleEndpoints = { IPEndPoint(IPAddress::FromIPLiteral("2001:DB8::7").value(), /*port=*/0)}; auto more_stale_result = std::make_unique( kName, DnsQueryType::AAAA, tick_clock_.NowTicks() - base::Hours(1), clock_.Now() - base::Hours(1), HostResolverInternalResult::Source::kDns, kMoreStaleEndpoints, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); const NetworkAnonymizationKey anonymization_key; cache.Set(std::move(less_stale_result), anonymization_key, HostResolverSource::SYSTEM, /*secure=*/false); cache.Set(std::move(more_stale_result), anonymization_key, HostResolverSource::DNS, /*secure=*/true); EXPECT_THAT( cache.LookupStale(kName, anonymization_key, DnsQueryType::AAAA, HostResolverSource::ANY, /*secure=*/std::nullopt), Optional(IsStale( ExpectHostResolverInternalDataResult( kName, DnsQueryType::AAAA, HostResolverInternalResult::Source::kDns, Ne(std::nullopt), Ne(std::nullopt), kLessStaleEndpoints), Optional(TimeDeltaIsApproximately(base::Minutes(3))), false))); // Other result still available for more specific lookups. EXPECT_THAT( cache.LookupStale(kName, anonymization_key, DnsQueryType::AAAA, HostResolverSource::DNS, /*secure=*/std::nullopt), Optional( IsStale(ExpectHostResolverInternalDataResult( kName, DnsQueryType::AAAA, HostResolverInternalResult::Source::kDns, Ne(std::nullopt), Ne(std::nullopt), kMoreStaleEndpoints), Optional(TimeDeltaIsApproximately(base::Hours(1))), false))); } TEST_F(HostResolverCacheTest, LookupStalePrefersMostSecure) { HostResolverCache cache(kMaxResults, clock_, tick_clock_); const std::string kName = "foo.test"; const std::vector kSecureEndpoints = { IPEndPoint(IPAddress::FromIPLiteral("2001:DB8::8").value(), /*port=*/0)}; auto secure_result = std::make_unique( kName, DnsQueryType::AAAA, tick_clock_.NowTicks() - base::Minutes(3), clock_.Now() - base::Minutes(3), HostResolverInternalResult::Source::kDns, kSecureEndpoints, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); const std::vector kInsecureEndpoints = { IPEndPoint(IPAddress::FromIPLiteral("2001:DB8::7").value(), /*port=*/0)}; auto insecure_result = std::make_unique( kName, DnsQueryType::AAAA, tick_clock_.NowTicks() - base::Minutes(3), clock_.Now() - base::Minutes(3), HostResolverInternalResult::Source::kDns, kInsecureEndpoints, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); const NetworkAnonymizationKey anonymization_key; cache.Set(std::move(secure_result), anonymization_key, HostResolverSource::SYSTEM, /*secure=*/true); cache.Set(std::move(insecure_result), anonymization_key, HostResolverSource::DNS, /*secure=*/false); EXPECT_THAT( cache.LookupStale(kName, anonymization_key, DnsQueryType::AAAA, HostResolverSource::ANY, /*secure=*/std::nullopt), Optional( IsStale(ExpectHostResolverInternalDataResult( kName, DnsQueryType::AAAA, HostResolverInternalResult::Source::kDns, Ne(std::nullopt), Ne(std::nullopt), kSecureEndpoints), Ne(std::nullopt), false))); // Other result still available for more specific lookups. EXPECT_THAT( cache.LookupStale(kName, anonymization_key, DnsQueryType::AAAA, HostResolverSource::DNS, /*secure=*/std::nullopt), Optional( IsStale(ExpectHostResolverInternalDataResult( kName, DnsQueryType::AAAA, HostResolverInternalResult::Source::kDns, Ne(std::nullopt), Ne(std::nullopt), kInsecureEndpoints), Ne(std::nullopt), false))); } // Same as LookupStalePrefersMostSecure except results are not stale. Expect // same general behavior (secure result preferred) but exercises slightly // different logic because no other results need to be considered once a // non-stale secure result is found. TEST_F(HostResolverCacheTest, LookupStalePrefersMostSecureNonStale) { HostResolverCache cache(kMaxResults, clock_, tick_clock_); const std::string kName = "foo.test"; const std::vector kInsecureEndpoints = { IPEndPoint(IPAddress::FromIPLiteral("2001:DB8::7").value(), /*port=*/0)}; auto insecure_result = std::make_unique( kName, DnsQueryType::AAAA, tick_clock_.NowTicks() + base::Minutes(3), clock_.Now() + base::Minutes(3), HostResolverInternalResult::Source::kDns, kInsecureEndpoints, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); const std::vector kSecureEndpoints = { IPEndPoint(IPAddress::FromIPLiteral("2001:DB8::8").value(), /*port=*/0)}; auto secure_result = std::make_unique( kName, DnsQueryType::AAAA, tick_clock_.NowTicks() + base::Minutes(3), clock_.Now() + base::Minutes(3), HostResolverInternalResult::Source::kDns, kSecureEndpoints, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); const NetworkAnonymizationKey anonymization_key; cache.Set(std::move(insecure_result), anonymization_key, HostResolverSource::DNS, /*secure=*/false); cache.Set(std::move(secure_result), anonymization_key, HostResolverSource::SYSTEM, /*secure=*/true); EXPECT_THAT( cache.LookupStale(kName, anonymization_key, DnsQueryType::AAAA, HostResolverSource::ANY, /*secure=*/std::nullopt), Optional(IsNotStale(ExpectHostResolverInternalDataResult( kName, DnsQueryType::AAAA, HostResolverInternalResult::Source::kDns, Ne(std::nullopt), Ne(std::nullopt), kSecureEndpoints)))); } TEST_F(HostResolverCacheTest, LookupStalePrefersMoreRecent) { HostResolverCache cache(kMaxResults, clock_, tick_clock_); const std::string kName = "foo.test"; const std::vector kOldEndpoints = { IPEndPoint(IPAddress::FromIPLiteral("2001:DB8::8").value(), /*port=*/0)}; auto old_result = std::make_unique( kName, DnsQueryType::AAAA, tick_clock_.NowTicks() - base::Minutes(3), clock_.Now() - base::Minutes(3), HostResolverInternalResult::Source::kDns, kOldEndpoints, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); const std::vector kNewEndpoints = { IPEndPoint(IPAddress::FromIPLiteral("2001:DB8::7").value(), /*port=*/0)}; auto new_result = std::make_unique( kName, DnsQueryType::AAAA, tick_clock_.NowTicks() - base::Minutes(3), clock_.Now() - base::Minutes(3), HostResolverInternalResult::Source::kDns, kNewEndpoints, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); const NetworkAnonymizationKey anonymization_key; cache.Set(std::move(old_result), anonymization_key, HostResolverSource::SYSTEM, /*secure=*/false); cache.Set(std::move(new_result), anonymization_key, HostResolverSource::DNS, /*secure=*/false); EXPECT_THAT( cache.LookupStale(kName, anonymization_key, DnsQueryType::AAAA, HostResolverSource::ANY, /*secure=*/std::nullopt), Optional(IsStale(ExpectHostResolverInternalDataResult( kName, DnsQueryType::AAAA, HostResolverInternalResult::Source::kDns, Ne(std::nullopt), Ne(std::nullopt), kNewEndpoints), Ne(std::nullopt), false))); // Other result still available for more specific lookups. EXPECT_THAT( cache.LookupStale(kName, anonymization_key, DnsQueryType::AAAA, HostResolverSource::SYSTEM, /*secure=*/std::nullopt), Optional(IsStale(ExpectHostResolverInternalDataResult( kName, DnsQueryType::AAAA, HostResolverInternalResult::Source::kDns, Ne(std::nullopt), Ne(std::nullopt), kOldEndpoints), Ne(std::nullopt), false))); } TEST_F(HostResolverCacheTest, EvictStaleResults) { HostResolverCache cache(/*max_results=*/2, clock_, tick_clock_); const std::string kName1 = "foo1.test"; const std::vector kEndpoints1 = { IPEndPoint(IPAddress::FromIPLiteral("::1").value(), /*port=*/0)}; auto result1 = std::make_unique( kName1, DnsQueryType::AAAA, tick_clock_.NowTicks() + base::Minutes(11), clock_.Now() + base::Minutes(11), HostResolverInternalResult::Source::kDns, kEndpoints1, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); const NetworkAnonymizationKey anonymization_key; cache.Set(std::move(result1), anonymization_key, HostResolverSource::DNS, /*secure=*/false); cache.MakeAllResultsStale(); const std::string kName2 = "foo2.test"; const std::vector kEndpoints2 = { IPEndPoint(IPAddress::FromIPLiteral("2001:DB8::4").value(), /*port=*/0)}; auto result2 = std::make_unique( kName2, DnsQueryType::AAAA, tick_clock_.NowTicks() - base::Minutes(4), clock_.Now() - base::Minutes(4), HostResolverInternalResult::Source::kDns, kEndpoints2, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); cache.Set(std::move(result2), anonymization_key, HostResolverSource::DNS, /*secure=*/false); // Expect `result1` to be stale via generation and `result2` to be stale via // expiration. EXPECT_THAT(cache.LookupStale(kName1, anonymization_key), Optional(IsStale(std::nullopt, true))); EXPECT_THAT(cache.LookupStale(kName2, anonymization_key), Optional(IsStale(Ne(std::nullopt), false))); const std::string kName3 = "foo3.test"; const std::vector kEndpoints3 = { IPEndPoint(IPAddress::FromIPLiteral("2001:DB8::5").value(), /*port=*/0)}; auto result3 = std::make_unique( kName3, DnsQueryType::AAAA, tick_clock_.NowTicks() + base::Minutes(8), clock_.Now() + base::Minutes(8), HostResolverInternalResult::Source::kDns, kEndpoints3, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); cache.Set(std::move(result3), anonymization_key, HostResolverSource::DNS, /*secure=*/false); // Expect `result1` and `result2` to be evicted and `result3` to still be // active. EXPECT_EQ(cache.LookupStale(kName1, anonymization_key), std::nullopt); EXPECT_EQ(cache.LookupStale(kName2, anonymization_key), std::nullopt); EXPECT_NE(cache.Lookup(kName3, anonymization_key), nullptr); } TEST_F(HostResolverCacheTest, EvictSoonestToExpireResult) { HostResolverCache cache(/*max_results=*/2, clock_, tick_clock_); const std::string kName1 = "foo1.test"; const std::vector kEndpoints1 = { IPEndPoint(IPAddress::FromIPLiteral("::1").value(), /*port=*/0)}; auto result1 = std::make_unique( kName1, DnsQueryType::AAAA, tick_clock_.NowTicks() + base::Minutes(11), clock_.Now() + base::Minutes(11), HostResolverInternalResult::Source::kDns, kEndpoints1, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); const NetworkAnonymizationKey anonymization_key; cache.Set(std::move(result1), anonymization_key, HostResolverSource::DNS, /*secure=*/false); const std::string kName2 = "foo2.test"; const std::vector kEndpoints2 = { IPEndPoint(IPAddress::FromIPLiteral("2001:DB8::4").value(), /*port=*/0)}; auto result2 = std::make_unique( kName2, DnsQueryType::AAAA, tick_clock_.NowTicks() + base::Minutes(4), clock_.Now() + base::Minutes(4), HostResolverInternalResult::Source::kDns, kEndpoints2, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); cache.Set(std::move(result2), anonymization_key, HostResolverSource::DNS, /*secure=*/false); // Expect both results to be active. EXPECT_NE(cache.Lookup(kName1, anonymization_key), nullptr); EXPECT_NE(cache.Lookup(kName2, anonymization_key), nullptr); const std::string kName3 = "foo3.test"; const std::vector kEndpoints3 = { IPEndPoint(IPAddress::FromIPLiteral("2001:DB8::5").value(), /*port=*/0)}; auto result3 = std::make_unique( kName3, DnsQueryType::AAAA, tick_clock_.NowTicks() + base::Minutes(8), clock_.Now() + base::Minutes(8), HostResolverInternalResult::Source::kDns, kEndpoints3, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); cache.Set(std::move(result3), anonymization_key, HostResolverSource::DNS, /*secure=*/false); // Expect `result2` to be evicted because it expires soonest. EXPECT_NE(cache.Lookup(kName1, anonymization_key), nullptr); EXPECT_EQ(cache.LookupStale(kName2, anonymization_key), std::nullopt); EXPECT_NE(cache.Lookup(kName3, anonymization_key), nullptr); } // If multiple results are equally soon-to-expire, expect least secure option to // be evicted. TEST_F(HostResolverCacheTest, EvictLeastSecureResult) { HostResolverCache cache(/*max_results=*/2, clock_, tick_clock_); const std::string kName1 = "foo1.test"; const base::TimeDelta kTtl = base::Minutes(2); const std::vector kEndpoints1 = { IPEndPoint(IPAddress::FromIPLiteral("::1").value(), /*port=*/0)}; auto result1 = std::make_unique( kName1, DnsQueryType::AAAA, tick_clock_.NowTicks() + kTtl, clock_.Now() + kTtl, HostResolverInternalResult::Source::kDns, kEndpoints1, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); const NetworkAnonymizationKey anonymization_key; cache.Set(std::move(result1), anonymization_key, HostResolverSource::DNS, /*secure=*/true); const std::string kName2 = "foo2.test"; const std::vector kEndpoints2 = { IPEndPoint(IPAddress::FromIPLiteral("2001:DB8::4").value(), /*port=*/0)}; auto result2 = std::make_unique( kName2, DnsQueryType::AAAA, tick_clock_.NowTicks() + kTtl, clock_.Now() + kTtl, HostResolverInternalResult::Source::kDns, kEndpoints2, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); cache.Set(std::move(result2), anonymization_key, HostResolverSource::DNS, /*secure=*/false); // Expect both results to be active. EXPECT_NE(cache.Lookup(kName1, anonymization_key), nullptr); EXPECT_NE(cache.Lookup(kName2, anonymization_key), nullptr); const std::string kName3 = "foo3.test"; const std::vector kEndpoints3 = { IPEndPoint(IPAddress::FromIPLiteral("2001:DB8::5").value(), /*port=*/0)}; auto result3 = std::make_unique( kName3, DnsQueryType::AAAA, tick_clock_.NowTicks() + base::Minutes(8), clock_.Now() + base::Minutes(8), HostResolverInternalResult::Source::kDns, kEndpoints3, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); cache.Set(std::move(result3), anonymization_key, HostResolverSource::DNS, /*secure=*/false); // Expect `result2` to be evicted because, while it will expire at the same // time as `result1`, it is less secure. EXPECT_NE(cache.Lookup(kName1, anonymization_key), nullptr); EXPECT_EQ(cache.LookupStale(kName2, anonymization_key), std::nullopt); EXPECT_NE(cache.Lookup(kName3, anonymization_key), nullptr); } // If multiple results are equally soon-to-expire and equally (in)secure, expect // oldest option to be evicted. TEST_F(HostResolverCacheTest, EvictOldestResult) { HostResolverCache cache(/*max_results=*/2, clock_, tick_clock_); const std::string kName1 = "foo1.test"; const base::TimeDelta kTtl = base::Minutes(2); const std::vector kEndpoints1 = { IPEndPoint(IPAddress::FromIPLiteral("::1").value(), /*port=*/0)}; auto result1 = std::make_unique( kName1, DnsQueryType::AAAA, tick_clock_.NowTicks() + kTtl, clock_.Now() + kTtl, HostResolverInternalResult::Source::kDns, kEndpoints1, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); const NetworkAnonymizationKey anonymization_key; cache.Set(std::move(result1), anonymization_key, HostResolverSource::DNS, /*secure=*/false); const std::string kName2 = "foo2.test"; const std::vector kEndpoints2 = { IPEndPoint(IPAddress::FromIPLiteral("2001:DB8::4").value(), /*port=*/0)}; auto result2 = std::make_unique( kName2, DnsQueryType::AAAA, tick_clock_.NowTicks() + kTtl, clock_.Now() + kTtl, HostResolverInternalResult::Source::kDns, kEndpoints2, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); cache.Set(std::move(result2), anonymization_key, HostResolverSource::DNS, /*secure=*/false); // Expect both results to be active. EXPECT_NE(cache.Lookup(kName1, anonymization_key), nullptr); EXPECT_NE(cache.Lookup(kName2, anonymization_key), nullptr); const std::string kName3 = "foo3.test"; const std::vector kEndpoints3 = { IPEndPoint(IPAddress::FromIPLiteral("2001:DB8::5").value(), /*port=*/0)}; auto result3 = std::make_unique( kName3, DnsQueryType::AAAA, tick_clock_.NowTicks() + base::Minutes(8), clock_.Now() + base::Minutes(8), HostResolverInternalResult::Source::kDns, kEndpoints3, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); cache.Set(std::move(result3), anonymization_key, HostResolverSource::DNS, /*secure=*/false); // Expect `result1` to be evicted because, while it will expire at the same // time as `result2` and both are insecure, it is older. EXPECT_EQ(cache.LookupStale(kName1, anonymization_key), std::nullopt); EXPECT_NE(cache.Lookup(kName2, anonymization_key), nullptr); EXPECT_NE(cache.Lookup(kName3, anonymization_key), nullptr); } // Even newly-added results that trigger eviction are themselves eligible for // eviction if best candidate. TEST_F(HostResolverCacheTest, EvictLatestResult) { HostResolverCache cache(/*max_results=*/2, clock_, tick_clock_); const std::string kName1 = "foo1.test"; const base::TimeDelta kTtl = base::Minutes(2); const std::vector kEndpoints1 = { IPEndPoint(IPAddress::FromIPLiteral("::1").value(), /*port=*/0)}; auto result1 = std::make_unique( kName1, DnsQueryType::AAAA, tick_clock_.NowTicks() + kTtl, clock_.Now() + kTtl, HostResolverInternalResult::Source::kDns, kEndpoints1, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); const NetworkAnonymizationKey anonymization_key; cache.Set(std::move(result1), anonymization_key, HostResolverSource::DNS, /*secure=*/false); const std::string kName2 = "foo2.test"; const std::vector kEndpoints2 = { IPEndPoint(IPAddress::FromIPLiteral("2001:DB8::4").value(), /*port=*/0)}; auto result2 = std::make_unique( kName2, DnsQueryType::AAAA, tick_clock_.NowTicks() + kTtl, clock_.Now() + kTtl, HostResolverInternalResult::Source::kDns, kEndpoints2, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); cache.Set(std::move(result2), anonymization_key, HostResolverSource::DNS, /*secure=*/false); // Expect both results to be active. EXPECT_NE(cache.Lookup(kName1, anonymization_key), nullptr); EXPECT_NE(cache.Lookup(kName2, anonymization_key), nullptr); const std::string kName3 = "foo3.test"; const std::vector kEndpoints3 = { IPEndPoint(IPAddress::FromIPLiteral("2001:DB8::5").value(), /*port=*/0)}; auto result3 = std::make_unique( kName3, DnsQueryType::AAAA, tick_clock_.NowTicks() + base::Minutes(1), clock_.Now() + base::Minutes(8), HostResolverInternalResult::Source::kDns, kEndpoints3, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); cache.Set(std::move(result3), anonymization_key, HostResolverSource::DNS, /*secure=*/false); // Expect `result3` to be evicted because it is soonest to expire. EXPECT_NE(cache.Lookup(kName1, anonymization_key), nullptr); EXPECT_NE(cache.Lookup(kName2, anonymization_key), nullptr); EXPECT_EQ(cache.LookupStale(kName3, anonymization_key), std::nullopt); } TEST_F(HostResolverCacheTest, SerializeAndDeserialize) { HostResolverCache cache(kMaxResults, clock_, tick_clock_); const std::string kName = "foo.test"; const std::vector kEndpoints = { IPEndPoint(IPAddress::FromIPLiteral("::1").value(), /*port=*/0)}; const base::Time kExpiration = clock_.Now() + base::Hours(2); auto result = std::make_unique( kName, DnsQueryType::AAAA, tick_clock_.NowTicks() + base::Hours(2), kExpiration, HostResolverInternalResult::Source::kDns, kEndpoints, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); const NetworkAnonymizationKey anonymization_key; cache.Set(std::move(result), anonymization_key, HostResolverSource::DNS, /*secure=*/false); base::Value value = cache.Serialize(); EXPECT_EQ(value.GetList().size(), 1u); HostResolverCache restored_cache(kMaxResults, clock_, tick_clock_); EXPECT_TRUE(restored_cache.RestoreFromValue(value)); // Expect restored result to be stale by generation. EXPECT_THAT( restored_cache.LookupStale(kName, anonymization_key), Optional(IsStale(ExpectHostResolverInternalDataResult( kName, DnsQueryType::AAAA, HostResolverInternalResult::Source::kDns, Eq(std::nullopt), Optional(kExpiration), kEndpoints), std::nullopt, true))); } TEST_F(HostResolverCacheTest, TransientAnonymizationKeyNotSerialized) { HostResolverCache cache(kMaxResults, clock_, tick_clock_); const std::string kName = "foo.test"; const std::vector kEndpoints = { IPEndPoint(IPAddress::FromIPLiteral("::1").value(), /*port=*/0)}; const base::Time kExpiration = clock_.Now() + base::Hours(2); auto result = std::make_unique( kName, DnsQueryType::AAAA, tick_clock_.NowTicks() + base::Hours(2), kExpiration, HostResolverInternalResult::Source::kDns, kEndpoints, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); const auto anonymization_key = NetworkAnonymizationKey::CreateTransient(); cache.Set(std::move(result), anonymization_key, HostResolverSource::DNS, /*secure=*/false); base::Value value = cache.Serialize(); EXPECT_TRUE(value.GetList().empty()); } TEST_F(HostResolverCacheTest, DeserializePrefersExistingResults) { HostResolverCache cache(kMaxResults, clock_, tick_clock_); const std::string kName = "foo.test"; const std::vector kRestoredEndpoints = { IPEndPoint(IPAddress::FromIPLiteral("::1").value(), /*port=*/0)}; const base::Time kExpiration = clock_.Now() + base::Hours(2); auto result = std::make_unique( kName, DnsQueryType::AAAA, tick_clock_.NowTicks() + base::Hours(2), kExpiration, HostResolverInternalResult::Source::kDns, kRestoredEndpoints, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); const NetworkAnonymizationKey anonymization_key; cache.Set(std::move(result), anonymization_key, HostResolverSource::DNS, /*secure=*/false); base::Value value = cache.Serialize(); EXPECT_EQ(value.GetList().size(), 1u); HostResolverCache restored_cache(kMaxResults, clock_, tick_clock_); const std::vector kEndpoints = { IPEndPoint(IPAddress::FromIPLiteral("2001:DB8::3").value(), /*port=*/0)}; result = std::make_unique( kName, DnsQueryType::AAAA, tick_clock_.NowTicks() + base::Hours(2), kExpiration, HostResolverInternalResult::Source::kDns, kEndpoints, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); restored_cache.Set(std::move(result), anonymization_key, HostResolverSource::DNS, /*secure=*/false); EXPECT_TRUE(restored_cache.RestoreFromValue(value)); // Expect pre-restoration result. EXPECT_THAT( restored_cache.LookupStale(kName, anonymization_key), Optional(IsNotStale(ExpectHostResolverInternalDataResult( kName, DnsQueryType::AAAA, HostResolverInternalResult::Source::kDns, Ne(std::nullopt), Optional(kExpiration), kEndpoints)))); } TEST_F(HostResolverCacheTest, DeserializeStopsBeforeEviction) { HostResolverCache cache(kMaxResults, clock_, tick_clock_); const std::string kName1 = "foo1.test"; const std::vector kRestoredEndpoints = { IPEndPoint(IPAddress::FromIPLiteral("::1").value(), /*port=*/0)}; const base::Time kExpiration = clock_.Now() + base::Hours(2); auto result = std::make_unique( kName1, DnsQueryType::AAAA, tick_clock_.NowTicks() + base::Hours(2), kExpiration, HostResolverInternalResult::Source::kDns, kRestoredEndpoints, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); const NetworkAnonymizationKey anonymization_key; cache.Set(std::move(result), anonymization_key, HostResolverSource::DNS, /*secure=*/false); base::Value value = cache.Serialize(); EXPECT_EQ(value.GetList().size(), 1u); HostResolverCache restored_cache(1, clock_, tick_clock_); const std::string kName2 = "foo2.test"; const std::vector kEndpoints = { IPEndPoint(IPAddress::FromIPLiteral("2001:DB8::3").value(), /*port=*/0)}; result = std::make_unique( kName2, DnsQueryType::AAAA, tick_clock_.NowTicks() + base::Hours(2), kExpiration, HostResolverInternalResult::Source::kDns, kEndpoints, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); restored_cache.Set(std::move(result), anonymization_key, HostResolverSource::DNS, /*secure=*/false); EXPECT_TRUE(restored_cache.RestoreFromValue(value)); // Expect only pre-restoration result. EXPECT_EQ(restored_cache.LookupStale(kName1, anonymization_key), std::nullopt); EXPECT_THAT( restored_cache.LookupStale(kName2, anonymization_key), Optional(IsNotStale(ExpectHostResolverInternalDataResult( kName2, DnsQueryType::AAAA, HostResolverInternalResult::Source::kDns, Ne(std::nullopt), Optional(kExpiration), kEndpoints)))); } TEST_F(HostResolverCacheTest, SerializeForLogging) { HostResolverCache cache(kMaxResults, clock_, tick_clock_); const std::string kName = "foo.test"; const std::vector kEndpoints = { IPEndPoint(IPAddress::FromIPLiteral("::1").value(), /*port=*/0)}; const base::Time kExpiration = clock_.Now() + base::Hours(2); auto result = std::make_unique( kName, DnsQueryType::AAAA, tick_clock_.NowTicks() + base::Hours(2), kExpiration, HostResolverInternalResult::Source::kDns, kEndpoints, /*strings=*/std::vector{}, /*hosts=*/std::vector{}); const NetworkAnonymizationKey anonymization_key; cache.Set(std::move(result), anonymization_key, HostResolverSource::DNS, /*secure=*/false); base::Value value = cache.SerializeForLogging(); EXPECT_TRUE(value.is_dict()); EXPECT_FALSE(cache.RestoreFromValue(value)); } } // namespace } // namespace net