blob: 6a0e32fd60cdc63516f118a9a6de6570aa3b1d43 [file] [log] [blame]
// 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/optional.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/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"
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, &results),
DnsResponseResultExtractor::ExtractionError::kOk);
EXPECT_THAT(results.error(), test::IsOk());
IPEndPoint expected_endpoint(kExpected, 0 /* port */);
ASSERT_TRUE(results.addresses());
EXPECT_THAT(results.addresses().value().endpoints(),
testing::ElementsAre(expected_endpoint));
EXPECT_EQ(results.addresses().value().GetCanonicalName(), kName);
EXPECT_THAT(results.addresses().value().dns_aliases(),
testing::ElementsAre(kName));
}
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, &results),
DnsResponseResultExtractor::ExtractionError::kOk);
EXPECT_THAT(results.error(), test::IsOk());
IPEndPoint expected_endpoint(expected, 0 /* port */);
ASSERT_TRUE(results.addresses());
EXPECT_THAT(results.addresses().value().endpoints(),
testing::ElementsAre(expected_endpoint));
EXPECT_EQ(results.addresses().value().GetCanonicalName(), kName);
EXPECT_THAT(results.addresses().value().dns_aliases(),
testing::ElementsAre(kName));
}
TEST(DnsResponseResultExtractorTest, ExtractsSingleARecordWithCname) {
const IPAddress kExpected(192, 168, 0, 1);
constexpr char kCanonicalName[] = "alias.test";
DnsResponse response = BuildTestDnsAddressResponseWithCname(
"address.test", kExpected, kCanonicalName);
DnsResponseResultExtractor extractor(&response);
HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN);
EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::A, &results),
DnsResponseResultExtractor::ExtractionError::kOk);
EXPECT_THAT(results.error(), test::IsOk());
IPEndPoint expected_endpoint(kExpected, 0 /* port */);
ASSERT_TRUE(results.addresses());
EXPECT_THAT(results.addresses().value().endpoints(),
testing::ElementsAre(expected_endpoint));
EXPECT_EQ(results.addresses().value().GetCanonicalName(), kCanonicalName);
EXPECT_THAT(results.addresses().value().dns_aliases(),
testing::ElementsAre(kCanonicalName, "address.test"));
}
TEST(DnsResponseResultExtractorTest, ExtractsARecordsWithCname) {
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("addresses.test", "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, &results),
DnsResponseResultExtractor::ExtractionError::kOk);
EXPECT_THAT(results.error(), test::IsOk());
ASSERT_TRUE(results.addresses());
EXPECT_THAT(results.addresses().value().endpoints(),
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_EQ(results.addresses().value().GetCanonicalName(), "alias.test");
EXPECT_THAT(results.addresses().value().dns_aliases(),
testing::ElementsAre("alias.test", "addresses.test"));
}
TEST(DnsResponseResultExtractorTest, ExtractsNxdomainAResponses) {
DnsResponse response = BuildTestDnsResponse(
"address.test", dns_protocol::kTypeA, {} /* answers */,
{BuildTestDnsRecord("address.test", dns_protocol::kTypeSOA,
"fake rdata")} /* authority */,
{} /* additional */, dns_protocol::kRcodeNOERROR);
DnsResponseResultExtractor extractor(&response);
HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN);
EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::A, &results),
DnsResponseResultExtractor::ExtractionError::kOk);
EXPECT_THAT(results.error(), test::IsError(ERR_NAME_NOT_RESOLVED));
ASSERT_TRUE(results.addresses());
EXPECT_TRUE(results.addresses().value().empty());
EXPECT_EQ(results.addresses().value().GetCanonicalName(), "");
EXPECT_TRUE(results.addresses().value().dns_aliases().empty());
}
TEST(DnsResponseResultExtractorTest, ExtractsNodataAResponses) {
DnsResponse response = BuildTestDnsResponse(
"address.test", dns_protocol::kTypeA, {} /* answers */);
DnsResponseResultExtractor extractor(&response);
HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN);
EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::A, &results),
DnsResponseResultExtractor::ExtractionError::kOk);
EXPECT_THAT(results.error(), test::IsError(ERR_NAME_NOT_RESOLVED));
ASSERT_TRUE(results.addresses());
EXPECT_TRUE(results.addresses().value().empty());
EXPECT_EQ(results.addresses().value().GetCanonicalName(), "");
EXPECT_TRUE(results.addresses().value().dns_aliases().empty());
}
TEST(DnsResponseResultExtractorTest, RejectsMalformedARecord) {
DnsResponse response = BuildTestDnsResponse(
"address.test", dns_protocol::kTypeA,
{BuildTestDnsRecord("address.test", dns_protocol::kTypeA,
"malformed rdata")} /* answers */);
DnsResponseResultExtractor extractor(&response);
HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN);
EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::A, &results),
DnsResponseResultExtractor::ExtractionError::kMalformedRecord);
EXPECT_THAT(results.error(), test::IsError(ERR_DNS_MALFORMED_RESPONSE));
}
TEST(DnsResponseResultExtractorTest, RejectsWrongNameARecord) {
DnsResponse response = BuildTestDnsAddressResponse(
"address.test", 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, &results),
DnsResponseResultExtractor::ExtractionError::kNameMismatch);
EXPECT_THAT(results.error(), test::IsError(ERR_DNS_MALFORMED_RESPONSE));
}
TEST(DnsResponseResultExtractorTest, IgnoresWrongTypeRecordsInAResponse) {
DnsResponse response = BuildTestDnsResponse(
"address.test", 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, &results),
DnsResponseResultExtractor::ExtractionError::kOk);
EXPECT_THAT(results.error(), test::IsError(ERR_NAME_NOT_RESOLVED));
ASSERT_TRUE(results.addresses());
EXPECT_TRUE(results.addresses().value().empty());
EXPECT_EQ(results.addresses().value().GetCanonicalName(), "");
EXPECT_TRUE(results.addresses().value().dns_aliases().empty());
}
TEST(DnsResponseResultExtractorTest, IgnoresWrongTypeRecordsMixedWithARecords) {
constexpr char kName[] = "address.test";
const IPAddress kExpected(8, 8, 8, 8);
DnsResponse response = BuildTestDnsResponse(
kName, dns_protocol::kTypeA,
{BuildTestTextRecord(kName, {"foo"} /* text_strings */),
BuildTestAddressRecord(kName, kExpected)});
DnsResponseResultExtractor extractor(&response);
HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN);
EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::A, &results),
DnsResponseResultExtractor::ExtractionError::kOk);
EXPECT_THAT(results.error(), test::IsOk());
ASSERT_TRUE(results.addresses());
IPEndPoint expected_endpoint(kExpected, 0 /* port */);
EXPECT_THAT(results.addresses().value().endpoints(),
testing::ElementsAre(expected_endpoint));
EXPECT_EQ(results.addresses().value().GetCanonicalName(), kName);
EXPECT_THAT(results.addresses().value().dns_aliases(),
testing::ElementsAre(kName));
}
TEST(DnsResponseResultExtractorTest, ExtractsMinATtl) {
constexpr char kName[] = "name.test";
constexpr base::TimeDelta kMinTtl = base::TimeDelta::FromMinutes(4);
DnsResponse response = BuildTestDnsResponse(
kName, dns_protocol::kTypeA,
{BuildTestAddressRecord(kName, IPAddress(1, 2, 3, 4),
base::TimeDelta::FromHours(3)),
BuildTestAddressRecord(kName, IPAddress(2, 3, 4, 5), kMinTtl),
BuildTestAddressRecord(kName, IPAddress(3, 4, 5, 6),
base::TimeDelta::FromMinutes(15))});
DnsResponseResultExtractor extractor(&response);
HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN);
EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::A, &results),
DnsResponseResultExtractor::ExtractionError::kOk);
ASSERT_TRUE(results.has_ttl());
EXPECT_EQ(results.ttl(), kMinTtl);
}
TEST(DnsResponseResultExtractorTest, ExtractsTxtResponses) {
// 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("name.test", std::move(text_records));
DnsResponseResultExtractor extractor(&response);
HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN);
EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::TXT, &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) {
DnsResponse response = BuildTestDnsResponse(
"name.test", dns_protocol::kTypeTXT, {} /* answers */,
{BuildTestDnsRecord("name.test", dns_protocol::kTypeSOA,
"fake rdata")} /* authority */,
{} /* additional */, dns_protocol::kRcodeNOERROR);
DnsResponseResultExtractor extractor(&response);
HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN);
EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::TXT, &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, ExtractsNodataTxtResponses) {
DnsResponse response =
BuildTestDnsTextResponse("name.test", {} /* text_records */);
DnsResponseResultExtractor extractor(&response);
HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN);
EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::TXT, &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, RejectsMalformedTxtRecord) {
DnsResponse response = BuildTestDnsResponse(
"name.test", dns_protocol::kTypeTXT,
{BuildTestDnsRecord("name.test", dns_protocol::kTypeTXT,
"malformed rdata")} /* answers */);
DnsResponseResultExtractor extractor(&response);
HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN);
EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::TXT, &results),
DnsResponseResultExtractor::ExtractionError::kMalformedRecord);
EXPECT_THAT(results.error(), test::IsError(ERR_DNS_MALFORMED_RESPONSE));
}
TEST(DnsResponseResultExtractorTest, RejectsWrongNameTxtRecord) {
DnsResponse response =
BuildTestDnsTextResponse("name.test", {{"foo"}}, "different.test");
DnsResponseResultExtractor extractor(&response);
HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN);
EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::TXT, &results),
DnsResponseResultExtractor::ExtractionError::kNameMismatch);
EXPECT_THAT(results.error(), test::IsError(ERR_DNS_MALFORMED_RESPONSE));
}
TEST(DnsResponseResultExtractorTest, IgnoresWrongTypeTxtResponses) {
DnsResponse response = BuildTestDnsResponse(
"name.test", dns_protocol::kTypeTXT,
{BuildTestAddressRecord("name.test", IPAddress(1, 2, 3, 4))});
DnsResponseResultExtractor extractor(&response);
HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN);
EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::TXT, &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, ExtractsMinTxtTtl) {
constexpr char kName[] = "name.test";
constexpr base::TimeDelta kMinTtl = base::TimeDelta::FromMinutes(4);
DnsResponse response = BuildTestDnsResponse(
kName, dns_protocol::kTypeTXT,
{BuildTestTextRecord(kName, {"foo"}, base::TimeDelta::FromHours(3)),
BuildTestTextRecord(kName, {"bar"}, kMinTtl),
BuildTestTextRecord(kName, {"baz"}, base::TimeDelta::FromMinutes(15))});
DnsResponseResultExtractor extractor(&response);
HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN);
EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::TXT, &results),
DnsResponseResultExtractor::ExtractionError::kOk);
ASSERT_TRUE(results.has_ttl());
EXPECT_EQ(results.ttl(), kMinTtl);
}
TEST(DnsResponseResultExtractorTest, ExtractsPtrResponses) {
DnsResponse response =
BuildTestDnsPointerResponse("name.test", {"foo.com", "bar.com"});
DnsResponseResultExtractor extractor(&response);
HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN);
EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::PTR, &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) {
DnsResponse response = BuildTestDnsResponse(
"name.test", dns_protocol::kTypePTR, {} /* answers */,
{BuildTestDnsRecord("name.test", dns_protocol::kTypeSOA,
"fake rdata")} /* authority */,
{} /* additional */, dns_protocol::kRcodeNOERROR);
DnsResponseResultExtractor extractor(&response);
HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN);
EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::PTR, &results),
DnsResponseResultExtractor::ExtractionError::kOk);
EXPECT_THAT(results.error(), test::IsError(ERR_NAME_NOT_RESOLVED));
EXPECT_THAT(results.hostnames(), testing::Optional(testing::IsEmpty()));
}
TEST(DnsResponseResultExtractorTest, ExtractsNodataPtrResponses) {
DnsResponse response =
BuildTestDnsPointerResponse("name.test", {} /* pointer_names */);
DnsResponseResultExtractor extractor(&response);
HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN);
EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::PTR, &results),
DnsResponseResultExtractor::ExtractionError::kOk);
EXPECT_THAT(results.error(), test::IsError(ERR_NAME_NOT_RESOLVED));
EXPECT_THAT(results.hostnames(), testing::Optional(testing::IsEmpty()));
}
TEST(DnsResponseResultExtractorTest, RejectsMalformedPtrRecord) {
DnsResponse response = BuildTestDnsResponse(
"name.test", dns_protocol::kTypePTR,
{BuildTestDnsRecord("name.test", dns_protocol::kTypePTR,
"malformed rdata")} /* answers */);
DnsResponseResultExtractor extractor(&response);
HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN);
EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::PTR, &results),
DnsResponseResultExtractor::ExtractionError::kMalformedRecord);
EXPECT_THAT(results.error(), test::IsError(ERR_DNS_MALFORMED_RESPONSE));
}
TEST(DnsResponseResultExtractorTest, RejectsWrongNamePtrRecord) {
DnsResponse response = BuildTestDnsPointerResponse(
"name.test", {"foo.com", "bar.com"}, "different.test");
DnsResponseResultExtractor extractor(&response);
HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN);
EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::PTR, &results),
DnsResponseResultExtractor::ExtractionError::kNameMismatch);
EXPECT_THAT(results.error(), test::IsError(ERR_DNS_MALFORMED_RESPONSE));
}
TEST(DnsResponseResultExtractorTest, IgnoresWrongTypePtrResponses) {
DnsResponse response = BuildTestDnsResponse(
"name.test", dns_protocol::kTypePTR,
{BuildTestAddressRecord("name.test", IPAddress(1, 2, 3, 4))});
DnsResponseResultExtractor extractor(&response);
HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN);
EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::PTR, &results),
DnsResponseResultExtractor::ExtractionError::kOk);
EXPECT_THAT(results.error(), test::IsError(ERR_NAME_NOT_RESOLVED));
EXPECT_THAT(results.hostnames(), testing::Optional(testing::IsEmpty()));
}
TEST(DnsResponseResultExtractorTest, ExtractsSrvResponses) {
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(
"name.test", {kRecord1, kRecord2, kRecord3, kRecord4});
DnsResponseResultExtractor extractor(&response);
HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN);
EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::SRV, &results),
DnsResponseResultExtractor::ExtractionError::kOk);
EXPECT_THAT(results.error(), test::IsOk());
// Expect ordered by priority, and random within a priority.
base::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) {
const TestServiceRecord kRecord1 = {5, 0, 80, "bar.com"};
const TestServiceRecord kRecord2 = {5, 0, 5, "google.com"};
DnsResponse response =
BuildTestDnsServiceResponse("name.test", {kRecord1, kRecord2});
DnsResponseResultExtractor extractor(&response);
HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN);
EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::SRV, &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) {
DnsResponse response = BuildTestDnsResponse(
"name.test", dns_protocol::kTypeSRV, {} /* answers */,
{BuildTestDnsRecord("name.test", dns_protocol::kTypeSOA,
"fake rdata")} /* authority */,
{} /* additional */, dns_protocol::kRcodeNOERROR);
DnsResponseResultExtractor extractor(&response);
HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN);
EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::SRV, &results),
DnsResponseResultExtractor::ExtractionError::kOk);
EXPECT_THAT(results.error(), test::IsError(ERR_NAME_NOT_RESOLVED));
EXPECT_THAT(results.hostnames(), testing::Optional(testing::IsEmpty()));
}
TEST(DnsResponseResultExtractorTest, ExtractsNodataSrvResponses) {
DnsResponse response =
BuildTestDnsServiceResponse("name.test", {} /* service_records */);
DnsResponseResultExtractor extractor(&response);
HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN);
EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::SRV, &results),
DnsResponseResultExtractor::ExtractionError::kOk);
EXPECT_THAT(results.error(), test::IsError(ERR_NAME_NOT_RESOLVED));
EXPECT_THAT(results.hostnames(), testing::Optional(testing::IsEmpty()));
}
TEST(DnsResponseResultExtractorTest, RejectsMalformedSrvRecord) {
DnsResponse response = BuildTestDnsResponse(
"name.test", dns_protocol::kTypeSRV,
{BuildTestDnsRecord("name.test", dns_protocol::kTypeSRV,
"malformed rdata")} /* answers */);
DnsResponseResultExtractor extractor(&response);
HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN);
EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::SRV, &results),
DnsResponseResultExtractor::ExtractionError::kMalformedRecord);
EXPECT_THAT(results.error(), test::IsError(ERR_DNS_MALFORMED_RESPONSE));
}
TEST(DnsResponseResultExtractorTest, RejectsWrongNameSrvRecord) {
const TestServiceRecord kRecord = {2, 3, 1223, "foo.com"};
DnsResponse response =
BuildTestDnsServiceResponse("name.test", {kRecord}, "different.test");
DnsResponseResultExtractor extractor(&response);
HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN);
EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::SRV, &results),
DnsResponseResultExtractor::ExtractionError::kNameMismatch);
EXPECT_THAT(results.error(), test::IsError(ERR_DNS_MALFORMED_RESPONSE));
}
TEST(DnsResponseResultExtractorTest, IgnoresWrongTypeSrvResponses) {
DnsResponse response = BuildTestDnsResponse(
"name.test", dns_protocol::kTypeSRV,
{BuildTestAddressRecord("name.test", IPAddress(1, 2, 3, 4))});
DnsResponseResultExtractor extractor(&response);
HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN);
EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::SRV, &results),
DnsResponseResultExtractor::ExtractionError::kOk);
EXPECT_THAT(results.error(), test::IsError(ERR_NAME_NOT_RESOLVED));
EXPECT_THAT(results.hostnames(), testing::Optional(testing::IsEmpty()));
}
TEST(DnsResponseResultExtractorTest, ExtractsHttpsResponses) {
DnsResponse response = BuildTestDnsResponse(
"https.test", dns_protocol::kTypeHttps,
{BuildTestHttpsAliasRecord("https.test", "alias.test")});
DnsResponseResultExtractor extractor(&response);
HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN);
EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::HTTPS, &results),
DnsResponseResultExtractor::ExtractionError::kOk);
// Experimental type, so does not affect overall result.
EXPECT_THAT(results.error(), test::IsError(ERR_NAME_NOT_RESOLVED));
EXPECT_THAT(results.experimental_results(),
testing::Optional(testing::ElementsAre(true)));
}
TEST(DnsResponseResultExtractorTest, ExtractsNxdomainHttpsResponses) {
DnsResponse response = BuildTestDnsResponse(
"https.test", dns_protocol::kTypeHttps, {} /* answers */,
{BuildTestDnsRecord("name.test", dns_protocol::kTypeSOA,
"fake rdata")} /* authority */,
{} /* additional */, dns_protocol::kRcodeNOERROR);
DnsResponseResultExtractor extractor(&response);
HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN);
EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::HTTPS, &results),
DnsResponseResultExtractor::ExtractionError::kOk);
EXPECT_THAT(results.error(), test::IsError(ERR_NAME_NOT_RESOLVED));
EXPECT_THAT(results.experimental_results(),
testing::Optional(testing::IsEmpty()));
}
TEST(DnsResponseResultExtractorTest, ExtractsNodataHttpsResponses) {
DnsResponse response = BuildTestDnsResponse(
"https.test", dns_protocol::kTypeHttps, {} /* answers */);
DnsResponseResultExtractor extractor(&response);
HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN);
EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::HTTPS, &results),
DnsResponseResultExtractor::ExtractionError::kOk);
EXPECT_THAT(results.error(), test::IsError(ERR_NAME_NOT_RESOLVED));
EXPECT_THAT(results.experimental_results(),
testing::Optional(testing::IsEmpty()));
}
TEST(DnsResponseResultExtractorTest, RecognizesMalformedHttpsRecord) {
DnsResponse response = BuildTestDnsResponse(
"https.test", dns_protocol::kTypeHttps,
{BuildTestDnsRecord("https.test", dns_protocol::kTypeHttps,
"malformed rdata")} /* answers */);
DnsResponseResultExtractor extractor(&response);
HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN);
EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::HTTPS, &results),
DnsResponseResultExtractor::ExtractionError::kOk);
EXPECT_THAT(results.error(), test::IsError(ERR_NAME_NOT_RESOLVED));
EXPECT_THAT(results.experimental_results(),
testing::Optional(testing::ElementsAre(false)));
}
TEST(DnsResponseResultExtractorTest, IgnoresWrongNameHttpsRecord) {
DnsResponse response = BuildTestDnsResponse(
"https.test", 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, &results),
DnsResponseResultExtractor::ExtractionError::kOk);
EXPECT_THAT(results.error(), test::IsError(ERR_NAME_NOT_RESOLVED));
HostCache::Entry default_results =
DnsResponseResultExtractor::CreateEmptyResult(DnsQueryType::HTTPS);
EXPECT_EQ(results.error(), default_results.error());
EXPECT_EQ(results.experimental_results(),
default_results.experimental_results());
}
TEST(DnsResponseResultExtractorTest, IgnoresWrongTypeHttpsResponses) {
DnsResponse response = BuildTestDnsResponse(
"https.test", dns_protocol::kTypeHttps,
{BuildTestAddressRecord("https.test", IPAddress(1, 2, 3, 4))});
DnsResponseResultExtractor extractor(&response);
HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN);
EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::HTTPS, &results),
DnsResponseResultExtractor::ExtractionError::kOk);
EXPECT_THAT(results.error(), test::IsError(ERR_NAME_NOT_RESOLVED));
EXPECT_THAT(results.experimental_results(),
testing::Optional(testing::IsEmpty()));
}
TEST(DnsResponseResultExtractorTest, IgnoresAdditionalHttpsRecords) {
DnsResponse response = BuildTestDnsResponse(
"https.test", dns_protocol::kTypeHttps,
{BuildTestHttpsAliasRecord("https.test", "alias.test")} /* answers */,
{} /* authority */,
{BuildTestHttpsServiceRecord("https.test", 3u, "service1.test", {}),
BuildTestHttpsServiceRecord("https.test", 2u, "service2.test",
{})} /* additional */);
DnsResponseResultExtractor extractor(&response);
HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN);
EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::HTTPS, &results),
DnsResponseResultExtractor::ExtractionError::kOk);
// Experimental type, so does not affect overall result.
EXPECT_THAT(results.error(), test::IsError(ERR_NAME_NOT_RESOLVED));
EXPECT_THAT(results.experimental_results(),
testing::Optional(testing::ElementsAre(true)));
}
TEST(DnsResponseResultExtractorTest, IgnoresUnsolicitedHttpsRecords) {
DnsResponse response = BuildTestDnsResponse(
"name.test", dns_protocol::kTypeTXT,
{BuildTestDnsRecord("name.test", dns_protocol::kTypeTXT,
"\003foo")} /* answers */,
{} /* authority */,
{BuildTestHttpsServiceRecord("https.test", 3u, "service1.test", {}),
BuildTestHttpsServiceRecord("https.test", 2u, "service2.test",
{})} /* additional */);
DnsResponseResultExtractor extractor(&response);
HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN);
EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::TXT, &results),
DnsResponseResultExtractor::ExtractionError::kOk);
EXPECT_THAT(results.error(), test::IsOk());
EXPECT_THAT(results.text_records(),
testing::Optional(testing::ElementsAre("foo")));
}
TEST(DnsResponseResultExtractorTest, HandlesInOrderCnameChain) {
DnsResponse response =
BuildTestDnsResponse("first.test", dns_protocol::kTypeTXT,
{BuildTestCnameRecord("first.test", "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, &results),
DnsResponseResultExtractor::ExtractionError::kOk);
EXPECT_THAT(results.error(), test::IsOk());
EXPECT_THAT(results.text_records(),
testing::Optional(testing::UnorderedElementsAre("foo", "bar")));
}
TEST(DnsResponseResultExtractorTest, HandlesInOrderCnameChainTypeA) {
const IPAddress kExpected(192, 168, 0, 1);
IPEndPoint expected_endpoint(kExpected, 0 /* port */);
DnsResponse response =
BuildTestDnsResponse("first.test", dns_protocol::kTypeA,
{BuildTestCnameRecord("first.test", "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, &results),
DnsResponseResultExtractor::ExtractionError::kOk);
EXPECT_THAT(results.error(), test::IsOk());
ASSERT_TRUE(results.addresses());
EXPECT_THAT(results.addresses().value().endpoints(),
testing::ElementsAre(expected_endpoint));
EXPECT_THAT(results.addresses().value().dns_aliases(),
testing::ElementsAre("fourth.test", "third.test", "second.test",
"first.test"));
}
TEST(DnsResponseResultExtractorTest, HandlesReverseOrderCnameChain) {
DnsResponse response =
BuildTestDnsResponse("first.test", dns_protocol::kTypeTXT,
{BuildTestTextRecord("fourth.test", {"foo"}),
BuildTestCnameRecord("third.test", "fourth.test"),
BuildTestCnameRecord("second.test", "third.test"),
BuildTestCnameRecord("first.test", "second.test")});
DnsResponseResultExtractor extractor(&response);
HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN);
EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::TXT, &results),
DnsResponseResultExtractor::ExtractionError::kOk);
EXPECT_THAT(results.error(), test::IsOk());
EXPECT_THAT(results.text_records(),
testing::Optional(testing::ElementsAre("foo")));
}
TEST(DnsResponseResultExtractorTest, HandlesReverseOrderCnameChainTypeA) {
const IPAddress kExpected(192, 168, 0, 1);
IPEndPoint expected_endpoint(kExpected, 0 /* port */);
DnsResponse response =
BuildTestDnsResponse("first.test", dns_protocol::kTypeA,
{BuildTestAddressRecord("fourth.test", kExpected),
BuildTestCnameRecord("third.test", "fourth.test"),
BuildTestCnameRecord("second.test", "third.test"),
BuildTestCnameRecord("first.test", "second.test")});
DnsResponseResultExtractor extractor(&response);
HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN);
EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::A, &results),
DnsResponseResultExtractor::ExtractionError::kOk);
EXPECT_THAT(results.error(), test::IsOk());
ASSERT_TRUE(results.addresses());
EXPECT_THAT(results.addresses().value().endpoints(),
testing::ElementsAre(expected_endpoint));
EXPECT_THAT(results.addresses().value().dns_aliases(),
testing::ElementsAre("fourth.test", "third.test", "second.test",
"first.test"));
}
TEST(DnsResponseResultExtractorTest, HandlesArbitraryOrderCnameChain) {
DnsResponse response =
BuildTestDnsResponse("first.test", 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, &results),
DnsResponseResultExtractor::ExtractionError::kOk);
EXPECT_THAT(results.error(), test::IsOk());
EXPECT_THAT(results.text_records(),
testing::Optional(testing::ElementsAre("foo")));
}
TEST(DnsResponseResultExtractorTest, HandlesArbitraryOrderCnameChainTypeA) {
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(
"first.test", dns_protocol::kTypeA,
{BuildTestCnameRecord("qsecond.test", "athird.test"),
BuildTestAddressRecord("zfourth.test", kExpected),
BuildTestCnameRecord("athird.test", "zfourth.test"),
BuildTestCnameRecord("first.test", "qsecond.test")});
DnsResponseResultExtractor extractor(&response);
HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN);
EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::A, &results),
DnsResponseResultExtractor::ExtractionError::kOk);
EXPECT_THAT(results.error(), test::IsOk());
ASSERT_TRUE(results.addresses());
EXPECT_THAT(results.addresses().value().endpoints(),
testing::ElementsAre(expected_endpoint));
EXPECT_THAT(results.addresses().value().dns_aliases(),
testing::ElementsAre("zfourth.test", "athird.test",
"qsecond.test", "first.test"));
}
TEST(DnsResponseResultExtractorTest, IgnoresNonResultTypesMixedWithCnameChain) {
DnsResponse response = BuildTestDnsResponse(
"first.test", 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("first.test", "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, &results),
DnsResponseResultExtractor::ExtractionError::kOk);
EXPECT_THAT(results.error(), test::IsOk());
EXPECT_THAT(results.text_records(),
testing::Optional(testing::ElementsAre("foo")));
EXPECT_FALSE(results.addresses());
}
TEST(DnsResponseResultExtractorTest,
IgnoresNonResultTypesMixedWithCnameChainTypeA) {
const IPAddress kExpected(192, 168, 0, 1);
IPEndPoint expected_endpoint(kExpected, 0 /* port */);
DnsResponse response =
BuildTestDnsResponse("first.test", dns_protocol::kTypeA,
{BuildTestCnameRecord("second.test", "third.test"),
BuildTestTextRecord("fourth.test", {"foo"}),
BuildTestCnameRecord("third.test", "fourth.test"),
BuildTestCnameRecord("first.test", "second.test"),
BuildTestAddressRecord("fourth.test", kExpected)});
DnsResponseResultExtractor extractor(&response);
HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN);
EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::A, &results),
DnsResponseResultExtractor::ExtractionError::kOk);
EXPECT_THAT(results.error(), test::IsOk());
EXPECT_FALSE(results.text_records());
ASSERT_TRUE(results.addresses());
EXPECT_THAT(results.addresses().value().endpoints(),
testing::ElementsAre(expected_endpoint));
EXPECT_THAT(results.addresses().value().dns_aliases(),
testing::ElementsAre("fourth.test", "third.test", "second.test",
"first.test"));
}
TEST(DnsResponseResultExtractorTest, HandlesCnameChainWithoutResult) {
DnsResponse response =
BuildTestDnsResponse("first.test", dns_protocol::kTypeTXT,
{BuildTestCnameRecord("second.test", "third.test"),
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, &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) {
DnsResponse response =
BuildTestDnsResponse("first.test", dns_protocol::kTypeA,
{BuildTestCnameRecord("second.test", "third.test"),
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::A, &results),
DnsResponseResultExtractor::ExtractionError::kOk);
EXPECT_THAT(results.error(), test::IsError(ERR_NAME_NOT_RESOLVED));
ASSERT_TRUE(results.addresses());
EXPECT_TRUE(results.addresses().value().dns_aliases().empty());
}
TEST(DnsResponseResultExtractorTest, RejectsCnameChainWithLoop) {
DnsResponse response =
BuildTestDnsResponse("first.test", dns_protocol::kTypeTXT,
{BuildTestCnameRecord("second.test", "third.test"),
BuildTestTextRecord("third.test", {"foo"}),
BuildTestCnameRecord("third.test", "second.test"),
BuildTestCnameRecord("first.test", "second.test")});
DnsResponseResultExtractor extractor(&response);
HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN);
EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::TXT, &results),
DnsResponseResultExtractor::ExtractionError::kBadAliasChain);
EXPECT_THAT(results.error(), test::IsError(ERR_DNS_MALFORMED_RESPONSE));
}
TEST(DnsResponseResultExtractorTest, RejectsCnameChainWithLoopToBeginning) {
DnsResponse response =
BuildTestDnsResponse("first.test", dns_protocol::kTypeTXT,
{BuildTestCnameRecord("second.test", "third.test"),
BuildTestTextRecord("third.test", {"foo"}),
BuildTestCnameRecord("third.test", "first.test"),
BuildTestCnameRecord("first.test", "second.test")});
DnsResponseResultExtractor extractor(&response);
HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN);
EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::TXT, &results),
DnsResponseResultExtractor::ExtractionError::kBadAliasChain);
EXPECT_THAT(results.error(), test::IsError(ERR_DNS_MALFORMED_RESPONSE));
}
TEST(DnsResponseResultExtractorTest,
RejectsCnameChainWithLoopToBeginningWithoutResult) {
DnsResponse response =
BuildTestDnsResponse("first.test", dns_protocol::kTypeTXT,
{BuildTestCnameRecord("second.test", "third.test"),
BuildTestCnameRecord("third.test", "first.test"),
BuildTestCnameRecord("first.test", "second.test")});
DnsResponseResultExtractor extractor(&response);
HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN);
EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::TXT, &results),
DnsResponseResultExtractor::ExtractionError::kBadAliasChain);
EXPECT_THAT(results.error(), test::IsError(ERR_DNS_MALFORMED_RESPONSE));
}
TEST(DnsResponseResultExtractorTest, RejectsCnameChainWithWrongStart) {
DnsResponse response =
BuildTestDnsResponse("test.test", 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, &results),
DnsResponseResultExtractor::ExtractionError::kBadAliasChain);
EXPECT_THAT(results.error(), test::IsError(ERR_DNS_MALFORMED_RESPONSE));
}
TEST(DnsResponseResultExtractorTest, RejectsCnameChainWithWrongResultName) {
DnsResponse response =
BuildTestDnsResponse("first.test", dns_protocol::kTypeTXT,
{BuildTestCnameRecord("second.test", "third.test"),
BuildTestTextRecord("third.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, &results),
DnsResponseResultExtractor::ExtractionError::kNameMismatch);
EXPECT_THAT(results.error(), test::IsError(ERR_DNS_MALFORMED_RESPONSE));
}
TEST(DnsResponseResultExtractorTest, RejectsCnameSharedWithResult) {
DnsResponse response =
BuildTestDnsResponse("first.test", dns_protocol::kTypeTXT,
{BuildTestCnameRecord("second.test", "third.test"),
BuildTestTextRecord("first.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, &results),
DnsResponseResultExtractor::ExtractionError::kNameMismatch);
EXPECT_THAT(results.error(), test::IsError(ERR_DNS_MALFORMED_RESPONSE));
}
TEST(DnsResponseResultExtractorTest, RejectsDisjointCnameChain) {
DnsResponse response = BuildTestDnsResponse(
"first.test", dns_protocol::kTypeTXT,
{BuildTestCnameRecord("second.test", "third.test"),
BuildTestTextRecord("fourth.test", {"foo"}),
BuildTestCnameRecord("third.test", "fourth.test"),
BuildTestCnameRecord("other1.test", "other2.test"),
BuildTestCnameRecord("first.test", "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, &results),
DnsResponseResultExtractor::ExtractionError::kBadAliasChain);
EXPECT_THAT(results.error(), test::IsError(ERR_DNS_MALFORMED_RESPONSE));
}
TEST(DnsResponseResultExtractorTest, RejectsDoubledCnames) {
DnsResponse response =
BuildTestDnsResponse("first.test", dns_protocol::kTypeTXT,
{BuildTestCnameRecord("second.test", "third.test"),
BuildTestTextRecord("fourth.test", {"foo"}),
BuildTestCnameRecord("third.test", "fourth.test"),
BuildTestCnameRecord("third.test", "fifth.test"),
BuildTestCnameRecord("first.test", "second.test")});
DnsResponseResultExtractor extractor(&response);
HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN);
EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::TXT, &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::TimeDelta::FromMinutes(4);
DnsResponse response = BuildTestDnsResponse(
kName, dns_protocol::kTypeTXT,
{BuildTestTextRecord(kName, {"foo"}, base::TimeDelta::FromHours(3)),
BuildTestTextRecord(kName, {"bar"}, kMinTtl),
BuildTestAddressRecord(kName, IPAddress(1, 2, 3, 4),
base::TimeDelta::FromSeconds(2)),
BuildTestTextRecord(kName, {"baz"}, base::TimeDelta::FromMinutes(15))});
DnsResponseResultExtractor extractor(&response);
HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN);
EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::TXT, &results),
DnsResponseResultExtractor::ExtractionError::kOk);
ASSERT_TRUE(results.has_ttl());
EXPECT_EQ(results.ttl(), kMinTtl);
}
TEST(DnsResponseResultExtractorTest, ExtractsTtlFromCname) {
constexpr char kAlias[] = "alias.test";
constexpr base::TimeDelta kMinTtl = base::TimeDelta::FromMinutes(4);
DnsResponse response = BuildTestDnsResponse(
"name.test", dns_protocol::kTypeTXT,
{BuildTestTextRecord(kAlias, {"foo"}, base::TimeDelta::FromHours(3)),
BuildTestTextRecord(kAlias, {"bar"}, base::TimeDelta::FromHours(2)),
BuildTestTextRecord(kAlias, {"baz"}, base::TimeDelta::FromMinutes(15)),
BuildTestCnameRecord("name.test", kAlias, kMinTtl)});
DnsResponseResultExtractor extractor(&response);
HostCache::Entry results(ERR_FAILED, HostCache::Entry::SOURCE_UNKNOWN);
EXPECT_EQ(extractor.ExtractDnsResults(DnsQueryType::TXT, &results),
DnsResponseResultExtractor::ExtractionError::kOk);
ASSERT_TRUE(results.has_ttl());
EXPECT_EQ(results.ttl(), kMinTtl);
}
} // namespace
} // namespace net