diff options
Diffstat (limited to 'chromium/net/dns/mdns_cache_unittest.cc')
-rw-r--r-- | chromium/net/dns/mdns_cache_unittest.cc | 375 |
1 files changed, 375 insertions, 0 deletions
diff --git a/chromium/net/dns/mdns_cache_unittest.cc b/chromium/net/dns/mdns_cache_unittest.cc new file mode 100644 index 00000000000..c12ad6b6ec3 --- /dev/null +++ b/chromium/net/dns/mdns_cache_unittest.cc @@ -0,0 +1,375 @@ +// 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 <algorithm> + +#include "base/bind.h" +#include "net/dns/dns_response.h" +#include "net/dns/dns_test_util.h" +#include "net/dns/mdns_cache.h" +#include "net/dns/record_parsed.h" +#include "net/dns/record_rdata.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using ::testing::Return; +using ::testing::StrictMock; + +namespace net { + +static const uint8 kTestResponsesDifferentAnswers[] = { + // Answer 1 + // ghs.l.google.com in DNS format. + 3, 'g', 'h', 's', + 1, 'l', + 6, 'g', 'o', 'o', 'g', 'l', 'e', + 3, 'c', 'o', 'm', + 0x00, + 0x00, 0x01, // TYPE is A. + 0x00, 0x01, // CLASS is IN. + 0, 0, 0, 53, // TTL (4 bytes) is 53 seconds. + 0, 4, // RDLENGTH is 4 bytes. + 74, 125, 95, 121, // RDATA is the IP: 74.125.95.121 + + // Answer 2 + // Pointer to answer 1 + 0xc0, 0x00, + 0x00, 0x01, // TYPE is A. + 0x00, 0x01, // CLASS is IN. + 0, 0, 0, 53, // TTL (4 bytes) is 53 seconds. + 0, 4, // RDLENGTH is 4 bytes. + 74, 125, 95, 122, // RDATA is the IP: 74.125.95.122 +}; + +static const uint8 kTestResponsesSameAnswers[] = { + // Answer 1 + // ghs.l.google.com in DNS format. + 3, 'g', 'h', 's', + 1, 'l', + 6, 'g', 'o', 'o', 'g', 'l', 'e', + 3, 'c', 'o', 'm', + 0x00, + 0x00, 0x01, // TYPE is A. + 0x00, 0x01, // CLASS is IN. + 0, 0, 0, 53, // TTL (4 bytes) is 53 seconds. + 0, 4, // RDLENGTH is 4 bytes. + 74, 125, 95, 121, // RDATA is the IP: 74.125.95.121 + + // Answer 2 + // Pointer to answer 1 + 0xc0, 0x00, + 0x00, 0x01, // TYPE is A. + 0x00, 0x01, // CLASS is IN. + 0, 0, 0, 112, // TTL (4 bytes) is 112 seconds. + 0, 4, // RDLENGTH is 4 bytes. + 74, 125, 95, 121, // RDATA is the IP: 74.125.95.121 +}; + +static const uint8 kTestResponseTwoRecords[] = { + // Answer 1 + // ghs.l.google.com in DNS format. (A) + 3, 'g', 'h', 's', + 1, 'l', + 6, 'g', 'o', 'o', 'g', 'l', 'e', + 3, 'c', 'o', 'm', + 0x00, + 0x00, 0x01, // TYPE is A. + 0x00, 0x01, // CLASS is IN. + 0, 0, 0, 53, // TTL (4 bytes) is 53 seconds. + 0, 4, // RDLENGTH is 4 bytes. + 74, 125, 95, 121, // RDATA is the IP: 74.125.95.121 + + // Answer 2 + // ghs.l.google.com in DNS format. (AAAA) + 3, 'g', 'h', 's', + 1, 'l', + 6, 'g', 'o', 'o', 'g', 'l', 'e', + 3, 'c', 'o', 'm', + 0x00, + 0x00, 0x1c, // TYPE is AAA. + 0x00, 0x01, // CLASS is IN. + 0, 0, 0, 53, // TTL (4 bytes) is 53 seconds. + 0, 16, // RDLENGTH is 16 bytes. + 0x4a, 0x7d, 0x4a, 0x7d, + 0x5f, 0x79, 0x5f, 0x79, + 0x5f, 0x79, 0x5f, 0x79, + 0x5f, 0x79, 0x5f, 0x79, +}; + +static const uint8 kTestResponsesGoodbyePacket[] = { + // Answer 1 + // ghs.l.google.com in DNS format. (Goodbye packet) + 3, 'g', 'h', 's', + 1, 'l', + 6, 'g', 'o', 'o', 'g', 'l', 'e', + 3, 'c', 'o', 'm', + 0x00, + 0x00, 0x01, // TYPE is A. + 0x00, 0x01, // CLASS is IN. + 0, 0, 0, 0, // TTL (4 bytes) is zero. + 0, 4, // RDLENGTH is 4 bytes. + 74, 125, 95, 121, // RDATA is the IP: 74.125.95.121 + + // Answer 2 + // ghs.l.google.com in DNS format. + 3, 'g', 'h', 's', + 1, 'l', + 6, 'g', 'o', 'o', 'g', 'l', 'e', + 3, 'c', 'o', 'm', + 0x00, + 0x00, 0x01, // TYPE is A. + 0x00, 0x01, // CLASS is IN. + 0, 0, 0, 53, // TTL (4 bytes) is 53 seconds. + 0, 4, // RDLENGTH is 4 bytes. + 74, 125, 95, 121, // RDATA is the IP: 74.125.95.121 +}; + +class RecordRemovalMock { + public: + MOCK_METHOD1(OnRecordRemoved, void(const RecordParsed*)); +}; + +class MDnsCacheTest : public ::testing::Test { + public: + MDnsCacheTest() + : default_time_(base::Time::FromDoubleT(1234.0)) {} + virtual ~MDnsCacheTest() {} + + protected: + base::Time default_time_; + StrictMock<RecordRemovalMock> record_removal_; + MDnsCache cache_; +}; + +// Test a single insert, corresponding lookup, and unsuccessful lookup. +TEST_F(MDnsCacheTest, InsertLookupSingle) { + DnsRecordParser parser(kT1ResponseDatagram, sizeof(kT1ResponseDatagram), + sizeof(dns_protocol::Header)); + parser.SkipQuestion(); + + scoped_ptr<const RecordParsed> record1; + scoped_ptr<const RecordParsed> record2; + std::vector<const RecordParsed*> results; + + record1 = RecordParsed::CreateFrom(&parser, default_time_); + record2 = RecordParsed::CreateFrom(&parser, default_time_); + + EXPECT_EQ(MDnsCache::RecordAdded, cache_.UpdateDnsRecord(record1.Pass())); + + EXPECT_EQ(MDnsCache::RecordAdded, cache_.UpdateDnsRecord(record2.Pass())); + + cache_.FindDnsRecords(ARecordRdata::kType, "ghs.l.google.com", &results, + default_time_); + + EXPECT_EQ(1u, results.size()); + EXPECT_EQ(default_time_, results.front()->time_created()); + + EXPECT_EQ("ghs.l.google.com", results.front()->name()); + + results.clear(); + cache_.FindDnsRecords(PtrRecordRdata::kType, "ghs.l.google.com", &results, + default_time_); + + EXPECT_EQ(0u, results.size()); +} + +// Test that records expire when their ttl has passed. +TEST_F(MDnsCacheTest, Expiration) { + DnsRecordParser parser(kT1ResponseDatagram, sizeof(kT1ResponseDatagram), + sizeof(dns_protocol::Header)); + parser.SkipQuestion(); + scoped_ptr<const RecordParsed> record1; + scoped_ptr<const RecordParsed> record2; + + std::vector<const RecordParsed*> results; + const RecordParsed* record_to_be_deleted; + + record1 = RecordParsed::CreateFrom(&parser, default_time_); + base::TimeDelta ttl1 = base::TimeDelta::FromSeconds(record1->ttl()); + + record2 = RecordParsed::CreateFrom(&parser, default_time_); + base::TimeDelta ttl2 = base::TimeDelta::FromSeconds(record2->ttl()); + record_to_be_deleted = record2.get(); + + EXPECT_EQ(MDnsCache::RecordAdded, cache_.UpdateDnsRecord(record1.Pass())); + EXPECT_EQ(MDnsCache::RecordAdded, cache_.UpdateDnsRecord(record2.Pass())); + + cache_.FindDnsRecords(ARecordRdata::kType, "ghs.l.google.com", &results, + default_time_); + + EXPECT_EQ(1u, results.size()); + + EXPECT_EQ(default_time_ + ttl2, cache_.next_expiration()); + + + cache_.FindDnsRecords(ARecordRdata::kType, "ghs.l.google.com", &results, + default_time_ + ttl2); + + EXPECT_EQ(0u, results.size()); + + EXPECT_CALL(record_removal_, OnRecordRemoved(record_to_be_deleted)); + + cache_.CleanupRecords(default_time_ + ttl2, base::Bind( + &RecordRemovalMock::OnRecordRemoved, base::Unretained(&record_removal_))); + + // To make sure that we've indeed removed them from the map, check no funny + // business happens once they're deleted for good. + + EXPECT_EQ(default_time_ + ttl1, cache_.next_expiration()); + cache_.FindDnsRecords(ARecordRdata::kType, "ghs.l.google.com", &results, + default_time_ + ttl2); + + EXPECT_EQ(0u, results.size()); +} + +// Test that a new record replacing one with the same identity (name/rrtype for +// unique records) causes the cache to output a "record changed" event. +TEST_F(MDnsCacheTest, RecordChange) { + DnsRecordParser parser(kTestResponsesDifferentAnswers, + sizeof(kTestResponsesDifferentAnswers), + 0); + + scoped_ptr<const RecordParsed> record1; + scoped_ptr<const RecordParsed> record2; + std::vector<const RecordParsed*> results; + + record1 = RecordParsed::CreateFrom(&parser, default_time_); + record2 = RecordParsed::CreateFrom(&parser, default_time_); + + EXPECT_EQ(MDnsCache::RecordAdded, cache_.UpdateDnsRecord(record1.Pass())); + EXPECT_EQ(MDnsCache::RecordChanged, + cache_.UpdateDnsRecord(record2.Pass())); +} + +// Test that a new record replacing an otherwise identical one already in the +// cache causes the cache to output a "no change" event. +TEST_F(MDnsCacheTest, RecordNoChange) { + DnsRecordParser parser(kTestResponsesSameAnswers, + sizeof(kTestResponsesSameAnswers), + 0); + + scoped_ptr<const RecordParsed> record1; + scoped_ptr<const RecordParsed> record2; + std::vector<const RecordParsed*> results; + + record1 = RecordParsed::CreateFrom(&parser, default_time_); + record2 = RecordParsed::CreateFrom(&parser, default_time_ + + base::TimeDelta::FromSeconds(1)); + + EXPECT_EQ(MDnsCache::RecordAdded, cache_.UpdateDnsRecord(record1.Pass())); + EXPECT_EQ(MDnsCache::NoChange, cache_.UpdateDnsRecord(record2.Pass())); +} + +// Test that the next expiration time of the cache is updated properly on record +// insertion. +TEST_F(MDnsCacheTest, RecordPreemptExpirationTime) { + DnsRecordParser parser(kTestResponsesSameAnswers, + sizeof(kTestResponsesSameAnswers), + 0); + + scoped_ptr<const RecordParsed> record1; + scoped_ptr<const RecordParsed> record2; + std::vector<const RecordParsed*> results; + + record1 = RecordParsed::CreateFrom(&parser, default_time_); + record2 = RecordParsed::CreateFrom(&parser, default_time_); + base::TimeDelta ttl1 = base::TimeDelta::FromSeconds(record1->ttl()); + base::TimeDelta ttl2 = base::TimeDelta::FromSeconds(record2->ttl()); + + EXPECT_EQ(base::Time(), cache_.next_expiration()); + EXPECT_EQ(MDnsCache::RecordAdded, cache_.UpdateDnsRecord(record2.Pass())); + EXPECT_EQ(default_time_ + ttl2, cache_.next_expiration()); + EXPECT_EQ(MDnsCache::NoChange, cache_.UpdateDnsRecord(record1.Pass())); + EXPECT_EQ(default_time_ + ttl1, cache_.next_expiration()); +} + +// Test that the cache handles mDNS "goodbye" packets correctly, not adding the +// records to the cache if they are not already there, and eventually removing +// records from the cache if they are. +TEST_F(MDnsCacheTest, GoodbyePacket) { + DnsRecordParser parser(kTestResponsesGoodbyePacket, + sizeof(kTestResponsesGoodbyePacket), + 0); + + scoped_ptr<const RecordParsed> record_goodbye; + scoped_ptr<const RecordParsed> record_hello; + scoped_ptr<const RecordParsed> record_goodbye2; + std::vector<const RecordParsed*> results; + + record_goodbye = RecordParsed::CreateFrom(&parser, default_time_); + record_hello = RecordParsed::CreateFrom(&parser, default_time_); + parser = DnsRecordParser(kTestResponsesGoodbyePacket, + sizeof(kTestResponsesGoodbyePacket), + 0); + record_goodbye2 = RecordParsed::CreateFrom(&parser, default_time_); + + base::TimeDelta ttl = base::TimeDelta::FromSeconds(record_hello->ttl()); + + EXPECT_EQ(base::Time(), cache_.next_expiration()); + EXPECT_EQ(MDnsCache::NoChange, cache_.UpdateDnsRecord(record_goodbye.Pass())); + EXPECT_EQ(base::Time(), cache_.next_expiration()); + EXPECT_EQ(MDnsCache::RecordAdded, + cache_.UpdateDnsRecord(record_hello.Pass())); + EXPECT_EQ(default_time_ + ttl, cache_.next_expiration()); + EXPECT_EQ(MDnsCache::NoChange, + cache_.UpdateDnsRecord(record_goodbye2.Pass())); + EXPECT_EQ(default_time_ + base::TimeDelta::FromSeconds(1), + cache_.next_expiration()); +} + +TEST_F(MDnsCacheTest, AnyRRType) { + DnsRecordParser parser(kTestResponseTwoRecords, + sizeof(kTestResponseTwoRecords), + 0); + + scoped_ptr<const RecordParsed> record1; + scoped_ptr<const RecordParsed> record2; + std::vector<const RecordParsed*> results; + + record1 = RecordParsed::CreateFrom(&parser, default_time_); + record2 = RecordParsed::CreateFrom(&parser, default_time_); + EXPECT_EQ(MDnsCache::RecordAdded, cache_.UpdateDnsRecord(record1.Pass())); + EXPECT_EQ(MDnsCache::RecordAdded, cache_.UpdateDnsRecord(record2.Pass())); + + cache_.FindDnsRecords(0, "ghs.l.google.com", &results, default_time_); + + EXPECT_EQ(2u, results.size()); + EXPECT_EQ(default_time_, results.front()->time_created()); + + EXPECT_EQ("ghs.l.google.com", results[0]->name()); + EXPECT_EQ("ghs.l.google.com", results[1]->name()); + EXPECT_EQ(dns_protocol::kTypeA, + std::min(results[0]->type(), results[1]->type())); + EXPECT_EQ(dns_protocol::kTypeAAAA, + std::max(results[0]->type(), results[1]->type())); +} + +TEST_F(MDnsCacheTest, RemoveRecord) { + DnsRecordParser parser(kT1ResponseDatagram, sizeof(kT1ResponseDatagram), + sizeof(dns_protocol::Header)); + parser.SkipQuestion(); + + scoped_ptr<const RecordParsed> record1; + std::vector<const RecordParsed*> results; + + record1 = RecordParsed::CreateFrom(&parser, default_time_); + EXPECT_EQ(MDnsCache::RecordAdded, cache_.UpdateDnsRecord(record1.Pass())); + + cache_.FindDnsRecords(dns_protocol::kTypeCNAME, "codereview.chromium.org", + &results, default_time_); + + EXPECT_EQ(1u, results.size()); + + scoped_ptr<const RecordParsed> record_out = + cache_.RemoveRecord(results.front()); + + EXPECT_EQ(record_out.get(), results.front()); + + cache_.FindDnsRecords(dns_protocol::kTypeCNAME, "codereview.chromium.org", + &results, default_time_); + + EXPECT_EQ(0u, results.size()); +} + +} // namespace net |