summaryrefslogtreecommitdiff
path: root/chromium/net/dns/mdns_cache_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/net/dns/mdns_cache_unittest.cc')
-rw-r--r--chromium/net/dns/mdns_cache_unittest.cc375
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