blob: c12ad6b6ec3c830fbb88489aea4d8c41df3ea5f4 [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
[email protected]21df16962013-07-01 17:39:535#include <algorithm>
6
[email protected]f6a9add2013-05-23 00:56:367#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
16using ::testing::Return;
17using ::testing::StrictMock;
18
19namespace net {
20
21static const uint8 kTestResponsesDifferentAnswers[] = {
22 // Answer 1
23 // ghs.l.google.com in DNS format.
[email protected]5942c9b92013-07-31 10:31:1024 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
[email protected]f6a9add2013-05-23 00:56:3634
35 // Answer 2
36 // Pointer to answer 1
[email protected]5942c9b92013-07-31 10:31:1037 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
[email protected]f6a9add2013-05-23 00:56:3643};
44
45static const uint8 kTestResponsesSameAnswers[] = {
46 // Answer 1
47 // ghs.l.google.com in DNS format.
[email protected]5942c9b92013-07-31 10:31:1048 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
[email protected]f6a9add2013-05-23 00:56:3658
59 // Answer 2
60 // Pointer to answer 1
[email protected]5942c9b92013-07-31 10:31:1061 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
[email protected]f6a9add2013-05-23 00:56:3667};
68
[email protected]21df16962013-07-01 17:39:5369static const uint8 kTestResponseTwoRecords[] = {
70 // Answer 1
71 // ghs.l.google.com in DNS format. (A)
[email protected]5942c9b92013-07-31 10:31:1072 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
[email protected]21df16962013-07-01 17:39:5383 // Answer 2
84 // ghs.l.google.com in DNS format. (AAAA)
[email protected]5942c9b92013-07-31 10:31:1085 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,
[email protected]21df16962013-07-01 17:39:5398};
99
[email protected]35f3a6b2013-07-11 19:59:03100static const uint8 kTestResponsesGoodbyePacket[] = {
101 // Answer 1
102 // ghs.l.google.com in DNS format. (Goodbye packet)
[email protected]5942c9b92013-07-31 10:31:10103 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
[email protected]35f3a6b2013-07-11 19:59:03113
114 // Answer 2
115 // ghs.l.google.com in DNS format.
[email protected]5942c9b92013-07-31 10:31:10116 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
[email protected]35f3a6b2013-07-11 19:59:03126};
127
[email protected]f6a9add2013-05-23 00:56:36128class RecordRemovalMock {
129 public:
130 MOCK_METHOD1(OnRecordRemoved, void(const RecordParsed*));
131};
132
133class MDnsCacheTest : public ::testing::Test {
134 public:
135 MDnsCacheTest()
136 : default_time_(base::Time::FromDoubleT(1234.0)) {}
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.
146TEST_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.
178TEST_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.
228TEST_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.
247TEST_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.
266TEST_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
[email protected]35f3a6b2013-07-11 19:59:03287// 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.
290TEST_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
[email protected]21df16962013-07-01 17:39:53321TEST_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
348TEST_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