1 // 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 <algorithm>
6
7 #include "base/bind.h"
8 #include "net/dns/dns_response.h"
9 #include "net/dns/dns_test_util.h"
10 #include "net/dns/mdns_cache.h"
11 #include "net/dns/record_parsed.h"
12 #include "net/dns/record_rdata.h"
13 #include "testing/gmock/include/gmock/gmock.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15
16 using ::testing::Return;
17 using ::testing::StrictMock;
18
19 namespace net {
20
21 static const uint8 kTestResponsesDifferentAnswers[] = {
22 // Answer 1
23 // ghs.l.google.com in DNS format.
24 3, 'g', 'h', 's',
25 1, 'l',
26 6, 'g', 'o', 'o', 'g', 'l', 'e',
27 3, 'c', 'o', 'm',
28 0x00,
29 0x00, 0x01, // TYPE is A.
30 0x00, 0x01, // CLASS is IN.
31 0, 0, 0, 53, // TTL (4 bytes) is 53 seconds.
32 0, 4, // RDLENGTH is 4 bytes.
33 74, 125, 95, 121, // RDATA is the IP: 74.125.95.121
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 0, 0, 0, 53, // TTL (4 bytes) is 53 seconds.
41 0, 4, // RDLENGTH is 4 bytes.
42 74, 125, 95, 122, // RDATA is the IP: 74.125.95.122
43 };
44
45 static const uint8 kTestResponsesSameAnswers[] = {
46 // Answer 1
47 // ghs.l.google.com in DNS format.
48 3, 'g', 'h', 's',
49 1, 'l',
50 6, 'g', 'o', 'o', 'g', 'l', 'e',
51 3, 'c', 'o', 'm',
52 0x00,
53 0x00, 0x01, // TYPE is A.
54 0x00, 0x01, // CLASS is IN.
55 0, 0, 0, 53, // TTL (4 bytes) is 53 seconds.
56 0, 4, // RDLENGTH is 4 bytes.
57 74, 125, 95, 121, // RDATA is the IP: 74.125.95.121
58
59 // Answer 2
60 // Pointer to answer 1
61 0xc0, 0x00,
62 0x00, 0x01, // TYPE is A.
63 0x00, 0x01, // CLASS is IN.
64 0, 0, 0, 112, // TTL (4 bytes) is 112 seconds.
65 0, 4, // RDLENGTH is 4 bytes.
66 74, 125, 95, 121, // RDATA is the IP: 74.125.95.121
67 };
68
69 static const uint8 kTestResponseTwoRecords[] = {
70 // Answer 1
71 // ghs.l.google.com in DNS format. (A)
72 3, 'g', 'h', 's',
73 1, 'l',
74 6, 'g', 'o', 'o', 'g', 'l', 'e',
75 3, 'c', 'o', 'm',
76 0x00,
77 0x00, 0x01, // TYPE is A.
78 0x00, 0x01, // CLASS is IN.
79 0, 0, 0, 53, // TTL (4 bytes) is 53 seconds.
80 0, 4, // RDLENGTH is 4 bytes.
81 74, 125, 95, 121, // RDATA is the IP: 74.125.95.121
82
83 // Answer 2
84 // ghs.l.google.com in DNS format. (AAAA)
85 3, 'g', 'h', 's',
86 1, 'l',
87 6, 'g', 'o', 'o', 'g', 'l', 'e',
88 3, 'c', 'o', 'm',
89 0x00,
90 0x00, 0x1c, // TYPE is AAA.
91 0x00, 0x01, // CLASS is IN.
92 0, 0, 0, 53, // TTL (4 bytes) is 53 seconds.
93 0, 16, // RDLENGTH is 16 bytes.
94 0x4a, 0x7d, 0x4a, 0x7d,
95 0x5f, 0x79, 0x5f, 0x79,
96 0x5f, 0x79, 0x5f, 0x79,
97 0x5f, 0x79, 0x5f, 0x79,
98 };
99
100 static const uint8 kTestResponsesGoodbyePacket[] = {
101 // Answer 1
102 // ghs.l.google.com in DNS format. (Goodbye packet)
103 3, 'g', 'h', 's',
104 1, 'l',
105 6, 'g', 'o', 'o', 'g', 'l', 'e',
106 3, 'c', 'o', 'm',
107 0x00,
108 0x00, 0x01, // TYPE is A.
109 0x00, 0x01, // CLASS is IN.
110 0, 0, 0, 0, // TTL (4 bytes) is zero.
111 0, 4, // RDLENGTH is 4 bytes.
112 74, 125, 95, 121, // RDATA is the IP: 74.125.95.121
113
114 // Answer 2
115 // ghs.l.google.com in DNS format.
116 3, 'g', 'h', 's',
117 1, 'l',
118 6, 'g', 'o', 'o', 'g', 'l', 'e',
119 3, 'c', 'o', 'm',
120 0x00,
121 0x00, 0x01, // TYPE is A.
122 0x00, 0x01, // CLASS is IN.
123 0, 0, 0, 53, // TTL (4 bytes) is 53 seconds.
124 0, 4, // RDLENGTH is 4 bytes.
125 74, 125, 95, 121, // RDATA is the IP: 74.125.95.121
126 };
127
128 class RecordRemovalMock {
129 public:
130 MOCK_METHOD1(OnRecordRemoved, void(const RecordParsed*));
131 };
132
133 class MDnsCacheTest : public ::testing::Test {
134 public:
MDnsCacheTest()135 MDnsCacheTest()
136 : default_time_(base::Time::FromDoubleT(1234.0)) {}
~MDnsCacheTest()137 virtual ~MDnsCacheTest() {}
138
139 protected:
140 base::Time default_time_;
141 StrictMock<RecordRemovalMock> record_removal_;
142 MDnsCache cache_;
143 };
144
145 // Test a single insert, corresponding lookup, and unsuccessful lookup.
TEST_F(MDnsCacheTest,InsertLookupSingle)146 TEST_F(MDnsCacheTest, InsertLookupSingle) {
147 DnsRecordParser parser(kT1ResponseDatagram, sizeof(kT1ResponseDatagram),
148 sizeof(dns_protocol::Header));
149 parser.SkipQuestion();
150
151 scoped_ptr<const RecordParsed> record1;
152 scoped_ptr<const RecordParsed> record2;
153 std::vector<const RecordParsed*> results;
154
155 record1 = RecordParsed::CreateFrom(&parser, default_time_);
156 record2 = RecordParsed::CreateFrom(&parser, default_time_);
157
158 EXPECT_EQ(MDnsCache::RecordAdded, cache_.UpdateDnsRecord(record1.Pass()));
159
160 EXPECT_EQ(MDnsCache::RecordAdded, cache_.UpdateDnsRecord(record2.Pass()));
161
162 cache_.FindDnsRecords(ARecordRdata::kType, "ghs.l.google.com", &results,
163 default_time_);
164
165 EXPECT_EQ(1u, results.size());
166 EXPECT_EQ(default_time_, results.front()->time_created());
167
168 EXPECT_EQ("ghs.l.google.com", results.front()->name());
169
170 results.clear();
171 cache_.FindDnsRecords(PtrRecordRdata::kType, "ghs.l.google.com", &results,
172 default_time_);
173
174 EXPECT_EQ(0u, results.size());
175 }
176
177 // Test that records expire when their ttl has passed.
TEST_F(MDnsCacheTest,Expiration)178 TEST_F(MDnsCacheTest, Expiration) {
179 DnsRecordParser parser(kT1ResponseDatagram, sizeof(kT1ResponseDatagram),
180 sizeof(dns_protocol::Header));
181 parser.SkipQuestion();
182 scoped_ptr<const RecordParsed> record1;
183 scoped_ptr<const RecordParsed> record2;
184
185 std::vector<const RecordParsed*> results;
186 const RecordParsed* record_to_be_deleted;
187
188 record1 = RecordParsed::CreateFrom(&parser, default_time_);
189 base::TimeDelta ttl1 = base::TimeDelta::FromSeconds(record1->ttl());
190
191 record2 = RecordParsed::CreateFrom(&parser, default_time_);
192 base::TimeDelta ttl2 = base::TimeDelta::FromSeconds(record2->ttl());
193 record_to_be_deleted = record2.get();
194
195 EXPECT_EQ(MDnsCache::RecordAdded, cache_.UpdateDnsRecord(record1.Pass()));
196 EXPECT_EQ(MDnsCache::RecordAdded, cache_.UpdateDnsRecord(record2.Pass()));
197
198 cache_.FindDnsRecords(ARecordRdata::kType, "ghs.l.google.com", &results,
199 default_time_);
200
201 EXPECT_EQ(1u, results.size());
202
203 EXPECT_EQ(default_time_ + ttl2, cache_.next_expiration());
204
205
206 cache_.FindDnsRecords(ARecordRdata::kType, "ghs.l.google.com", &results,
207 default_time_ + ttl2);
208
209 EXPECT_EQ(0u, results.size());
210
211 EXPECT_CALL(record_removal_, OnRecordRemoved(record_to_be_deleted));
212
213 cache_.CleanupRecords(default_time_ + ttl2, base::Bind(
214 &RecordRemovalMock::OnRecordRemoved, base::Unretained(&record_removal_)));
215
216 // To make sure that we've indeed removed them from the map, check no funny
217 // business happens once they're deleted for good.
218
219 EXPECT_EQ(default_time_ + ttl1, cache_.next_expiration());
220 cache_.FindDnsRecords(ARecordRdata::kType, "ghs.l.google.com", &results,
221 default_time_ + ttl2);
222
223 EXPECT_EQ(0u, results.size());
224 }
225
226 // Test that a new record replacing one with the same identity (name/rrtype for
227 // unique records) causes the cache to output a "record changed" event.
TEST_F(MDnsCacheTest,RecordChange)228 TEST_F(MDnsCacheTest, RecordChange) {
229 DnsRecordParser parser(kTestResponsesDifferentAnswers,
230 sizeof(kTestResponsesDifferentAnswers),
231 0);
232
233 scoped_ptr<const RecordParsed> record1;
234 scoped_ptr<const RecordParsed> record2;
235 std::vector<const RecordParsed*> results;
236
237 record1 = RecordParsed::CreateFrom(&parser, default_time_);
238 record2 = RecordParsed::CreateFrom(&parser, default_time_);
239
240 EXPECT_EQ(MDnsCache::RecordAdded, cache_.UpdateDnsRecord(record1.Pass()));
241 EXPECT_EQ(MDnsCache::RecordChanged,
242 cache_.UpdateDnsRecord(record2.Pass()));
243 }
244
245 // Test that a new record replacing an otherwise identical one already in the
246 // cache causes the cache to output a "no change" event.
TEST_F(MDnsCacheTest,RecordNoChange)247 TEST_F(MDnsCacheTest, RecordNoChange) {
248 DnsRecordParser parser(kTestResponsesSameAnswers,
249 sizeof(kTestResponsesSameAnswers),
250 0);
251
252 scoped_ptr<const RecordParsed> record1;
253 scoped_ptr<const RecordParsed> record2;
254 std::vector<const RecordParsed*> results;
255
256 record1 = RecordParsed::CreateFrom(&parser, default_time_);
257 record2 = RecordParsed::CreateFrom(&parser, default_time_ +
258 base::TimeDelta::FromSeconds(1));
259
260 EXPECT_EQ(MDnsCache::RecordAdded, cache_.UpdateDnsRecord(record1.Pass()));
261 EXPECT_EQ(MDnsCache::NoChange, cache_.UpdateDnsRecord(record2.Pass()));
262 }
263
264 // Test that the next expiration time of the cache is updated properly on record
265 // insertion.
TEST_F(MDnsCacheTest,RecordPreemptExpirationTime)266 TEST_F(MDnsCacheTest, RecordPreemptExpirationTime) {
267 DnsRecordParser parser(kTestResponsesSameAnswers,
268 sizeof(kTestResponsesSameAnswers),
269 0);
270
271 scoped_ptr<const RecordParsed> record1;
272 scoped_ptr<const RecordParsed> record2;
273 std::vector<const RecordParsed*> results;
274
275 record1 = RecordParsed::CreateFrom(&parser, default_time_);
276 record2 = RecordParsed::CreateFrom(&parser, default_time_);
277 base::TimeDelta ttl1 = base::TimeDelta::FromSeconds(record1->ttl());
278 base::TimeDelta ttl2 = base::TimeDelta::FromSeconds(record2->ttl());
279
280 EXPECT_EQ(base::Time(), cache_.next_expiration());
281 EXPECT_EQ(MDnsCache::RecordAdded, cache_.UpdateDnsRecord(record2.Pass()));
282 EXPECT_EQ(default_time_ + ttl2, cache_.next_expiration());
283 EXPECT_EQ(MDnsCache::NoChange, cache_.UpdateDnsRecord(record1.Pass()));
284 EXPECT_EQ(default_time_ + ttl1, cache_.next_expiration());
285 }
286
287 // Test that the cache handles mDNS "goodbye" packets correctly, not adding the
288 // records to the cache if they are not already there, and eventually removing
289 // records from the cache if they are.
TEST_F(MDnsCacheTest,GoodbyePacket)290 TEST_F(MDnsCacheTest, GoodbyePacket) {
291 DnsRecordParser parser(kTestResponsesGoodbyePacket,
292 sizeof(kTestResponsesGoodbyePacket),
293 0);
294
295 scoped_ptr<const RecordParsed> record_goodbye;
296 scoped_ptr<const RecordParsed> record_hello;
297 scoped_ptr<const RecordParsed> record_goodbye2;
298 std::vector<const RecordParsed*> results;
299
300 record_goodbye = RecordParsed::CreateFrom(&parser, default_time_);
301 record_hello = RecordParsed::CreateFrom(&parser, default_time_);
302 parser = DnsRecordParser(kTestResponsesGoodbyePacket,
303 sizeof(kTestResponsesGoodbyePacket),
304 0);
305 record_goodbye2 = RecordParsed::CreateFrom(&parser, default_time_);
306
307 base::TimeDelta ttl = base::TimeDelta::FromSeconds(record_hello->ttl());
308
309 EXPECT_EQ(base::Time(), cache_.next_expiration());
310 EXPECT_EQ(MDnsCache::NoChange, cache_.UpdateDnsRecord(record_goodbye.Pass()));
311 EXPECT_EQ(base::Time(), cache_.next_expiration());
312 EXPECT_EQ(MDnsCache::RecordAdded,
313 cache_.UpdateDnsRecord(record_hello.Pass()));
314 EXPECT_EQ(default_time_ + ttl, cache_.next_expiration());
315 EXPECT_EQ(MDnsCache::NoChange,
316 cache_.UpdateDnsRecord(record_goodbye2.Pass()));
317 EXPECT_EQ(default_time_ + base::TimeDelta::FromSeconds(1),
318 cache_.next_expiration());
319 }
320
TEST_F(MDnsCacheTest,AnyRRType)321 TEST_F(MDnsCacheTest, AnyRRType) {
322 DnsRecordParser parser(kTestResponseTwoRecords,
323 sizeof(kTestResponseTwoRecords),
324 0);
325
326 scoped_ptr<const RecordParsed> record1;
327 scoped_ptr<const RecordParsed> record2;
328 std::vector<const RecordParsed*> results;
329
330 record1 = RecordParsed::CreateFrom(&parser, default_time_);
331 record2 = RecordParsed::CreateFrom(&parser, default_time_);
332 EXPECT_EQ(MDnsCache::RecordAdded, cache_.UpdateDnsRecord(record1.Pass()));
333 EXPECT_EQ(MDnsCache::RecordAdded, cache_.UpdateDnsRecord(record2.Pass()));
334
335 cache_.FindDnsRecords(0, "ghs.l.google.com", &results, default_time_);
336
337 EXPECT_EQ(2u, results.size());
338 EXPECT_EQ(default_time_, results.front()->time_created());
339
340 EXPECT_EQ("ghs.l.google.com", results[0]->name());
341 EXPECT_EQ("ghs.l.google.com", results[1]->name());
342 EXPECT_EQ(dns_protocol::kTypeA,
343 std::min(results[0]->type(), results[1]->type()));
344 EXPECT_EQ(dns_protocol::kTypeAAAA,
345 std::max(results[0]->type(), results[1]->type()));
346 }
347
TEST_F(MDnsCacheTest,RemoveRecord)348 TEST_F(MDnsCacheTest, RemoveRecord) {
349 DnsRecordParser parser(kT1ResponseDatagram, sizeof(kT1ResponseDatagram),
350 sizeof(dns_protocol::Header));
351 parser.SkipQuestion();
352
353 scoped_ptr<const RecordParsed> record1;
354 std::vector<const RecordParsed*> results;
355
356 record1 = RecordParsed::CreateFrom(&parser, default_time_);
357 EXPECT_EQ(MDnsCache::RecordAdded, cache_.UpdateDnsRecord(record1.Pass()));
358
359 cache_.FindDnsRecords(dns_protocol::kTypeCNAME, "codereview.chromium.org",
360 &results, default_time_);
361
362 EXPECT_EQ(1u, results.size());
363
364 scoped_ptr<const RecordParsed> record_out =
365 cache_.RemoveRecord(results.front());
366
367 EXPECT_EQ(record_out.get(), results.front());
368
369 cache_.FindDnsRecords(dns_protocol::kTypeCNAME, "codereview.chromium.org",
370 &results, default_time_);
371
372 EXPECT_EQ(0u, results.size());
373 }
374
375 } // namespace net
376