blob: 88f4c2bd96f6efed98db80f1be08a10bb255b1d8 [file] [log] [blame]
// Copyright (c) 2013 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/record_rdata.h"
#include <algorithm>
#include <memory>
#include <utility>
#include "base/big_endian.h"
#include "base/optional.h"
#include "net/dns/dns_response.h"
#include "net/dns/dns_test_util.h"
#include "net/test/gtest_util.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace net {
namespace {
using ::testing::ElementsAreArray;
using ::testing::IsNull;
using ::testing::NotNull;
using ::testing::SizeIs;
base::StringPiece MakeStringPiece(const uint8_t* data, unsigned size) {
const char* data_cc = reinterpret_cast<const char*>(data);
return base::StringPiece(data_cc, size);
}
base::StringPiece MakeStringPiece(const std::vector<uint8_t>& vec) {
return MakeStringPiece(vec.data(), vec.size());
}
TEST(RecordRdataTest, ParseSrvRecord) {
// These are just the rdata portions of the DNS records, rather than complete
// records, but it works well enough for this test.
const uint8_t
record[] =
{
0x00, 0x01, 0x00, 0x02, 0x00, 0x50, 0x03, 'w', 'w',
'w', 0x06, 'g', 'o', 'o', 'g', 'l', 'e', 0x03,
'c', 'o', 'm', 0x00, 0x01, 0x01, 0x01, 0x02, 0x01,
0x03, 0x04, 'w', 'w', 'w', '2', 0xc0, 0x0a, // Pointer to
// "google.com"
};
DnsRecordParser parser(record, sizeof(record), 0);
const unsigned first_record_len = 22;
base::StringPiece record1_strpiece = MakeStringPiece(
record, first_record_len);
base::StringPiece record2_strpiece = MakeStringPiece(
record + first_record_len, sizeof(record) - first_record_len);
std::unique_ptr<SrvRecordRdata> record1_obj =
SrvRecordRdata::Create(record1_strpiece, parser);
ASSERT_TRUE(record1_obj != nullptr);
ASSERT_EQ(1, record1_obj->priority());
ASSERT_EQ(2, record1_obj->weight());
ASSERT_EQ(80, record1_obj->port());
ASSERT_EQ("www.google.com", record1_obj->target());
std::unique_ptr<SrvRecordRdata> record2_obj =
SrvRecordRdata::Create(record2_strpiece, parser);
ASSERT_TRUE(record2_obj != nullptr);
ASSERT_EQ(257, record2_obj->priority());
ASSERT_EQ(258, record2_obj->weight());
ASSERT_EQ(259, record2_obj->port());
ASSERT_EQ("www2.google.com", record2_obj->target());
ASSERT_TRUE(record1_obj->IsEqual(record1_obj.get()));
ASSERT_FALSE(record1_obj->IsEqual(record2_obj.get()));
}
TEST(RecordRdataTest, ParseARecord) {
// These are just the rdata portions of the DNS records, rather than complete
// records, but it works well enough for this test.
const uint8_t record[] = {
0x7F, 0x00, 0x00, 0x01 // 127.0.0.1
};
DnsRecordParser parser(record, sizeof(record), 0);
base::StringPiece record_strpiece = MakeStringPiece(record, sizeof(record));
std::unique_ptr<ARecordRdata> record_obj =
ARecordRdata::Create(record_strpiece, parser);
ASSERT_TRUE(record_obj != nullptr);
ASSERT_EQ("127.0.0.1", record_obj->address().ToString());
ASSERT_TRUE(record_obj->IsEqual(record_obj.get()));
}
TEST(RecordRdataTest, ParseAAAARecord) {
// These are just the rdata portions of the DNS records, rather than complete
// records, but it works well enough for this test.
const uint8_t record[] = {
0x12, 0x34, 0x56, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09 // 1234:5678::9A
};
DnsRecordParser parser(record, sizeof(record), 0);
base::StringPiece record_strpiece = MakeStringPiece(record, sizeof(record));
std::unique_ptr<AAAARecordRdata> record_obj =
AAAARecordRdata::Create(record_strpiece, parser);
ASSERT_TRUE(record_obj != nullptr);
ASSERT_EQ("1234:5678::9", record_obj->address().ToString());
ASSERT_TRUE(record_obj->IsEqual(record_obj.get()));
}
TEST(RecordRdataTest, ParseCnameRecord) {
// These are just the rdata portions of the DNS records, rather than complete
// records, but it works well enough for this test.
const uint8_t record[] = {0x03, 'w', 'w', 'w', 0x06, 'g', 'o', 'o',
'g', 'l', 'e', 0x03, 'c', 'o', 'm', 0x00};
DnsRecordParser parser(record, sizeof(record), 0);
base::StringPiece record_strpiece = MakeStringPiece(record, sizeof(record));
std::unique_ptr<CnameRecordRdata> record_obj =
CnameRecordRdata::Create(record_strpiece, parser);
ASSERT_TRUE(record_obj != nullptr);
ASSERT_EQ("www.google.com", record_obj->cname());
ASSERT_TRUE(record_obj->IsEqual(record_obj.get()));
}
TEST(RecordRdataTest, ParsePtrRecord) {
// These are just the rdata portions of the DNS records, rather than complete
// records, but it works well enough for this test.
const uint8_t record[] = {0x03, 'w', 'w', 'w', 0x06, 'g', 'o', 'o',
'g', 'l', 'e', 0x03, 'c', 'o', 'm', 0x00};
DnsRecordParser parser(record, sizeof(record), 0);
base::StringPiece record_strpiece = MakeStringPiece(record, sizeof(record));
std::unique_ptr<PtrRecordRdata> record_obj =
PtrRecordRdata::Create(record_strpiece, parser);
ASSERT_TRUE(record_obj != nullptr);
ASSERT_EQ("www.google.com", record_obj->ptrdomain());
ASSERT_TRUE(record_obj->IsEqual(record_obj.get()));
}
TEST(RecordRdataTest, ParseTxtRecord) {
// These are just the rdata portions of the DNS records, rather than complete
// records, but it works well enough for this test.
const uint8_t record[] = {0x03, 'w', 'w', 'w', 0x06, 'g', 'o', 'o',
'g', 'l', 'e', 0x03, 'c', 'o', 'm'};
DnsRecordParser parser(record, sizeof(record), 0);
base::StringPiece record_strpiece = MakeStringPiece(record, sizeof(record));
std::unique_ptr<TxtRecordRdata> record_obj =
TxtRecordRdata::Create(record_strpiece, parser);
ASSERT_TRUE(record_obj != nullptr);
std::vector<std::string> expected;
expected.push_back("www");
expected.push_back("google");
expected.push_back("com");
ASSERT_EQ(expected, record_obj->texts());
ASSERT_TRUE(record_obj->IsEqual(record_obj.get()));
}
TEST(RecordRdataTest, ParseNsecRecord) {
// These are just the rdata portions of the DNS records, rather than complete
// records, but it works well enough for this test.
const uint8_t record[] = {0x03, 'w', 'w', 'w', 0x06, 'g', 'o',
'o', 'g', 'l', 'e', 0x03, 'c', 'o',
'm', 0x00, 0x00, 0x02, 0x40, 0x01};
DnsRecordParser parser(record, sizeof(record), 0);
base::StringPiece record_strpiece = MakeStringPiece(record, sizeof(record));
std::unique_ptr<NsecRecordRdata> record_obj =
NsecRecordRdata::Create(record_strpiece, parser);
ASSERT_TRUE(record_obj != nullptr);
ASSERT_EQ(16u, record_obj->bitmap_length());
EXPECT_FALSE(record_obj->GetBit(0));
EXPECT_TRUE(record_obj->GetBit(1));
for (int i = 2; i < 15; i++) {
EXPECT_FALSE(record_obj->GetBit(i));
}
EXPECT_TRUE(record_obj->GetBit(15));
ASSERT_TRUE(record_obj->IsEqual(record_obj.get()));
}
TEST(RecordRdataTest, CreateNsecRecordWithEmptyBitmapReturnsNull) {
// These are just the rdata portions of the DNS records, rather than complete
// records, but it works well enough for this test.
// This record has a bitmap that is 0 bytes long.
const uint8_t record[] = {0x03, 'w', 'w', 'w', 0x06, 'g', 'o', 'o', 'g',
'l', 'e', 0x03, 'c', 'o', 'm', 0x00, 0x00, 0x00};
DnsRecordParser parser(record, sizeof(record), 0);
base::StringPiece record_strpiece = MakeStringPiece(record, sizeof(record));
std::unique_ptr<NsecRecordRdata> record_obj =
NsecRecordRdata::Create(record_strpiece, parser);
ASSERT_FALSE(record_obj);
}
TEST(RecordRdataTest, CreateNsecRecordWithOversizedBitmapReturnsNull) {
// These are just the rdata portions of the DNS records, rather than complete
// records, but it works well enough for this test.
// This record has a bitmap that is 33 bytes long. The maximum size allowed by
// RFC 3845, Section 2.1.2, is 32 bytes.
const uint8_t record[] = {
0x03, 'w', 'w', 'w', 0x06, 'g', 'o', 'o', 'g', 'l', 'e',
0x03, 'c', 'o', 'm', 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
DnsRecordParser parser(record, sizeof(record), 0);
base::StringPiece record_strpiece = MakeStringPiece(record, sizeof(record));
std::unique_ptr<NsecRecordRdata> record_obj =
NsecRecordRdata::Create(record_strpiece, parser);
ASSERT_FALSE(record_obj);
}
TEST(RecordRdataTest, ParseOptRecord) {
// This is just the rdata portion of an OPT record, rather than a complete
// record.
const uint8_t rdata[] = {
// First OPT
0x00, 0x01, // OPT code
0x00, 0x02, // OPT data size
0xDE, 0xAD, // OPT data
// Second OPT
0x00, 0xFF, // OPT code
0x00, 0x04, // OPT data size
0xDE, 0xAD, 0xBE, 0xEF // OPT data
};
DnsRecordParser parser(rdata, sizeof(rdata), 0);
base::StringPiece rdata_strpiece = MakeStringPiece(rdata, sizeof(rdata));
std::unique_ptr<OptRecordRdata> rdata_obj =
OptRecordRdata::Create(rdata_strpiece, parser);
ASSERT_THAT(rdata_obj, NotNull());
ASSERT_THAT(rdata_obj->opts(), SizeIs(2));
ASSERT_EQ(1, rdata_obj->opts()[0].code());
ASSERT_EQ("\xde\xad", rdata_obj->opts()[0].data());
ASSERT_EQ(255, rdata_obj->opts()[1].code());
ASSERT_EQ("\xde\xad\xbe\xef", rdata_obj->opts()[1].data());
ASSERT_TRUE(rdata_obj->IsEqual(rdata_obj.get()));
}
TEST(RecordRdataTest, ParseOptRecordWithShorterSizeThanData) {
// This is just the rdata portion of an OPT record, rather than a complete
// record.
const uint8_t rdata[] = {
0x00, 0xFF, // OPT code
0x00, 0x02, // OPT data size (incorrect, should be 4)
0xDE, 0xAD, 0xBE, 0xEF // OPT data
};
DnsRecordParser parser(rdata, sizeof(rdata), 0);
base::StringPiece rdata_strpiece = MakeStringPiece(rdata, sizeof(rdata));
std::unique_ptr<OptRecordRdata> rdata_obj =
OptRecordRdata::Create(rdata_strpiece, parser);
ASSERT_THAT(rdata_obj, IsNull());
}
TEST(RecordRdataTest, ParseOptRecordWithLongerSizeThanData) {
// This is just the rdata portion of an OPT record, rather than a complete
// record.
const uint8_t rdata[] = {
0x00, 0xFF, // OPT code
0x00, 0x04, // OPT data size (incorrect, should be 4)
0xDE, 0xAD // OPT data
};
DnsRecordParser parser(rdata, sizeof(rdata), 0);
base::StringPiece rdata_strpiece = MakeStringPiece(rdata, sizeof(rdata));
std::unique_ptr<OptRecordRdata> rdata_obj =
OptRecordRdata::Create(rdata_strpiece, parser);
ASSERT_THAT(rdata_obj, IsNull());
}
TEST(RecordRdataTest, AddOptToOptRecord) {
// This is just the rdata portion of an OPT record, rather than a complete
// record.
const uint8_t expected_rdata[] = {
0x00, 0xFF, // OPT code
0x00, 0x04, // OPT data size
0xDE, 0xAD, 0xBE, 0xEF // OPT data
};
OptRecordRdata rdata;
rdata.AddOpt(OptRecordRdata::Opt(255, "\xde\xad\xbe\xef"));
EXPECT_THAT(rdata.buf(), ElementsAreArray(expected_rdata));
}
// Test that for arbitrary IntegrityRecordRdata r, Parse(Serialize(r)) == r.
TEST(RecordRdataTest, IntegrityParseSerializeInverseProperty) {
IntegrityRecordRdata record(IntegrityRecordRdata::Random());
EXPECT_TRUE(record.IsIntact());
base::Optional<std::vector<uint8_t>> serialized = record.Serialize();
EXPECT_TRUE(serialized);
std::unique_ptr<IntegrityRecordRdata> reparsed =
IntegrityRecordRdata::Create(MakeStringPiece(*serialized));
EXPECT_TRUE(reparsed);
EXPECT_TRUE(reparsed->IsEqual(&record));
}
TEST(RecordRdataTest, IntegrityEmptyNonceCornerCase) {
const IntegrityRecordRdata::Nonce empty_nonce;
IntegrityRecordRdata record(empty_nonce);
EXPECT_TRUE(record.IsIntact());
base::Optional<std::vector<uint8_t>> serialized = record.Serialize();
EXPECT_TRUE(serialized);
std::unique_ptr<IntegrityRecordRdata> reparsed =
IntegrityRecordRdata::Create(MakeStringPiece(*serialized));
EXPECT_TRUE(reparsed);
EXPECT_TRUE(reparsed->IsIntact());
EXPECT_TRUE(reparsed->IsEqual(&record));
EXPECT_EQ(reparsed->nonce().size(), 0u);
}
TEST(RecordRdataTest, IntegrityMoveConstructor) {
IntegrityRecordRdata record_a(IntegrityRecordRdata::Random());
EXPECT_TRUE(record_a.IsIntact());
base::Optional<std::vector<uint8_t>> serialized_a = record_a.Serialize();
EXPECT_TRUE(serialized_a);
IntegrityRecordRdata record_b = std::move(record_a);
EXPECT_TRUE(record_b.IsIntact());
base::Optional<std::vector<uint8_t>> serialized_b = record_b.Serialize();
EXPECT_TRUE(serialized_b);
EXPECT_EQ(serialized_a, serialized_b);
}
TEST(RecordRdataTest, IntegrityRandomRecordsDiffer) {
IntegrityRecordRdata record_a(IntegrityRecordRdata::Random());
IntegrityRecordRdata record_b(IntegrityRecordRdata::Random());
EXPECT_TRUE(!record_a.IsEqual(&record_b));
}
TEST(RecordRdataTest, IntegritySerialize) {
IntegrityRecordRdata record({'A'});
EXPECT_TRUE(record.IsIntact());
const base::Optional<std::vector<uint8_t>> serialized = record.Serialize();
EXPECT_TRUE(serialized);
// Expected payload contains the SHA256 hash of 'A'. For the lazy:
// $ echo -n A | sha256sum | cut -f1 -d' ' | sed -e 's/\(..\)/0x\1, /g'
const std::vector<uint8_t> expected = {
0, 1, 'A', // Length prefix and nonce
// Begin digest
0x55, 0x9a, 0xea, 0xd0, 0x82, 0x64, 0xd5, 0x79, 0x5d, 0x39, 0x09, 0x71,
0x8c, 0xdd, 0x05, 0xab, 0xd4, 0x95, 0x72, 0xe8, 0x4f, 0xe5, 0x55, 0x90,
0xee, 0xf3, 0x1a, 0x88, 0xa0, 0x8f, 0xdf, 0xfd, // End digest
};
EXPECT_TRUE(*serialized == expected);
}
TEST(RecordRdataTest, IntegrityParse) {
const std::vector<uint8_t> serialized = {
0, 6, 'f', 'o', 'o', 'b', 'a', 'r', // Length prefix and nonce
0xc3, 0xab, 0x8f, 0xf1, 0x37, 0x20, 0xe8, 0xad, 0x90, // Begin digest
0x47, 0xdd, 0x39, 0x46, 0x6b, 0x3c, 0x89, 0x74, 0xe5, 0x92, 0xc2,
0xfa, 0x38, 0x3d, 0x4a, 0x39, 0x60, 0x71, 0x4c, 0xae, 0xf0, 0xc4,
0xf2, // End digest
};
auto record = IntegrityRecordRdata::Create(MakeStringPiece(serialized));
EXPECT_TRUE(record);
EXPECT_TRUE(record->IsIntact());
}
TEST(RecordRdataTest, IntegrityBadParseEmptyRdata) {
const std::vector<uint8_t> serialized = {};
auto record = IntegrityRecordRdata::Create(MakeStringPiece(serialized));
EXPECT_TRUE(record);
EXPECT_FALSE(record->IsIntact());
}
TEST(RecordRdataTest, IntegrityBadParseTruncatedNonce) {
const std::vector<uint8_t> serialized = {
0, 6, 'f', 'o', 'o' // Length prefix and truncated nonce
};
auto record = IntegrityRecordRdata::Create(MakeStringPiece(serialized));
EXPECT_TRUE(record);
EXPECT_FALSE(record->IsIntact());
}
TEST(RecordRdataTest, IntegrityBadParseTruncatedDigest) {
const std::vector<uint8_t> serialized = {
0, 6, 'f', 'o', 'o', 'b', 'a', 'r', // Length prefix and nonce
// Begin Digest
0xc3, 0xab, 0x8f, 0xf1, 0x37, 0x20, 0xe8, 0xad, 0x90, 0x47, 0xdd, 0x39,
0x46, 0x6b, 0x3c, 0x89, 0x74, 0xe5, 0x92, 0xc2, 0xfa, 0x38, 0x3d,
0x4a, // End digest
};
auto record = IntegrityRecordRdata::Create(MakeStringPiece(serialized));
EXPECT_TRUE(record);
EXPECT_FALSE(record->IsIntact());
}
TEST(RecordRdataTest, IntegrityBadParseExtraBytes) {
const std::vector<uint8_t> serialized = {
0, 6, 'f', 'o', 'o', 'b', 'a', 'r', // Length prefix and nonce
// Begin digest
0xc3, 0xab, 0x8f, 0xf1, 0x37, 0x20, 0xe8, 0xad, 0x90, 0x47, 0xdd, 0x39,
0x46, 0x6b, 0x3c, 0x89, 0x74, 0xe5, 0x92, 0xc2, 0xfa, 0x38, 0x3d, 0x4a,
0x39, 0x60, 0x71, 0x4c, 0xae, 0xf0, 0xc4, 0xf2, // End digest
'e', 'x', 't', 'r', 'a' // Trailing bytes
};
auto record = IntegrityRecordRdata::Create(MakeStringPiece(serialized));
EXPECT_TRUE(record);
EXPECT_FALSE(record->IsIntact());
}
TEST(RecordRdataTest, IntegrityCorruptedDigest) {
const std::vector<uint8_t> serialized = {
0, 6, 'f', 'o', 'o', 'b', 'a', 'r', // Length prefix and nonce
0xde, 0xad, 0xbe, 0xef, 0x37, 0x20, 0xe8, 0xad, 0x90, // Begin digest
0x47, 0xdd, 0x39, 0x46, 0x6b, 0x3c, 0x89, 0x74, 0xe5, 0x92, 0xc2,
0xfa, 0x38, 0x3d, 0x4a, 0x39, 0x60, 0x71, 0x4c, 0xae, 0xf0, 0xc4,
0xf2, // End digest
};
auto record = IntegrityRecordRdata::Create(MakeStringPiece(serialized));
EXPECT_TRUE(record);
EXPECT_FALSE(record->IsIntact());
}
} // namespace
} // namespace net