blob: 69de38aaf7100dfa94131a03f133f4ea213ffaa7 [file] [log] [blame]
[email protected]f6a9add2013-05-23 00:56:361// Copyright (c) 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/bind.h"
6#include "net/dns/dns_response.h"
7#include "net/dns/dns_test_util.h"
8#include "net/dns/mdns_cache.h"
9#include "net/dns/record_parsed.h"
10#include "net/dns/record_rdata.h"
11#include "testing/gmock/include/gmock/gmock.h"
12#include "testing/gtest/include/gtest/gtest.h"
13
14using ::testing::Return;
15using ::testing::StrictMock;
16
17namespace net {
18
19static const uint8 kTestResponsesDifferentAnswers[] = {
20 // Answer 1
21 // ghs.l.google.com in DNS format.
22 0x03, 'g', 'h', 's',
23 0x01, 'l',
24 0x06, 'g', 'o', 'o', 'g', 'l', 'e',
25 0x03, 'c', 'o', 'm',
26 0x00,
27 0x00, 0x01, // TYPE is A.
28 0x00, 0x01, // CLASS is IN.
29 0x00, 0x00, // TTL (4 bytes) is 53 seconds.
30 0x00, 0x35,
31 0x00, 0x04, // RDLENGTH is 4 bytes.
32 0x4a, 0x7d, // RDATA is the IP: 74.125.95.121
33 0x5f, 0x79,
34
35 // Answer 2
36 // Pointer to answer 1
37 0xc0, 0x00,
38 0x00, 0x01, // TYPE is A.
39 0x00, 0x01, // CLASS is IN.
40 0x00, 0x00, // TTL (4 bytes) is 53 seconds.
41 0x00, 0x35,
42 0x00, 0x04, // RDLENGTH is 4 bytes.
43 0x4a, 0x7d, // RDATA is the IP: 74.125.95.122
44 0x5f, 0x80,
45};
46
47static const uint8 kTestResponsesSameAnswers[] = {
48 // Answer 1
49 // ghs.l.google.com in DNS format.
50 0x03, 'g', 'h', 's',
51 0x01, 'l',
52 0x06, 'g', 'o', 'o', 'g', 'l', 'e',
53 0x03, 'c', 'o', 'm',
54 0x00,
55 0x00, 0x01, // TYPE is A.
56 0x00, 0x01, // CLASS is IN.
57 0x00, 0x00, // TTL (4 bytes) is 53 seconds.
58 0x00, 0x35,
59 0x00, 0x04, // RDLENGTH is 4 bytes.
60 0x4a, 0x7d, // RDATA is the IP: 74.125.95.121
61 0x5f, 0x79,
62
63 // Answer 2
64 // Pointer to answer 1
65 0xc0, 0x00,
66 0x00, 0x01, // TYPE is A.
67 0x00, 0x01, // CLASS is IN.
68 0x00, 0x00, // TTL (4 bytes) is 112 seconds.
69 0x00, 0x70,
70 0x00, 0x04, // RDLENGTH is 4 bytes.
71 0x4a, 0x7d, // RDATA is the IP: 74.125.95.121
72 0x5f, 0x79,
73};
74
75class RecordRemovalMock {
76 public:
77 MOCK_METHOD1(OnRecordRemoved, void(const RecordParsed*));
78};
79
80class MDnsCacheTest : public ::testing::Test {
81 public:
82 MDnsCacheTest()
83 : default_time_(base::Time::FromDoubleT(1234.0)) {}
84 virtual ~MDnsCacheTest() {}
85
86 protected:
87 base::Time default_time_;
88 StrictMock<RecordRemovalMock> record_removal_;
89 MDnsCache cache_;
90};
91
92// Test a single insert, corresponding lookup, and unsuccessful lookup.
93TEST_F(MDnsCacheTest, InsertLookupSingle) {
94 DnsRecordParser parser(kT1ResponseDatagram, sizeof(kT1ResponseDatagram),
95 sizeof(dns_protocol::Header));
96 parser.SkipQuestion();
97
98 scoped_ptr<const RecordParsed> record1;
99 scoped_ptr<const RecordParsed> record2;
100 std::vector<const RecordParsed*> results;
101
102 record1 = RecordParsed::CreateFrom(&parser, default_time_);
103 record2 = RecordParsed::CreateFrom(&parser, default_time_);
104
105 EXPECT_EQ(MDnsCache::RecordAdded, cache_.UpdateDnsRecord(record1.Pass()));
106
107 EXPECT_EQ(MDnsCache::RecordAdded, cache_.UpdateDnsRecord(record2.Pass()));
108
109 cache_.FindDnsRecords(ARecordRdata::kType, "ghs.l.google.com", &results,
110 default_time_);
111
112 EXPECT_EQ(1u, results.size());
113 EXPECT_EQ(default_time_, results.front()->time_created());
114
115 EXPECT_EQ("ghs.l.google.com", results.front()->name());
116
117 results.clear();
118 cache_.FindDnsRecords(PtrRecordRdata::kType, "ghs.l.google.com", &results,
119 default_time_);
120
121 EXPECT_EQ(0u, results.size());
122}
123
124// Test that records expire when their ttl has passed.
125TEST_F(MDnsCacheTest, Expiration) {
126 DnsRecordParser parser(kT1ResponseDatagram, sizeof(kT1ResponseDatagram),
127 sizeof(dns_protocol::Header));
128 parser.SkipQuestion();
129 scoped_ptr<const RecordParsed> record1;
130 scoped_ptr<const RecordParsed> record2;
131
132 std::vector<const RecordParsed*> results;
133 const RecordParsed* record_to_be_deleted;
134
135 record1 = RecordParsed::CreateFrom(&parser, default_time_);
136 base::TimeDelta ttl1 = base::TimeDelta::FromSeconds(record1->ttl());
137
138 record2 = RecordParsed::CreateFrom(&parser, default_time_);
139 base::TimeDelta ttl2 = base::TimeDelta::FromSeconds(record2->ttl());
140 record_to_be_deleted = record2.get();
141
142 EXPECT_EQ(MDnsCache::RecordAdded, cache_.UpdateDnsRecord(record1.Pass()));
143 EXPECT_EQ(MDnsCache::RecordAdded, cache_.UpdateDnsRecord(record2.Pass()));
144
145 cache_.FindDnsRecords(ARecordRdata::kType, "ghs.l.google.com", &results,
146 default_time_);
147
148 EXPECT_EQ(1u, results.size());
149
150 EXPECT_EQ(default_time_ + ttl2, cache_.next_expiration());
151
152
153 cache_.FindDnsRecords(ARecordRdata::kType, "ghs.l.google.com", &results,
154 default_time_ + ttl2);
155
156 EXPECT_EQ(0u, results.size());
157
158 EXPECT_CALL(record_removal_, OnRecordRemoved(record_to_be_deleted));
159
160 cache_.CleanupRecords(default_time_ + ttl2, base::Bind(
161 &RecordRemovalMock::OnRecordRemoved, base::Unretained(&record_removal_)));
162
163 // To make sure that we've indeed removed them from the map, check no funny
164 // business happens once they're deleted for good.
165
166 EXPECT_EQ(default_time_ + ttl1, cache_.next_expiration());
167 cache_.FindDnsRecords(ARecordRdata::kType, "ghs.l.google.com", &results,
168 default_time_ + ttl2);
169
170 EXPECT_EQ(0u, results.size());
171}
172
173// Test that a new record replacing one with the same identity (name/rrtype for
174// unique records) causes the cache to output a "record changed" event.
175TEST_F(MDnsCacheTest, RecordChange) {
176 DnsRecordParser parser(kTestResponsesDifferentAnswers,
177 sizeof(kTestResponsesDifferentAnswers),
178 0);
179
180 scoped_ptr<const RecordParsed> record1;
181 scoped_ptr<const RecordParsed> record2;
182 std::vector<const RecordParsed*> results;
183
184 record1 = RecordParsed::CreateFrom(&parser, default_time_);
185 record2 = RecordParsed::CreateFrom(&parser, default_time_);
186
187 EXPECT_EQ(MDnsCache::RecordAdded, cache_.UpdateDnsRecord(record1.Pass()));
188 EXPECT_EQ(MDnsCache::RecordChanged,
189 cache_.UpdateDnsRecord(record2.Pass()));
190}
191
192// Test that a new record replacing an otherwise identical one already in the
193// cache causes the cache to output a "no change" event.
194TEST_F(MDnsCacheTest, RecordNoChange) {
195 DnsRecordParser parser(kTestResponsesSameAnswers,
196 sizeof(kTestResponsesSameAnswers),
197 0);
198
199 scoped_ptr<const RecordParsed> record1;
200 scoped_ptr<const RecordParsed> record2;
201 std::vector<const RecordParsed*> results;
202
203 record1 = RecordParsed::CreateFrom(&parser, default_time_);
204 record2 = RecordParsed::CreateFrom(&parser, default_time_ +
205 base::TimeDelta::FromSeconds(1));
206
207 EXPECT_EQ(MDnsCache::RecordAdded, cache_.UpdateDnsRecord(record1.Pass()));
208 EXPECT_EQ(MDnsCache::NoChange, cache_.UpdateDnsRecord(record2.Pass()));
209}
210
211// Test that the next expiration time of the cache is updated properly on record
212// insertion.
213TEST_F(MDnsCacheTest, RecordPreemptExpirationTime) {
214 DnsRecordParser parser(kTestResponsesSameAnswers,
215 sizeof(kTestResponsesSameAnswers),
216 0);
217
218 scoped_ptr<const RecordParsed> record1;
219 scoped_ptr<const RecordParsed> record2;
220 std::vector<const RecordParsed*> results;
221
222 record1 = RecordParsed::CreateFrom(&parser, default_time_);
223 record2 = RecordParsed::CreateFrom(&parser, default_time_);
224 base::TimeDelta ttl1 = base::TimeDelta::FromSeconds(record1->ttl());
225 base::TimeDelta ttl2 = base::TimeDelta::FromSeconds(record2->ttl());
226
227 EXPECT_EQ(base::Time(), cache_.next_expiration());
228 EXPECT_EQ(MDnsCache::RecordAdded, cache_.UpdateDnsRecord(record2.Pass()));
229 EXPECT_EQ(default_time_ + ttl2, cache_.next_expiration());
230 EXPECT_EQ(MDnsCache::NoChange, cache_.UpdateDnsRecord(record1.Pass()));
231 EXPECT_EQ(default_time_ + ttl1, cache_.next_expiration());
232}
233
234} // namespace net