| // Copyright 2020 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "net/dns/dns_response_result_extractor.h" |
| |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/strings/string_piece.h" |
| #include "base/time/time.h" |
| #include "net/base/connection_endpoint_metadata_test_util.h" |
| #include "net/base/host_port_pair.h" |
| #include "net/base/ip_address.h" |
| #include "net/base/ip_endpoint.h" |
| #include "net/base/net_errors.h" |
| #include "net/dns/dns_query.h" |
| #include "net/dns/dns_response.h" |
| #include "net/dns/dns_test_util.h" |
| #include "net/dns/host_cache.h" |
| #include "net/dns/host_resolver_results_test_util.h" |
| #include "net/dns/public/dns_protocol.h" |
| #include "net/dns/public/dns_query_type.h" |
| #include "net/test/gtest_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" |
| |
| namespace net { |
| namespace { |
| |
| TEST(DnsResponseResultExtractorTest, ExtractsSingleARecord) { |
| constexpr char kName[] = "address.test"; |
| const IPAddress kExpected(192, 168, 0, 1); |
| |
| DnsResponse response = BuildTestDnsAddressResponse(kName, kExpected); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::A, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsOk()); |
| IPEndPoint expected_endpoint(kExpected, 0 /* port */); |
| |
| EXPECT_THAT(results.GetEndpoints(), |
| testing::Optional(testing::ElementsAre(ExpectEndpointResult( |
| testing::ElementsAre(expected_endpoint))))); |
| EXPECT_THAT(results.aliases(), testing::Pointee(testing::ElementsAre(kName))); |
| EXPECT_TRUE(results.has_ttl()); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, ExtractsSingleAAAARecord) { |
| constexpr char kName[] = "address.test"; |
| |
| IPAddress expected; |
| CHECK(expected.AssignFromIPLiteral("2001:4860:4860::8888")); |
| |
| DnsResponse response = BuildTestDnsAddressResponse(kName, expected); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::AAAA, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsOk()); |
| IPEndPoint expected_endpoint(expected, 0 /* port */); |
| EXPECT_THAT(results.GetEndpoints(), |
| testing::Optional(testing::ElementsAre(ExpectEndpointResult( |
| testing::ElementsAre(expected_endpoint))))); |
| EXPECT_THAT(results.aliases(), testing::Pointee(testing::ElementsAre(kName))); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, ExtractsSingleARecordWithCname) { |
| const IPAddress kExpected(192, 168, 0, 1); |
| constexpr char kName[] = "address.test"; |
| constexpr char kCanonicalName[] = "alias.test"; |
| |
| DnsResponse response = |
| BuildTestDnsAddressResponseWithCname(kName, kExpected, kCanonicalName); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::A, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsOk()); |
| IPEndPoint expected_endpoint(kExpected, 0 /* port */); |
| EXPECT_THAT(results.GetEndpoints(), |
| testing::Optional(testing::ElementsAre(ExpectEndpointResult( |
| testing::ElementsAre(expected_endpoint))))); |
| EXPECT_THAT(results.aliases(), testing::Pointee(testing::UnorderedElementsAre( |
| kName, kCanonicalName))); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, ExtractsARecordsWithCname) { |
| constexpr char kName[] = "addresses.test"; |
| |
| DnsResponse response = BuildTestDnsResponse( |
| "addresses.test", dns_protocol::kTypeA, |
| { |
| BuildTestAddressRecord("alias.test", IPAddress(74, 125, 226, 179)), |
| BuildTestAddressRecord("alias.test", IPAddress(74, 125, 226, 180)), |
| BuildTestCnameRecord(kName, "alias.test"), |
| BuildTestAddressRecord("alias.test", IPAddress(74, 125, 226, 176)), |
| BuildTestAddressRecord("alias.test", IPAddress(74, 125, 226, 177)), |
| BuildTestAddressRecord("alias.test", IPAddress(74, 125, 226, 178)), |
| }); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::A, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsOk()); |
| EXPECT_THAT( |
| results.GetEndpoints(), |
| testing::Optional(testing::ElementsAre( |
| ExpectEndpointResult(testing::UnorderedElementsAre( |
| IPEndPoint(IPAddress(74, 125, 226, 179), 0 /* port */), |
| IPEndPoint(IPAddress(74, 125, 226, 178), 0 /* port */), |
| IPEndPoint(IPAddress(74, 125, 226, 180), 0 /* port */), |
| IPEndPoint(IPAddress(74, 125, 226, 176), 0 /* port */), |
| IPEndPoint(IPAddress(74, 125, 226, 177), 0 /* port */)))))); |
| EXPECT_THAT(results.aliases(), testing::Pointee(testing::UnorderedElementsAre( |
| "alias.test", kName))); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, ExtractsNxdomainAResponses) { |
| constexpr char kName[] = "address.test"; |
| constexpr auto kTtl = base::Hours(2); |
| |
| DnsResponse response = BuildTestDnsResponse( |
| kName, dns_protocol::kTypeA, /*answers=*/{}, |
| /*authority=*/ |
| {BuildTestDnsRecord(kName, dns_protocol::kTypeSOA, "fake rdata", kTtl)}, |
| /*additional=*/{}, dns_protocol::kRcodeNXDOMAIN); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::A, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsError(ERR_NAME_NOT_RESOLVED)); |
| EXPECT_THAT(results.GetEndpoints(), testing::Optional(testing::IsEmpty())); |
| EXPECT_THAT(results.aliases(), testing::Pointee(testing::ElementsAre(kName))); |
| |
| ASSERT_TRUE(results.has_ttl()); |
| EXPECT_EQ(results.ttl(), kTtl); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, ExtractsNodataAResponses) { |
| constexpr char kName[] = "address.test"; |
| constexpr auto kTtl = base::Minutes(15); |
| |
| DnsResponse response = BuildTestDnsResponse( |
| kName, dns_protocol::kTypeA, /*answers=*/{}, |
| /*authority=*/ |
| {BuildTestDnsRecord(kName, dns_protocol::kTypeSOA, "fake rdata", kTtl)}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::A, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsError(ERR_NAME_NOT_RESOLVED)); |
| EXPECT_THAT(results.GetEndpoints(), testing::Optional(testing::IsEmpty())); |
| EXPECT_THAT(results.aliases(), testing::Pointee(testing::ElementsAre(kName))); |
| |
| ASSERT_TRUE(results.has_ttl()); |
| EXPECT_EQ(results.ttl(), kTtl); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, RejectsMalformedARecord) { |
| constexpr char kName[] = "address.test"; |
| |
| DnsResponse response = BuildTestDnsResponse( |
| kName, dns_protocol::kTypeA, |
| {BuildTestDnsRecord(kName, dns_protocol::kTypeA, |
| "malformed rdata")} /* answers */); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::A, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kMalformedRecord); |
| |
| EXPECT_THAT(results.error(), test::IsError(ERR_DNS_MALFORMED_RESPONSE)); |
| EXPECT_FALSE(results.has_ttl()); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, RejectsWrongNameARecord) { |
| constexpr char kName[] = "address.test"; |
| |
| DnsResponse response = BuildTestDnsAddressResponse( |
| kName, IPAddress(1, 2, 3, 4), "different.test"); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::A, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kNameMismatch); |
| |
| EXPECT_THAT(results.error(), test::IsError(ERR_DNS_MALFORMED_RESPONSE)); |
| EXPECT_FALSE(results.has_ttl()); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, IgnoresWrongTypeRecordsInAResponse) { |
| constexpr char kName[] = "address.test"; |
| |
| DnsResponse response = BuildTestDnsResponse( |
| kName, dns_protocol::kTypeA, |
| {BuildTestTextRecord("address.test", {"foo"} /* text_strings */)}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::A, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsError(ERR_NAME_NOT_RESOLVED)); |
| EXPECT_THAT(results.GetEndpoints(), testing::Optional(testing::IsEmpty())); |
| EXPECT_THAT(results.aliases(), |
| testing::Pointee(testing::ElementsAre("address.test"))); |
| EXPECT_FALSE(results.has_ttl()); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, IgnoresWrongTypeRecordsMixedWithARecords) { |
| constexpr char kName[] = "address.test"; |
| const IPAddress kExpected(8, 8, 8, 8); |
| constexpr auto kTtl = base::Days(3); |
| |
| DnsResponse response = BuildTestDnsResponse( |
| kName, dns_protocol::kTypeA, |
| {BuildTestTextRecord(kName, /*text_strings=*/{"foo"}, base::Hours(2)), |
| BuildTestAddressRecord(kName, kExpected, kTtl)}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::A, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsOk()); |
| IPEndPoint expected_endpoint(kExpected, 0 /* port */); |
| EXPECT_THAT(results.GetEndpoints(), |
| testing::Optional(testing::ElementsAre(ExpectEndpointResult( |
| testing::ElementsAre(expected_endpoint))))); |
| EXPECT_THAT(results.aliases(), testing::Pointee(testing::ElementsAre(kName))); |
| |
| ASSERT_TRUE(results.has_ttl()); |
| EXPECT_EQ(results.ttl(), kTtl); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, ExtractsMinATtl) { |
| constexpr char kName[] = "name.test"; |
| constexpr base::TimeDelta kMinTtl = base::Minutes(4); |
| |
| DnsResponse response = BuildTestDnsResponse( |
| kName, dns_protocol::kTypeA, |
| {BuildTestAddressRecord(kName, IPAddress(1, 2, 3, 4), base::Hours(3)), |
| BuildTestAddressRecord(kName, IPAddress(2, 3, 4, 5), kMinTtl), |
| BuildTestAddressRecord(kName, IPAddress(3, 4, 5, 6), |
| base::Minutes(15))}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::A, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| ASSERT_TRUE(results.has_ttl()); |
| EXPECT_EQ(results.ttl(), kMinTtl); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, ExtractsTxtResponses) { |
| constexpr char kName[] = "name.test"; |
| |
| // Simulate two separate DNS records, each with multiple strings. |
| std::vector<std::string> foo_records = {"foo1", "foo2", "foo3"}; |
| std::vector<std::string> bar_records = {"bar1", "bar2"}; |
| std::vector<std::vector<std::string>> text_records = {foo_records, |
| bar_records}; |
| |
| DnsResponse response = |
| BuildTestDnsTextResponse(kName, std::move(text_records)); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::TXT, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsOk()); |
| |
| // Order between separate DNS records is undefined, but each record should |
| // stay in order as that order may be meaningful. |
| ASSERT_THAT(results.text_records(), |
| testing::Optional(testing::UnorderedElementsAre( |
| "foo1", "foo2", "foo3", "bar1", "bar2"))); |
| std::vector<std::string> results_vector = results.text_records().value(); |
| EXPECT_NE(results_vector.end(), |
| std::search(results_vector.begin(), results_vector.end(), |
| foo_records.begin(), foo_records.end())); |
| EXPECT_NE(results_vector.end(), |
| std::search(results_vector.begin(), results_vector.end(), |
| bar_records.begin(), bar_records.end())); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, ExtractsNxdomainTxtResponses) { |
| constexpr char kName[] = "name.test"; |
| constexpr auto kTtl = base::Days(4); |
| |
| DnsResponse response = BuildTestDnsResponse( |
| kName, dns_protocol::kTypeTXT, /*answers=*/{}, |
| /*authority=*/ |
| {BuildTestDnsRecord(kName, dns_protocol::kTypeSOA, "fake rdata", kTtl)}, |
| /*additional=*/{}, dns_protocol::kRcodeNXDOMAIN); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::TXT, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsError(ERR_NAME_NOT_RESOLVED)); |
| EXPECT_THAT(results.text_records(), testing::Optional(testing::IsEmpty())); |
| |
| ASSERT_TRUE(results.has_ttl()); |
| EXPECT_EQ(results.ttl(), kTtl); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, ExtractsNodataTxtResponses) { |
| constexpr char kName[] = "name.test"; |
| constexpr auto kTtl = base::Minutes(42); |
| |
| DnsResponse response = BuildTestDnsResponse( |
| kName, dns_protocol::kTypeTXT, |
| /*answers=*/{}, /*authority=*/ |
| {BuildTestDnsRecord(kName, dns_protocol::kTypeSOA, "fake rdata", kTtl)}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::TXT, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsError(ERR_NAME_NOT_RESOLVED)); |
| EXPECT_THAT(results.text_records(), testing::Optional(testing::IsEmpty())); |
| |
| ASSERT_TRUE(results.has_ttl()); |
| EXPECT_EQ(results.ttl(), kTtl); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, RejectsMalformedTxtRecord) { |
| constexpr char kName[] = "name.test"; |
| |
| DnsResponse response = BuildTestDnsResponse( |
| kName, dns_protocol::kTypeTXT, |
| {BuildTestDnsRecord(kName, dns_protocol::kTypeTXT, |
| "malformed rdata")} /* answers */); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::TXT, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kMalformedRecord); |
| |
| EXPECT_THAT(results.error(), test::IsError(ERR_DNS_MALFORMED_RESPONSE)); |
| EXPECT_FALSE(results.has_ttl()); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, RejectsWrongNameTxtRecord) { |
| constexpr char kName[] = "name.test"; |
| |
| DnsResponse response = |
| BuildTestDnsTextResponse(kName, {{"foo"}}, "different.test"); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::TXT, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kNameMismatch); |
| |
| EXPECT_THAT(results.error(), test::IsError(ERR_DNS_MALFORMED_RESPONSE)); |
| EXPECT_FALSE(results.has_ttl()); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, IgnoresWrongTypeTxtResponses) { |
| constexpr char kName[] = "name.test"; |
| |
| DnsResponse response = BuildTestDnsResponse( |
| kName, dns_protocol::kTypeTXT, |
| {BuildTestAddressRecord(kName, IPAddress(1, 2, 3, 4))}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::TXT, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsError(ERR_NAME_NOT_RESOLVED)); |
| EXPECT_THAT(results.text_records(), testing::Optional(testing::IsEmpty())); |
| EXPECT_FALSE(results.has_ttl()); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, ExtractsMinTxtTtl) { |
| constexpr char kName[] = "name.test"; |
| constexpr base::TimeDelta kMinTtl = base::Minutes(4); |
| |
| DnsResponse response = BuildTestDnsResponse( |
| kName, dns_protocol::kTypeTXT, |
| {BuildTestTextRecord(kName, {"foo"}, base::Hours(3)), |
| BuildTestTextRecord(kName, {"bar"}, kMinTtl), |
| BuildTestTextRecord(kName, {"baz"}, base::Minutes(15))}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::TXT, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| ASSERT_TRUE(results.has_ttl()); |
| EXPECT_EQ(results.ttl(), kMinTtl); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, ExtractsPtrResponses) { |
| constexpr char kName[] = "name.test"; |
| |
| DnsResponse response = |
| BuildTestDnsPointerResponse(kName, {"foo.com", "bar.com"}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::PTR, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsOk()); |
| |
| // Order between separate records is undefined. |
| EXPECT_THAT(results.hostnames(), |
| testing::Optional(testing::UnorderedElementsAre( |
| HostPortPair("foo.com", 0), HostPortPair("bar.com", 0)))); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, ExtractsNxdomainPtrResponses) { |
| constexpr char kName[] = "name.test"; |
| constexpr auto kTtl = base::Hours(5); |
| |
| DnsResponse response = BuildTestDnsResponse( |
| kName, dns_protocol::kTypePTR, /*answers=*/{}, |
| /*authority=*/ |
| {BuildTestDnsRecord(kName, dns_protocol::kTypeSOA, "fake rdata", kTtl)}, |
| /*additional=*/{}, dns_protocol::kRcodeNXDOMAIN); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::PTR, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsError(ERR_NAME_NOT_RESOLVED)); |
| EXPECT_THAT(results.hostnames(), testing::Optional(testing::IsEmpty())); |
| |
| ASSERT_TRUE(results.has_ttl()); |
| EXPECT_EQ(results.ttl(), kTtl); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, ExtractsNodataPtrResponses) { |
| constexpr char kName[] = "name.test"; |
| constexpr auto kTtl = base::Minutes(50); |
| |
| DnsResponse response = BuildTestDnsResponse( |
| kName, dns_protocol::kTypePTR, /*answers=*/{}, |
| /*authority=*/ |
| {BuildTestDnsRecord(kName, dns_protocol::kTypeSOA, "fake rdata", kTtl)}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::PTR, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsError(ERR_NAME_NOT_RESOLVED)); |
| EXPECT_THAT(results.hostnames(), testing::Optional(testing::IsEmpty())); |
| |
| ASSERT_TRUE(results.has_ttl()); |
| EXPECT_EQ(results.ttl(), kTtl); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, RejectsMalformedPtrRecord) { |
| constexpr char kName[] = "name.test"; |
| |
| DnsResponse response = BuildTestDnsResponse( |
| kName, dns_protocol::kTypePTR, |
| {BuildTestDnsRecord(kName, dns_protocol::kTypePTR, |
| "malformed rdata")} /* answers */); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::PTR, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kMalformedRecord); |
| |
| EXPECT_THAT(results.error(), test::IsError(ERR_DNS_MALFORMED_RESPONSE)); |
| EXPECT_FALSE(results.has_ttl()); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, RejectsWrongNamePtrRecord) { |
| constexpr char kName[] = "name.test"; |
| |
| DnsResponse response = BuildTestDnsPointerResponse( |
| kName, {"foo.com", "bar.com"}, "different.test"); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::PTR, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kNameMismatch); |
| |
| EXPECT_THAT(results.error(), test::IsError(ERR_DNS_MALFORMED_RESPONSE)); |
| EXPECT_FALSE(results.has_ttl()); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, IgnoresWrongTypePtrResponses) { |
| constexpr char kName[] = "name.test"; |
| |
| DnsResponse response = BuildTestDnsResponse( |
| kName, dns_protocol::kTypePTR, |
| {BuildTestAddressRecord(kName, IPAddress(1, 2, 3, 4))}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::PTR, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsError(ERR_NAME_NOT_RESOLVED)); |
| EXPECT_THAT(results.hostnames(), testing::Optional(testing::IsEmpty())); |
| EXPECT_FALSE(results.has_ttl()); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, ExtractsSrvResponses) { |
| constexpr char kName[] = "name.test"; |
| |
| const TestServiceRecord kRecord1 = {2, 3, 1223, "foo.com"}; |
| const TestServiceRecord kRecord2 = {5, 10, 80, "bar.com"}; |
| const TestServiceRecord kRecord3 = {5, 1, 5, "google.com"}; |
| const TestServiceRecord kRecord4 = {2, 100, 12345, "chromium.org"}; |
| |
| DnsResponse response = BuildTestDnsServiceResponse( |
| kName, {kRecord1, kRecord2, kRecord3, kRecord4}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::SRV, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsOk()); |
| |
| // Expect ordered by priority, and random within a priority. |
| absl::optional<std::vector<HostPortPair>> result_hosts = results.hostnames(); |
| ASSERT_THAT( |
| result_hosts, |
| testing::Optional(testing::UnorderedElementsAre( |
| HostPortPair("foo.com", 1223), HostPortPair("bar.com", 80), |
| HostPortPair("google.com", 5), HostPortPair("chromium.org", 12345)))); |
| auto priority2 = std::vector<HostPortPair>(result_hosts.value().begin(), |
| result_hosts.value().begin() + 2); |
| EXPECT_THAT(priority2, testing::UnorderedElementsAre( |
| HostPortPair("foo.com", 1223), |
| HostPortPair("chromium.org", 12345))); |
| auto priority5 = std::vector<HostPortPair>(result_hosts.value().begin() + 2, |
| result_hosts.value().end()); |
| EXPECT_THAT(priority5, |
| testing::UnorderedElementsAre(HostPortPair("bar.com", 80), |
| HostPortPair("google.com", 5))); |
| } |
| |
| // 0-weight services are allowed. Ensure that we can handle such records, |
| // especially the case where all entries have weight 0. |
| TEST(DnsResponseResultExtractorTest, ExtractsZeroWeightSrvResponses) { |
| constexpr char kName[] = "name.test"; |
| |
| const TestServiceRecord kRecord1 = {5, 0, 80, "bar.com"}; |
| const TestServiceRecord kRecord2 = {5, 0, 5, "google.com"}; |
| |
| DnsResponse response = |
| BuildTestDnsServiceResponse(kName, {kRecord1, kRecord2}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::SRV, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsOk()); |
| |
| // Expect ordered by priority, and random within a priority. |
| EXPECT_THAT(results.hostnames(), |
| testing::Optional(testing::UnorderedElementsAre( |
| HostPortPair("bar.com", 80), HostPortPair("google.com", 5)))); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, ExtractsNxdomainSrvResponses) { |
| constexpr char kName[] = "name.test"; |
| constexpr auto kTtl = base::Days(7); |
| |
| DnsResponse response = BuildTestDnsResponse( |
| kName, dns_protocol::kTypeSRV, /*answers=*/{}, |
| /*authority=*/ |
| {BuildTestDnsRecord(kName, dns_protocol::kTypeSOA, "fake rdata", kTtl)}, |
| /*additional=*/{}, dns_protocol::kRcodeNXDOMAIN); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::SRV, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsError(ERR_NAME_NOT_RESOLVED)); |
| EXPECT_THAT(results.hostnames(), testing::Optional(testing::IsEmpty())); |
| |
| ASSERT_TRUE(results.has_ttl()); |
| EXPECT_EQ(results.ttl(), kTtl); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, ExtractsNodataSrvResponses) { |
| constexpr char kName[] = "name.test"; |
| constexpr auto kTtl = base::Hours(12); |
| |
| DnsResponse response = BuildTestDnsResponse( |
| kName, dns_protocol::kTypeSRV, /*answers=*/{}, |
| /*authority=*/ |
| {BuildTestDnsRecord(kName, dns_protocol::kTypeSOA, "fake rdata", kTtl)}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::SRV, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsError(ERR_NAME_NOT_RESOLVED)); |
| EXPECT_THAT(results.hostnames(), testing::Optional(testing::IsEmpty())); |
| |
| ASSERT_TRUE(results.has_ttl()); |
| EXPECT_EQ(results.ttl(), kTtl); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, RejectsMalformedSrvRecord) { |
| constexpr char kName[] = "name.test"; |
| |
| DnsResponse response = BuildTestDnsResponse( |
| kName, dns_protocol::kTypeSRV, |
| {BuildTestDnsRecord(kName, dns_protocol::kTypeSRV, |
| "malformed rdata")} /* answers */); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::SRV, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kMalformedRecord); |
| |
| EXPECT_THAT(results.error(), test::IsError(ERR_DNS_MALFORMED_RESPONSE)); |
| EXPECT_FALSE(results.has_ttl()); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, RejectsWrongNameSrvRecord) { |
| constexpr char kName[] = "name.test"; |
| |
| const TestServiceRecord kRecord = {2, 3, 1223, "foo.com"}; |
| DnsResponse response = |
| BuildTestDnsServiceResponse(kName, {kRecord}, "different.test"); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::SRV, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kNameMismatch); |
| |
| EXPECT_THAT(results.error(), test::IsError(ERR_DNS_MALFORMED_RESPONSE)); |
| EXPECT_FALSE(results.has_ttl()); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, IgnoresWrongTypeSrvResponses) { |
| constexpr char kName[] = "name.test"; |
| |
| DnsResponse response = BuildTestDnsResponse( |
| kName, dns_protocol::kTypeSRV, |
| {BuildTestAddressRecord(kName, IPAddress(1, 2, 3, 4))}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::SRV, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsError(ERR_NAME_NOT_RESOLVED)); |
| EXPECT_THAT(results.hostnames(), testing::Optional(testing::IsEmpty())); |
| EXPECT_FALSE(results.has_ttl()); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, ExtractsExperimentalHttpsResponses) { |
| constexpr char kName[] = "https.test"; |
| constexpr auto kTtl = base::Minutes(31); |
| |
| DnsResponse response = BuildTestDnsResponse( |
| kName, dns_protocol::kTypeHttps, |
| {BuildTestHttpsAliasRecord(kName, "alias.test", kTtl)}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::HTTPS_EXPERIMENTAL, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsOk()); |
| EXPECT_THAT(results.https_record_compatibility(), |
| testing::Pointee(testing::ElementsAre(true))); |
| |
| ASSERT_TRUE(results.has_ttl()); |
| EXPECT_EQ(results.ttl(), kTtl); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, |
| ExtractsNxdomainExperimentalHttpsResponses) { |
| constexpr char kName[] = "https.test"; |
| constexpr auto kTtl = base::Hours(8); |
| |
| DnsResponse response = BuildTestDnsResponse( |
| kName, dns_protocol::kTypeHttps, /*answers=*/{}, |
| /*authority=*/ |
| {BuildTestDnsRecord(kName, dns_protocol::kTypeSOA, "fake rdata", kTtl)}, |
| /*additional=*/{}, dns_protocol::kRcodeNXDOMAIN); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::HTTPS_EXPERIMENTAL, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsError(ERR_NAME_NOT_RESOLVED)); |
| EXPECT_THAT(results.https_record_compatibility(), |
| testing::Pointee(testing::IsEmpty())); |
| |
| ASSERT_TRUE(results.has_ttl()); |
| EXPECT_EQ(results.ttl(), kTtl); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, ExtractsNodataExperimentalHttpsResponses) { |
| constexpr char kName[] = "https.test"; |
| constexpr auto kTtl = base::Days(3); |
| |
| DnsResponse response = BuildTestDnsResponse( |
| kName, dns_protocol::kTypeHttps, /*answers=*/{}, |
| /*authority=*/ |
| {BuildTestDnsRecord(kName, dns_protocol::kTypeSOA, "fake rdata", kTtl)}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::HTTPS_EXPERIMENTAL, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsError(ERR_NAME_NOT_RESOLVED)); |
| EXPECT_THAT(results.https_record_compatibility(), |
| testing::Pointee(testing::IsEmpty())); |
| |
| ASSERT_TRUE(results.has_ttl()); |
| EXPECT_EQ(results.ttl(), kTtl); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, RejectsMalformedExperimentalHttpsRecord) { |
| constexpr char kName[] = "https.test"; |
| |
| DnsResponse response = BuildTestDnsResponse( |
| kName, dns_protocol::kTypeHttps, |
| {BuildTestDnsRecord(kName, dns_protocol::kTypeHttps, |
| "malformed rdata")} /* answers */); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::HTTPS_EXPERIMENTAL, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kMalformedRecord); |
| |
| EXPECT_THAT(results.error(), test::IsError(ERR_DNS_MALFORMED_RESPONSE)); |
| EXPECT_FALSE(results.has_ttl()); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, RejectsWrongNameExperimentalHttpsRecord) { |
| constexpr char kName[] = "https.test"; |
| |
| DnsResponse response = BuildTestDnsResponse( |
| kName, dns_protocol::kTypeHttps, |
| {BuildTestHttpsAliasRecord("different.test", "alias.test")}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::HTTPS_EXPERIMENTAL, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kNameMismatch); |
| |
| EXPECT_THAT(results.error(), test::IsError(ERR_DNS_MALFORMED_RESPONSE)); |
| EXPECT_FALSE(results.has_ttl()); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, |
| IgnoresWrongTypeExperimentalHttpsResponses) { |
| constexpr char kName[] = "https.test"; |
| |
| DnsResponse response = BuildTestDnsResponse( |
| kName, dns_protocol::kTypeHttps, |
| {BuildTestAddressRecord(kName, IPAddress(1, 2, 3, 4))}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::HTTPS_EXPERIMENTAL, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsError(ERR_NAME_NOT_RESOLVED)); |
| EXPECT_THAT(results.https_record_compatibility(), |
| testing::Pointee(testing::IsEmpty())); |
| EXPECT_FALSE(results.has_ttl()); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, |
| IgnoresAdditionalExperimentalHttpsRecords) { |
| constexpr char kName[] = "https.test"; |
| constexpr auto kTtl = base::Days(3); |
| |
| DnsResponse response = BuildTestDnsResponse( |
| kName, dns_protocol::kTypeHttps, |
| /*answers=*/{BuildTestHttpsAliasRecord(kName, "alias.test", kTtl)}, |
| /*authority=*/{}, |
| /*additional=*/ |
| {BuildTestHttpsServiceRecord(kName, 3u, "service1.test", {}, |
| base::Minutes(44)), |
| BuildTestHttpsServiceRecord(kName, 2u, "service2.test", {}, |
| base::Minutes(30))}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::HTTPS_EXPERIMENTAL, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsOk()); |
| EXPECT_THAT(results.https_record_compatibility(), |
| testing::Pointee(testing::ElementsAre(true))); |
| |
| ASSERT_TRUE(results.has_ttl()); |
| EXPECT_EQ(results.ttl(), kTtl); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, ExtractsBasicHttpsResponses) { |
| constexpr char kName[] = "https.test"; |
| constexpr auto kTtl = base::Hours(12); |
| |
| DnsResponse response = |
| BuildTestDnsResponse(kName, dns_protocol::kTypeHttps, |
| {BuildTestHttpsServiceRecord(kName, /*priority=*/4, |
| /*service_name=*/".", |
| /*params=*/{}, kTtl)}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::HTTPS, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsOk()); |
| EXPECT_THAT( |
| results.GetMetadatas(), |
| testing::Optional(testing::ElementsAre(ExpectConnectionEndpointMetadata( |
| testing::ElementsAre(dns_protocol::kHttpsServiceDefaultAlpn))))); |
| EXPECT_THAT(results.https_record_compatibility(), |
| testing::Pointee(testing::ElementsAre(true))); |
| |
| ASSERT_TRUE(results.has_ttl()); |
| EXPECT_EQ(results.ttl(), kTtl); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, ExtractsComprehensiveHttpsResponses) { |
| constexpr char kName[] = "https.test"; |
| constexpr char kAlpn[] = "foo"; |
| constexpr uint8_t kEchConfig[] = "EEEEEEEEECH!"; |
| constexpr auto kTtl = base::Hours(12); |
| |
| DnsResponse response = BuildTestDnsResponse( |
| kName, dns_protocol::kTypeHttps, |
| {BuildTestHttpsServiceRecord( |
| kName, /*priority=*/4, |
| /*service_name=*/".", |
| /*params=*/ |
| {BuildTestHttpsServiceAlpnParam({kAlpn}), |
| BuildTestHttpsServiceEchConfigParam(kEchConfig)}, |
| kTtl), |
| BuildTestHttpsServiceRecord( |
| kName, /*priority=*/3, |
| /*service_name=*/".", |
| /*params=*/ |
| {BuildTestHttpsServiceAlpnParam({kAlpn}), |
| {dns_protocol::kHttpsServiceParamKeyNoDefaultAlpn, ""}}, |
| /*ttl=*/base::Days(3))}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::HTTPS, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsOk()); |
| EXPECT_THAT(results.GetMetadatas(), |
| testing::Optional(testing::ElementsAre( |
| ExpectConnectionEndpointMetadata(testing::ElementsAre(kAlpn)), |
| ExpectConnectionEndpointMetadata( |
| testing::ElementsAre( |
| kAlpn, dns_protocol::kHttpsServiceDefaultAlpn), |
| testing::ElementsAreArray(kEchConfig))))); |
| EXPECT_THAT(results.https_record_compatibility(), |
| testing::Pointee(testing::ElementsAre(true, true))); |
| |
| ASSERT_TRUE(results.has_ttl()); |
| EXPECT_EQ(results.ttl(), kTtl); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, IgnoresHttpsResponseWithAlias) { |
| constexpr char kName[] = "https.test"; |
| |
| DnsResponse response = |
| BuildTestDnsResponse(kName, dns_protocol::kTypeHttps, |
| {BuildTestHttpsServiceRecord(kName, /*priority=*/4, |
| /*service_name=*/".", |
| /*params=*/{}), |
| BuildTestHttpsAliasRecord(kName, "alias.test")}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::HTTPS, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsError(ERR_NAME_NOT_RESOLVED)); |
| EXPECT_THAT(results.GetMetadatas(), testing::Optional(testing::IsEmpty())); |
| |
| // Expected to still output record compatibility for otherwise-ignored records |
| EXPECT_THAT(results.https_record_compatibility(), |
| testing::Pointee(testing::ElementsAre(true, true))); |
| } |
| |
| // Expect the entire response to be ignored if all HTTPS records have the |
| // "no-default-alpn" param. |
| TEST(DnsResponseResultExtractorTest, IgnoresHttpsResponseWithNoDefaultAlpn) { |
| constexpr char kName[] = "https.test"; |
| |
| DnsResponse response = BuildTestDnsResponse( |
| kName, dns_protocol::kTypeHttps, |
| {BuildTestHttpsServiceRecord( |
| kName, /*priority=*/4, |
| /*service_name=*/".", |
| /*params=*/ |
| {BuildTestHttpsServiceAlpnParam({"foo1"}), |
| {dns_protocol::kHttpsServiceParamKeyNoDefaultAlpn, ""}}), |
| BuildTestHttpsServiceRecord( |
| kName, /*priority=*/5, |
| /*service_name=*/".", |
| /*params=*/ |
| {BuildTestHttpsServiceAlpnParam({"foo2"}), |
| {dns_protocol::kHttpsServiceParamKeyNoDefaultAlpn, ""}})}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::HTTPS, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsError(ERR_NAME_NOT_RESOLVED)); |
| EXPECT_THAT(results.GetMetadatas(), testing::Optional(testing::IsEmpty())); |
| |
| // Expected to still output record compatibility for otherwise-ignored records |
| EXPECT_THAT(results.https_record_compatibility(), |
| testing::Pointee(testing::ElementsAre(true, true))); |
| } |
| |
| // Unsupported/unknown HTTPS params are simply ignored if not marked mandatory. |
| TEST(DnsResponseResultExtractorTest, IgnoresUnsupportedParamsInHttpsRecord) { |
| constexpr char kName[] = "https.test"; |
| constexpr uint16_t kMadeUpParamKey = 65500; // From the private-use block. |
| |
| DnsResponse response = BuildTestDnsResponse( |
| kName, dns_protocol::kTypeHttps, |
| {BuildTestHttpsServiceRecord(kName, /*priority=*/4, |
| /*service_name=*/".", |
| /*params=*/ |
| {{kMadeUpParamKey, "foo"}})}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::HTTPS, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsOk()); |
| EXPECT_THAT( |
| results.GetMetadatas(), |
| testing::Optional(testing::ElementsAre(ExpectConnectionEndpointMetadata( |
| testing::ElementsAre(dns_protocol::kHttpsServiceDefaultAlpn))))); |
| EXPECT_THAT(results.https_record_compatibility(), |
| testing::Pointee(testing::ElementsAre(true))); |
| } |
| |
| // Entire record is dropped if an unsupported/unknown HTTPS param is marked |
| // mandatory. |
| TEST(DnsResponseResultExtractorTest, |
| IgnoresHttpsRecordWithUnsupportedMandatoryParam) { |
| constexpr char kName[] = "https.test"; |
| constexpr uint16_t kMadeUpParamKey = 65500; // From the private-use block. |
| |
| DnsResponse response = BuildTestDnsResponse( |
| kName, dns_protocol::kTypeHttps, |
| {BuildTestHttpsServiceRecord( |
| kName, /*priority=*/4, |
| /*service_name=*/".", |
| /*params=*/ |
| {BuildTestHttpsServiceAlpnParam({"ignored_alpn"}), |
| BuildTestHttpsServiceMandatoryParam({kMadeUpParamKey}), |
| {kMadeUpParamKey, "foo"}}), |
| BuildTestHttpsServiceRecord( |
| kName, /*priority=*/5, |
| /*service_name=*/".", |
| /*params=*/{BuildTestHttpsServiceAlpnParam({"foo"})})}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::HTTPS, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsOk()); |
| EXPECT_THAT(results.GetMetadatas(), |
| testing::Optional(testing::ElementsAre( |
| ExpectConnectionEndpointMetadata(testing::ElementsAre( |
| "foo", dns_protocol::kHttpsServiceDefaultAlpn))))); |
| EXPECT_THAT(results.https_record_compatibility(), |
| testing::Pointee(testing::ElementsAre(false, true))); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, |
| ExtractsHttpsRecordWithMatchingServiceName) { |
| constexpr char kName[] = "https.test"; |
| |
| DnsResponse response = BuildTestDnsResponse( |
| kName, dns_protocol::kTypeHttps, |
| {BuildTestHttpsServiceRecord(kName, /*priority=*/4, |
| /*service_name=*/kName, |
| /*params=*/ |
| {BuildTestHttpsServiceAlpnParam({"foo"})})}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::HTTPS, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsOk()); |
| EXPECT_THAT(results.GetMetadatas(), |
| testing::Optional(testing::ElementsAre( |
| ExpectConnectionEndpointMetadata(testing::ElementsAre( |
| "foo", dns_protocol::kHttpsServiceDefaultAlpn))))); |
| EXPECT_THAT(results.https_record_compatibility(), |
| testing::Pointee(testing::ElementsAre(true))); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, |
| ExtractsHttpsRecordWithMatchingDefaultServiceName) { |
| constexpr char kName[] = "https.test"; |
| |
| DnsResponse response = BuildTestDnsResponse( |
| kName, dns_protocol::kTypeHttps, |
| {BuildTestHttpsServiceRecord(kName, /*priority=*/4, |
| /*service_name=*/".", |
| /*params=*/ |
| {BuildTestHttpsServiceAlpnParam({"foo"})})}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::HTTPS, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsOk()); |
| EXPECT_THAT(results.GetMetadatas(), |
| testing::Optional(testing::ElementsAre( |
| ExpectConnectionEndpointMetadata(testing::ElementsAre( |
| "foo", dns_protocol::kHttpsServiceDefaultAlpn))))); |
| EXPECT_THAT(results.https_record_compatibility(), |
| testing::Pointee(testing::ElementsAre(true))); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, |
| ExtractsHttpsRecordWithPrefixedNameAndMatchingServiceName) { |
| constexpr char kName[] = "https.test"; |
| constexpr char kPrefixedName[] = "_444._https.https.test"; |
| |
| DnsResponse response = BuildTestDnsResponse( |
| kPrefixedName, dns_protocol::kTypeHttps, |
| {BuildTestHttpsServiceRecord(kPrefixedName, /*priority=*/4, |
| /*service_name=*/kName, |
| /*params=*/ |
| {BuildTestHttpsServiceAlpnParam({"foo"})})}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::HTTPS, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsOk()); |
| EXPECT_THAT(results.GetMetadatas(), |
| testing::Optional(testing::ElementsAre( |
| ExpectConnectionEndpointMetadata(testing::ElementsAre( |
| "foo", dns_protocol::kHttpsServiceDefaultAlpn))))); |
| EXPECT_THAT(results.https_record_compatibility(), |
| testing::Pointee(testing::ElementsAre(true))); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, |
| ExtractsHttpsRecordWithAliasingAndMatchingServiceName) { |
| constexpr char kName[] = "https.test"; |
| |
| DnsResponse response = BuildTestDnsResponse( |
| kName, dns_protocol::kTypeHttps, |
| {BuildTestCnameRecord(kName, "alias.test"), |
| BuildTestHttpsServiceRecord("alias.test", /*priority=*/4, |
| /*service_name=*/kName, |
| /*params=*/ |
| {BuildTestHttpsServiceAlpnParam({"foo"})})}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::HTTPS, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsOk()); |
| EXPECT_THAT(results.GetMetadatas(), |
| testing::Optional(testing::ElementsAre( |
| ExpectConnectionEndpointMetadata(testing::ElementsAre( |
| "foo", dns_protocol::kHttpsServiceDefaultAlpn))))); |
| EXPECT_THAT(results.https_record_compatibility(), |
| testing::Pointee(testing::ElementsAre(true))); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, |
| IgnoreHttpsRecordWithNonMatchingServiceName) { |
| constexpr char kName[] = "https.test"; |
| |
| DnsResponse response = BuildTestDnsResponse( |
| kName, dns_protocol::kTypeHttps, |
| {BuildTestHttpsServiceRecord( |
| kName, /*priority=*/4, |
| /*service_name=*/"other.service.test", |
| /*params=*/ |
| {BuildTestHttpsServiceAlpnParam({"ignored"})}), |
| BuildTestHttpsServiceRecord("https.test", /*priority=*/5, |
| /*service_name=*/".", |
| /*params=*/ |
| {BuildTestHttpsServiceAlpnParam({"foo"})})}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::HTTPS, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsOk()); |
| EXPECT_THAT(results.GetMetadatas(), |
| testing::Optional(testing::ElementsAre( |
| ExpectConnectionEndpointMetadata(testing::ElementsAre( |
| "foo", dns_protocol::kHttpsServiceDefaultAlpn))))); |
| EXPECT_THAT(results.https_record_compatibility(), |
| testing::Pointee(testing::ElementsAre(true, true))); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, |
| IgnoreHttpsRecordWithPrefixedNameAndDefaultServiceName) { |
| constexpr char kPrefixedName[] = "_445._https.https.test"; |
| |
| DnsResponse response = |
| BuildTestDnsResponse(kPrefixedName, dns_protocol::kTypeHttps, |
| {BuildTestHttpsServiceRecord( |
| kPrefixedName, /*priority=*/4, |
| /*service_name=*/".", |
| /*params=*/ |
| {BuildTestHttpsServiceAlpnParam({"ignored"})})}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::HTTPS, |
| /*original_domain_name=*/"https.test", |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsError(ERR_NAME_NOT_RESOLVED)); |
| EXPECT_THAT(results.GetMetadatas(), testing::Optional(testing::IsEmpty())); |
| EXPECT_THAT(results.https_record_compatibility(), |
| testing::Pointee(testing::ElementsAre(true))); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, |
| IgnoreHttpsRecordWithAliasingAndDefaultServiceName) { |
| constexpr char kName[] = "https.test"; |
| |
| DnsResponse response = BuildTestDnsResponse( |
| kName, dns_protocol::kTypeHttps, |
| {BuildTestCnameRecord(kName, "alias.test"), |
| BuildTestHttpsServiceRecord("alias.test", /*priority=*/4, |
| /*service_name=*/".", |
| /*params=*/ |
| {BuildTestHttpsServiceAlpnParam({"foo"})})}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::HTTPS, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsError(ERR_NAME_NOT_RESOLVED)); |
| EXPECT_THAT(results.GetMetadatas(), testing::Optional(testing::IsEmpty())); |
| EXPECT_THAT(results.https_record_compatibility(), |
| testing::Pointee(testing::ElementsAre(true))); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, ExtractsHttpsRecordWithMatchingPort) { |
| constexpr char kName[] = "https.test"; |
| constexpr uint16_t kPort = 4567; |
| |
| DnsResponse response = BuildTestDnsResponse( |
| kName, dns_protocol::kTypeHttps, |
| {BuildTestHttpsServiceRecord(kName, /*priority=*/4, |
| /*service_name=*/".", |
| /*params=*/ |
| {BuildTestHttpsServiceAlpnParam({"foo"}), |
| BuildTestHttpsServicePortParam(kPort)})}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::HTTPS, |
| /*original_domain_name=*/kName, |
| /*request_port=*/kPort, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsOk()); |
| EXPECT_THAT(results.GetMetadatas(), |
| testing::Optional(testing::ElementsAre( |
| ExpectConnectionEndpointMetadata(testing::ElementsAre( |
| "foo", dns_protocol::kHttpsServiceDefaultAlpn))))); |
| EXPECT_THAT(results.https_record_compatibility(), |
| testing::Pointee(testing::ElementsAre(true))); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, IgnoresHttpsRecordWithMismatchingPort) { |
| constexpr char kName[] = "https.test"; |
| |
| DnsResponse response = BuildTestDnsResponse( |
| kName, dns_protocol::kTypeHttps, |
| {BuildTestHttpsServiceRecord(kName, /*priority=*/4, |
| /*service_name=*/".", |
| /*params=*/ |
| {BuildTestHttpsServiceAlpnParam({"ignored"}), |
| BuildTestHttpsServicePortParam(1003)}), |
| BuildTestHttpsServiceRecord(kName, /*priority=*/4, |
| /*service_name=*/".", |
| /*params=*/ |
| {BuildTestHttpsServiceAlpnParam({"foo"})})}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::HTTPS, |
| /*original_domain_name=*/kName, |
| /*request_port=*/55, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsOk()); |
| EXPECT_THAT(results.GetMetadatas(), |
| testing::Optional(testing::ElementsAre( |
| ExpectConnectionEndpointMetadata(testing::ElementsAre( |
| "foo", dns_protocol::kHttpsServiceDefaultAlpn))))); |
| EXPECT_THAT(results.https_record_compatibility(), |
| testing::Pointee(testing::ElementsAre(true, true))); |
| } |
| |
| // HTTPS records with "no-default-alpn" but also no "alpn" are not |
| // "self-consistent" and should be ignored. |
| TEST(DnsResponseResultExtractorTest, IgnoresHttpsRecordWithNoAlpn) { |
| constexpr char kName[] = "https.test"; |
| |
| DnsResponse response = BuildTestDnsResponse( |
| kName, dns_protocol::kTypeHttps, |
| {BuildTestHttpsServiceRecord( |
| kName, /*priority=*/4, |
| /*service_name=*/".", |
| /*params=*/ |
| {{dns_protocol::kHttpsServiceParamKeyNoDefaultAlpn, ""}}), |
| BuildTestHttpsServiceRecord(kName, /*priority=*/4, |
| /*service_name=*/".", |
| /*params=*/ |
| {BuildTestHttpsServiceAlpnParam({"foo"})})}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::HTTPS, |
| /*original_domain_name=*/kName, |
| /*request_port=*/55, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsOk()); |
| EXPECT_THAT(results.GetMetadatas(), |
| testing::Optional(testing::ElementsAre( |
| ExpectConnectionEndpointMetadata(testing::ElementsAre( |
| "foo", dns_protocol::kHttpsServiceDefaultAlpn))))); |
| EXPECT_THAT(results.https_record_compatibility(), |
| testing::Pointee(testing::ElementsAre(true, true))); |
| } |
| |
| // Expect the entire response to be ignored if all HTTPS records have the |
| // "no-default-alpn" param. |
| TEST(DnsResponseResultExtractorTest, |
| IgnoresHttpsResponseWithNoCompatibleDefaultAlpn) { |
| constexpr char kName[] = "https.test"; |
| constexpr uint16_t kMadeUpParamKey = 65500; // From the private-use block. |
| |
| DnsResponse response = BuildTestDnsResponse( |
| kName, dns_protocol::kTypeHttps, |
| {BuildTestHttpsServiceRecord( |
| kName, /*priority=*/4, |
| /*service_name=*/".", |
| /*params=*/ |
| {BuildTestHttpsServiceAlpnParam({"foo1"}), |
| {dns_protocol::kHttpsServiceParamKeyNoDefaultAlpn, ""}}), |
| BuildTestHttpsServiceRecord( |
| kName, /*priority=*/5, |
| /*service_name=*/".", |
| /*params=*/ |
| {BuildTestHttpsServiceAlpnParam({"foo2"}), |
| {dns_protocol::kHttpsServiceParamKeyNoDefaultAlpn, ""}}), |
| // Allows default ALPN, but ignored due to non-matching service name. |
| BuildTestHttpsServiceRecord(kName, /*priority=*/3, |
| /*service_name=*/"other.test", |
| /*params=*/{}), |
| // Allows default ALPN, but ignored due to incompatible param. |
| BuildTestHttpsServiceRecord( |
| kName, /*priority=*/6, |
| /*service_name=*/".", |
| /*params=*/ |
| {BuildTestHttpsServiceMandatoryParam({kMadeUpParamKey}), |
| {kMadeUpParamKey, "foo"}}), |
| // Allows default ALPN, but ignored due to mismatching port. |
| BuildTestHttpsServiceRecord( |
| kName, /*priority=*/10, |
| /*service_name=*/".", |
| /*params=*/{BuildTestHttpsServicePortParam(1005)})}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::HTTPS, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsError(ERR_NAME_NOT_RESOLVED)); |
| EXPECT_THAT(results.GetMetadatas(), testing::Optional(testing::IsEmpty())); |
| |
| // Expected to still output record compatibility for otherwise-ignored records |
| EXPECT_THAT( |
| results.https_record_compatibility(), |
| testing::Pointee(testing::ElementsAre(true, true, true, false, true))); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, ExtractsNxdomainHttpsResponses) { |
| constexpr char kName[] = "https.test"; |
| constexpr auto kTtl = base::Minutes(45); |
| |
| DnsResponse response = BuildTestDnsResponse( |
| kName, dns_protocol::kTypeHttps, /*answers=*/{}, |
| /*authority=*/ |
| {BuildTestDnsRecord(kName, dns_protocol::kTypeSOA, "fake rdata", kTtl)}, |
| /*additional=*/{}, dns_protocol::kRcodeNXDOMAIN); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::HTTPS, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsError(ERR_NAME_NOT_RESOLVED)); |
| EXPECT_THAT(results.GetMetadatas(), testing::Optional(testing::IsEmpty())); |
| EXPECT_THAT(results.https_record_compatibility(), |
| testing::Pointee(testing::IsEmpty())); |
| |
| ASSERT_TRUE(results.has_ttl()); |
| EXPECT_EQ(results.ttl(), kTtl); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, ExtractsNodataHttpsResponses) { |
| constexpr char kName[] = "https.test"; |
| constexpr auto kTtl = base::Hours(36); |
| |
| DnsResponse response = BuildTestDnsResponse( |
| kName, dns_protocol::kTypeHttps, /*answers=*/{}, |
| /*authority=*/ |
| {BuildTestDnsRecord(kName, dns_protocol::kTypeSOA, "fake rdata", kTtl)}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::HTTPS, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsError(ERR_NAME_NOT_RESOLVED)); |
| EXPECT_THAT(results.GetMetadatas(), testing::Optional(testing::IsEmpty())); |
| EXPECT_THAT(results.https_record_compatibility(), |
| testing::Pointee(testing::IsEmpty())); |
| |
| ASSERT_TRUE(results.has_ttl()); |
| EXPECT_EQ(results.ttl(), kTtl); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, RejectsMalformedHttpsRecord) { |
| constexpr char kName[] = "https.test"; |
| |
| DnsResponse response = BuildTestDnsResponse( |
| kName, dns_protocol::kTypeHttps, |
| {BuildTestDnsRecord(kName, dns_protocol::kTypeHttps, |
| "malformed rdata")} /* answers */); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::HTTPS, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kMalformedRecord); |
| |
| EXPECT_THAT(results.error(), test::IsError(ERR_DNS_MALFORMED_RESPONSE)); |
| EXPECT_FALSE(results.has_ttl()); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, RejectsWrongNameHttpsRecord) { |
| constexpr char kName[] = "https.test"; |
| |
| DnsResponse response = BuildTestDnsResponse( |
| kName, dns_protocol::kTypeHttps, |
| {BuildTestHttpsAliasRecord("different.test", "alias.test")}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::HTTPS, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kNameMismatch); |
| |
| EXPECT_THAT(results.error(), test::IsError(ERR_DNS_MALFORMED_RESPONSE)); |
| EXPECT_FALSE(results.has_ttl()); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, IgnoresWrongTypeHttpsResponses) { |
| constexpr char kName[] = "https.test"; |
| |
| DnsResponse response = BuildTestDnsResponse( |
| kName, dns_protocol::kTypeHttps, |
| {BuildTestAddressRecord(kName, IPAddress(1, 2, 3, 4))}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::HTTPS, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsError(ERR_NAME_NOT_RESOLVED)); |
| EXPECT_THAT(results.GetMetadatas(), testing::Optional(testing::IsEmpty())); |
| EXPECT_THAT(results.https_record_compatibility(), |
| testing::Pointee(testing::IsEmpty())); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, IgnoresAdditionalHttpsRecords) { |
| constexpr char kName[] = "https.test"; |
| constexpr auto kTtl = base::Days(5); |
| |
| // Give all records an "alpn" value to help validate that only the correct |
| // record is used. |
| DnsResponse response = BuildTestDnsResponse( |
| kName, dns_protocol::kTypeHttps, |
| /*answers=*/ |
| {BuildTestHttpsServiceRecord(kName, /*priority=*/5u, |
| /*service_name=*/".", |
| /*params=*/ |
| {BuildTestHttpsServiceAlpnParam({"foo1"})}, |
| kTtl)}, |
| /*authority=*/{}, |
| /*additional=*/ |
| {BuildTestHttpsServiceRecord(kName, /*priority=*/3u, /*service_name=*/".", |
| /*params=*/ |
| {BuildTestHttpsServiceAlpnParam({"foo2"})}, |
| base::Minutes(44)), |
| BuildTestHttpsServiceRecord(kName, /*priority=*/2u, /*service_name=*/".", |
| /*params=*/ |
| {BuildTestHttpsServiceAlpnParam({"foo3"})}, |
| base::Minutes(30))}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::HTTPS, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsOk()); |
| EXPECT_THAT(results.GetMetadatas(), |
| testing::Optional(testing::ElementsAre( |
| ExpectConnectionEndpointMetadata(testing::ElementsAre( |
| "foo1", dns_protocol::kHttpsServiceDefaultAlpn))))); |
| EXPECT_THAT(results.https_record_compatibility(), |
| testing::Pointee(testing::ElementsAre(true))); |
| |
| ASSERT_TRUE(results.has_ttl()); |
| EXPECT_EQ(results.ttl(), kTtl); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, IgnoresUnsolicitedHttpsRecords) { |
| constexpr char kName[] = "name.test"; |
| |
| DnsResponse response = BuildTestDnsResponse( |
| kName, dns_protocol::kTypeTXT, |
| {BuildTestDnsRecord(kName, dns_protocol::kTypeTXT, |
| "\003foo")} /* answers */, |
| {} /* authority */, |
| {BuildTestHttpsServiceRecord( |
| "https.test", /*priority=*/3u, /*service_name=*/".", |
| /*params=*/ |
| {BuildTestHttpsServiceAlpnParam({"foo2"})}, base::Minutes(44)), |
| BuildTestHttpsServiceRecord("https.test", /*priority=*/2u, |
| /*service_name=*/".", |
| /*params=*/ |
| {BuildTestHttpsServiceAlpnParam({"foo3"})}, |
| base::Minutes(30))} /* additional */); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::TXT, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsOk()); |
| EXPECT_THAT(results.text_records(), |
| testing::Optional(testing::ElementsAre("foo"))); |
| EXPECT_FALSE(results.GetMetadatas()); |
| EXPECT_FALSE(results.https_record_compatibility()); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, HandlesInOrderCnameChain) { |
| constexpr char kName[] = "first.test"; |
| |
| DnsResponse response = |
| BuildTestDnsResponse(kName, dns_protocol::kTypeTXT, |
| {BuildTestCnameRecord(kName, "second.test"), |
| BuildTestCnameRecord("second.test", "third.test"), |
| BuildTestCnameRecord("third.test", "fourth.test"), |
| BuildTestTextRecord("fourth.test", {"foo"}), |
| BuildTestTextRecord("fourth.test", {"bar"})}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::TXT, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsOk()); |
| EXPECT_THAT(results.text_records(), |
| testing::Optional(testing::UnorderedElementsAre("foo", "bar"))); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, HandlesInOrderCnameChainTypeA) { |
| constexpr char kName[] = "first.test"; |
| |
| const IPAddress kExpected(192, 168, 0, 1); |
| IPEndPoint expected_endpoint(kExpected, 0 /* port */); |
| |
| DnsResponse response = |
| BuildTestDnsResponse(kName, dns_protocol::kTypeA, |
| {BuildTestCnameRecord(kName, "second.test"), |
| BuildTestCnameRecord("second.test", "third.test"), |
| BuildTestCnameRecord("third.test", "fourth.test"), |
| BuildTestAddressRecord("fourth.test", kExpected)}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::A, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsOk()); |
| EXPECT_THAT(results.GetEndpoints(), |
| testing::Optional(testing::ElementsAre(ExpectEndpointResult( |
| testing::ElementsAre(expected_endpoint))))); |
| |
| EXPECT_THAT(results.aliases(), |
| testing::Pointee(testing::UnorderedElementsAre( |
| "fourth.test", "third.test", "second.test", kName))); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, HandlesReverseOrderCnameChain) { |
| constexpr char kName[] = "first.test"; |
| |
| DnsResponse response = |
| BuildTestDnsResponse(kName, dns_protocol::kTypeTXT, |
| {BuildTestTextRecord("fourth.test", {"foo"}), |
| BuildTestCnameRecord("third.test", "fourth.test"), |
| BuildTestCnameRecord("second.test", "third.test"), |
| BuildTestCnameRecord(kName, "second.test")}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::TXT, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsOk()); |
| EXPECT_THAT(results.text_records(), |
| testing::Optional(testing::ElementsAre("foo"))); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, HandlesReverseOrderCnameChainTypeA) { |
| constexpr char kName[] = "first.test"; |
| |
| const IPAddress kExpected(192, 168, 0, 1); |
| IPEndPoint expected_endpoint(kExpected, 0 /* port */); |
| |
| DnsResponse response = |
| BuildTestDnsResponse(kName, dns_protocol::kTypeA, |
| {BuildTestAddressRecord("fourth.test", kExpected), |
| BuildTestCnameRecord("third.test", "fourth.test"), |
| BuildTestCnameRecord("second.test", "third.test"), |
| BuildTestCnameRecord(kName, "second.test")}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::A, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsOk()); |
| EXPECT_THAT(results.GetEndpoints(), |
| testing::Optional(testing::ElementsAre(ExpectEndpointResult( |
| testing::ElementsAre(expected_endpoint))))); |
| |
| EXPECT_THAT(results.aliases(), |
| testing::Pointee(testing::UnorderedElementsAre( |
| "fourth.test", "third.test", "second.test", kName))); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, HandlesArbitraryOrderCnameChain) { |
| constexpr char kName[] = "first.test"; |
| |
| DnsResponse response = |
| BuildTestDnsResponse(kName, dns_protocol::kTypeTXT, |
| {BuildTestCnameRecord("second.test", "third.test"), |
| BuildTestTextRecord("fourth.test", {"foo"}), |
| BuildTestCnameRecord("third.test", "fourth.test"), |
| BuildTestCnameRecord(kName, "second.test")}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::TXT, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsOk()); |
| EXPECT_THAT(results.text_records(), |
| testing::Optional(testing::ElementsAre("foo"))); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, HandlesArbitraryOrderCnameChainTypeA) { |
| constexpr char kName[] = "first.test"; |
| |
| const IPAddress kExpected(192, 168, 0, 1); |
| IPEndPoint expected_endpoint(kExpected, 0 /* port */); |
| |
| // Alias names are chosen so that the chain order is not in alphabetical |
| // order. |
| DnsResponse response = |
| BuildTestDnsResponse(kName, dns_protocol::kTypeA, |
| {BuildTestCnameRecord("qsecond.test", "athird.test"), |
| BuildTestAddressRecord("zfourth.test", kExpected), |
| BuildTestCnameRecord("athird.test", "zfourth.test"), |
| BuildTestCnameRecord(kName, "qsecond.test")}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::A, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsOk()); |
| EXPECT_THAT(results.GetEndpoints(), |
| testing::Optional(testing::ElementsAre(ExpectEndpointResult( |
| testing::ElementsAre(expected_endpoint))))); |
| |
| EXPECT_THAT(results.aliases(), |
| testing::Pointee(testing::UnorderedElementsAre( |
| "zfourth.test", "athird.test", "qsecond.test", kName))); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, IgnoresNonResultTypesMixedWithCnameChain) { |
| constexpr char kName[] = "first.test"; |
| |
| DnsResponse response = BuildTestDnsResponse( |
| kName, dns_protocol::kTypeTXT, |
| {BuildTestCnameRecord("second.test", "third.test"), |
| BuildTestTextRecord("fourth.test", {"foo"}), |
| BuildTestCnameRecord("third.test", "fourth.test"), |
| BuildTestAddressRecord("third.test", IPAddress(1, 2, 3, 4)), |
| BuildTestCnameRecord(kName, "second.test"), |
| BuildTestAddressRecord("fourth.test", IPAddress(2, 3, 4, 5))}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::TXT, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsOk()); |
| EXPECT_THAT(results.text_records(), |
| testing::Optional(testing::ElementsAre("foo"))); |
| EXPECT_FALSE(results.GetEndpoints()); |
| EXPECT_FALSE(results.aliases()); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, |
| IgnoresNonResultTypesMixedWithCnameChainTypeA) { |
| constexpr char kName[] = "first.test"; |
| |
| const IPAddress kExpected(192, 168, 0, 1); |
| IPEndPoint expected_endpoint(kExpected, 0 /* port */); |
| |
| DnsResponse response = |
| BuildTestDnsResponse(kName, dns_protocol::kTypeA, |
| {BuildTestCnameRecord("second.test", "third.test"), |
| BuildTestTextRecord("fourth.test", {"foo"}), |
| BuildTestCnameRecord("third.test", "fourth.test"), |
| BuildTestCnameRecord(kName, "second.test"), |
| BuildTestAddressRecord("fourth.test", kExpected)}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::A, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsOk()); |
| EXPECT_FALSE(results.text_records()); |
| EXPECT_THAT(results.GetEndpoints(), |
| testing::Optional(testing::ElementsAre(ExpectEndpointResult( |
| testing::ElementsAre(expected_endpoint))))); |
| |
| EXPECT_THAT(results.aliases(), |
| testing::Pointee(testing::UnorderedElementsAre( |
| "fourth.test", "third.test", "second.test", kName))); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, HandlesCnameChainWithoutResult) { |
| constexpr char kName[] = "first.test"; |
| |
| DnsResponse response = |
| BuildTestDnsResponse(kName, dns_protocol::kTypeTXT, |
| {BuildTestCnameRecord("second.test", "third.test"), |
| BuildTestCnameRecord("third.test", "fourth.test"), |
| BuildTestCnameRecord(kName, "second.test")}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::TXT, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsError(ERR_NAME_NOT_RESOLVED)); |
| EXPECT_THAT(results.text_records(), testing::Optional(testing::IsEmpty())); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, HandlesCnameChainWithoutResultTypeA) { |
| constexpr char kName[] = "first.test"; |
| |
| DnsResponse response = |
| BuildTestDnsResponse(kName, dns_protocol::kTypeA, |
| {BuildTestCnameRecord("second.test", "third.test"), |
| BuildTestCnameRecord("third.test", "fourth.test"), |
| BuildTestCnameRecord(kName, "second.test")}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::A, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsError(ERR_NAME_NOT_RESOLVED)); |
| EXPECT_THAT(results.GetEndpoints(), testing::Optional(testing::IsEmpty())); |
| |
| EXPECT_THAT(results.aliases(), |
| testing::Pointee(testing::UnorderedElementsAre( |
| "fourth.test", "third.test", "second.test", kName))); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, RejectsCnameChainWithLoop) { |
| constexpr char kName[] = "first.test"; |
| |
| DnsResponse response = |
| BuildTestDnsResponse(kName, dns_protocol::kTypeTXT, |
| {BuildTestCnameRecord("second.test", "third.test"), |
| BuildTestTextRecord("third.test", {"foo"}), |
| BuildTestCnameRecord("third.test", "second.test"), |
| BuildTestCnameRecord(kName, "second.test")}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::TXT, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kBadAliasChain); |
| |
| EXPECT_THAT(results.error(), test::IsError(ERR_DNS_MALFORMED_RESPONSE)); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, RejectsCnameChainWithLoopToBeginning) { |
| constexpr char kName[] = "first.test"; |
| |
| DnsResponse response = |
| BuildTestDnsResponse(kName, dns_protocol::kTypeTXT, |
| {BuildTestCnameRecord("second.test", "third.test"), |
| BuildTestTextRecord("third.test", {"foo"}), |
| BuildTestCnameRecord("third.test", "first.test"), |
| BuildTestCnameRecord(kName, "second.test")}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::TXT, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kBadAliasChain); |
| |
| EXPECT_THAT(results.error(), test::IsError(ERR_DNS_MALFORMED_RESPONSE)); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, |
| RejectsCnameChainWithLoopToBeginningWithoutResult) { |
| constexpr char kName[] = "first.test"; |
| |
| DnsResponse response = |
| BuildTestDnsResponse(kName, dns_protocol::kTypeTXT, |
| {BuildTestCnameRecord("second.test", "third.test"), |
| BuildTestCnameRecord("third.test", "first.test"), |
| BuildTestCnameRecord(kName, "second.test")}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::TXT, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kBadAliasChain); |
| |
| EXPECT_THAT(results.error(), test::IsError(ERR_DNS_MALFORMED_RESPONSE)); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, RejectsCnameChainWithWrongStart) { |
| constexpr char kName[] = "test.test"; |
| |
| DnsResponse response = |
| BuildTestDnsResponse(kName, dns_protocol::kTypeTXT, |
| {BuildTestCnameRecord("second.test", "third.test"), |
| BuildTestTextRecord("fourth.test", {"foo"}), |
| BuildTestCnameRecord("third.test", "fourth.test"), |
| BuildTestCnameRecord("first.test", "second.test")}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::TXT, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kBadAliasChain); |
| |
| EXPECT_THAT(results.error(), test::IsError(ERR_DNS_MALFORMED_RESPONSE)); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, RejectsCnameChainWithWrongResultName) { |
| constexpr char kName[] = "first.test"; |
| |
| DnsResponse response = |
| BuildTestDnsResponse(kName, dns_protocol::kTypeTXT, |
| {BuildTestCnameRecord("second.test", "third.test"), |
| BuildTestTextRecord("third.test", {"foo"}), |
| BuildTestCnameRecord("third.test", "fourth.test"), |
| BuildTestCnameRecord(kName, "second.test")}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::TXT, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kNameMismatch); |
| |
| EXPECT_THAT(results.error(), test::IsError(ERR_DNS_MALFORMED_RESPONSE)); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, RejectsCnameSharedWithResult) { |
| constexpr char kName[] = "first.test"; |
| |
| DnsResponse response = |
| BuildTestDnsResponse(kName, dns_protocol::kTypeTXT, |
| {BuildTestCnameRecord("second.test", "third.test"), |
| BuildTestTextRecord(kName, {"foo"}), |
| BuildTestCnameRecord("third.test", "fourth.test"), |
| BuildTestCnameRecord(kName, "second.test")}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::TXT, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kNameMismatch); |
| |
| EXPECT_THAT(results.error(), test::IsError(ERR_DNS_MALFORMED_RESPONSE)); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, RejectsDisjointCnameChain) { |
| constexpr char kName[] = "first.test"; |
| |
| DnsResponse response = BuildTestDnsResponse( |
| kName, dns_protocol::kTypeTXT, |
| {BuildTestCnameRecord("second.test", "third.test"), |
| BuildTestTextRecord("fourth.test", {"foo"}), |
| BuildTestCnameRecord("third.test", "fourth.test"), |
| BuildTestCnameRecord("other1.test", "other2.test"), |
| BuildTestCnameRecord(kName, "second.test"), |
| BuildTestCnameRecord("other2.test", "other3.test")}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::TXT, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kBadAliasChain); |
| |
| EXPECT_THAT(results.error(), test::IsError(ERR_DNS_MALFORMED_RESPONSE)); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, RejectsDoubledCnames) { |
| constexpr char kName[] = "first.test"; |
| |
| DnsResponse response = |
| BuildTestDnsResponse(kName, dns_protocol::kTypeTXT, |
| {BuildTestCnameRecord("second.test", "third.test"), |
| BuildTestTextRecord("fourth.test", {"foo"}), |
| BuildTestCnameRecord("third.test", "fourth.test"), |
| BuildTestCnameRecord("third.test", "fifth.test"), |
| BuildTestCnameRecord(kName, "second.test")}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::TXT, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kMultipleCnames); |
| |
| EXPECT_THAT(results.error(), test::IsError(ERR_DNS_MALFORMED_RESPONSE)); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, IgnoresTtlFromNonResultType) { |
| constexpr char kName[] = "name.test"; |
| constexpr base::TimeDelta kMinTtl = base::Minutes(4); |
| |
| DnsResponse response = BuildTestDnsResponse( |
| kName, dns_protocol::kTypeTXT, |
| {BuildTestTextRecord(kName, {"foo"}, base::Hours(3)), |
| BuildTestTextRecord(kName, {"bar"}, kMinTtl), |
| BuildTestAddressRecord(kName, IPAddress(1, 2, 3, 4), base::Seconds(2)), |
| BuildTestTextRecord(kName, {"baz"}, base::Minutes(15))}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::TXT, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| ASSERT_TRUE(results.has_ttl()); |
| EXPECT_EQ(results.ttl(), kMinTtl); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, ExtractsTtlFromCname) { |
| constexpr char kName[] = "name.test"; |
| constexpr char kAlias[] = "alias.test"; |
| constexpr base::TimeDelta kMinTtl = base::Minutes(4); |
| |
| DnsResponse response = BuildTestDnsResponse( |
| "name.test", dns_protocol::kTypeTXT, |
| {BuildTestTextRecord(kAlias, {"foo"}, base::Hours(3)), |
| BuildTestTextRecord(kAlias, {"bar"}, base::Hours(2)), |
| BuildTestTextRecord(kAlias, {"baz"}, base::Minutes(15)), |
| BuildTestCnameRecord(kName, kAlias, kMinTtl)}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::TXT, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| ASSERT_TRUE(results.has_ttl()); |
| EXPECT_EQ(results.ttl(), kMinTtl); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, ValidatesAliasNames) { |
| constexpr char kName[] = "first.test"; |
| |
| const IPAddress kExpected(192, 168, 0, 1); |
| IPEndPoint expected_endpoint(kExpected, 0 /* port */); |
| |
| DnsResponse response = |
| BuildTestDnsResponse(kName, dns_protocol::kTypeA, |
| {BuildTestCnameRecord(kName, "second.test"), |
| BuildTestCnameRecord("second.test", "localhost"), |
| BuildTestCnameRecord("localhost", "fourth.test"), |
| BuildTestAddressRecord("fourth.test", kExpected)}); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::A, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsOk()); |
| EXPECT_THAT(results.GetEndpoints(), |
| testing::Optional(testing::ElementsAre(ExpectEndpointResult( |
| testing::ElementsAre(expected_endpoint))))); |
| |
| // Expect "localhost" to be validated out of the alias list. |
| EXPECT_THAT(results.aliases(), testing::Pointee(testing::UnorderedElementsAre( |
| "fourth.test", "second.test", kName))); |
| } |
| |
| TEST(DnsResponseResultExtractorTest, CanonicalizesAliasNames) { |
| const IPAddress kExpected(192, 168, 0, 1); |
| constexpr char kName[] = "address.test"; |
| |
| DnsResponse response = |
| BuildTestDnsAddressResponseWithCname(kName, kExpected, "ALIAS.test."); |
| DnsResponseResultExtractor extractor(&response); |
| |
| HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN); |
| EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::A, |
| /*original_domain_name=*/kName, |
| /*request_port=*/0, &results), |
| DnsResponseResultExtractor::ExtractionError::kOk); |
| |
| EXPECT_THAT(results.error(), test::IsOk()); |
| IPEndPoint expected_endpoint(kExpected, 0 /* port */); |
| EXPECT_THAT(results.GetEndpoints(), |
| testing::Optional(testing::ElementsAre(ExpectEndpointResult( |
| testing::ElementsAre(expected_endpoint))))); |
| EXPECT_THAT(results.aliases(), testing::Pointee(testing::UnorderedElementsAre( |
| kName, "alias.test"))); |
| } |
| |
| } // namespace |
| } // namespace net |