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.
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"
16 using ::testing::Return
;
17 using ::testing::StrictMock
;
21 static const uint8 kTestResponsesDifferentAnswers
[] = {
23 // ghs.l.google.com in DNS format.
26 6, 'g', 'o', 'o', 'g', 'l', 'e',
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
36 // Pointer to answer 1
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
45 static const uint8 kTestResponsesSameAnswers
[] = {
47 // ghs.l.google.com in DNS format.
50 6, 'g', 'o', 'o', 'g', 'l', 'e',
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
60 // Pointer to answer 1
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
69 static const uint8 kTestResponseTwoRecords
[] = {
71 // ghs.l.google.com in DNS format. (A)
74 6, 'g', 'o', 'o', 'g', 'l', 'e',
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
84 // ghs.l.google.com in DNS format. (AAAA)
87 6, 'g', 'o', 'o', 'g', 'l', 'e',
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,
100 static const uint8 kTestResponsesGoodbyePacket
[] = {
102 // ghs.l.google.com in DNS format. (Goodbye packet)
105 6, 'g', 'o', 'o', 'g', 'l', 'e',
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
115 // ghs.l.google.com in DNS format.
118 6, 'g', 'o', 'o', 'g', 'l', 'e',
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
128 class RecordRemovalMock
{
130 MOCK_METHOD1(OnRecordRemoved
, void(const RecordParsed
*));
133 class MDnsCacheTest
: public ::testing::Test
{
136 : default_time_(base::Time::FromDoubleT(1234.0)) {}
137 virtual ~MDnsCacheTest() {}
140 base::Time default_time_
;
141 StrictMock
<RecordRemovalMock
> record_removal_
;
145 // Test a single insert, corresponding lookup, and unsuccessful lookup.
146 TEST_F(MDnsCacheTest
, InsertLookupSingle
) {
147 DnsRecordParser
parser(kT1ResponseDatagram
, sizeof(kT1ResponseDatagram
),
148 sizeof(dns_protocol::Header
));
149 parser
.SkipQuestion();
151 scoped_ptr
<const RecordParsed
> record1
;
152 scoped_ptr
<const RecordParsed
> record2
;
153 std::vector
<const RecordParsed
*> results
;
155 record1
= RecordParsed::CreateFrom(&parser
, default_time_
);
156 record2
= RecordParsed::CreateFrom(&parser
, default_time_
);
158 EXPECT_EQ(MDnsCache::RecordAdded
, cache_
.UpdateDnsRecord(record1
.Pass()));
160 EXPECT_EQ(MDnsCache::RecordAdded
, cache_
.UpdateDnsRecord(record2
.Pass()));
162 cache_
.FindDnsRecords(ARecordRdata::kType
, "ghs.l.google.com", &results
,
165 EXPECT_EQ(1u, results
.size());
166 EXPECT_EQ(default_time_
, results
.front()->time_created());
168 EXPECT_EQ("ghs.l.google.com", results
.front()->name());
171 cache_
.FindDnsRecords(PtrRecordRdata::kType
, "ghs.l.google.com", &results
,
174 EXPECT_EQ(0u, results
.size());
177 // Test that records expire when their ttl has passed.
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
;
185 std::vector
<const RecordParsed
*> results
;
186 const RecordParsed
* record_to_be_deleted
;
188 record1
= RecordParsed::CreateFrom(&parser
, default_time_
);
189 base::TimeDelta ttl1
= base::TimeDelta::FromSeconds(record1
->ttl());
191 record2
= RecordParsed::CreateFrom(&parser
, default_time_
);
192 base::TimeDelta ttl2
= base::TimeDelta::FromSeconds(record2
->ttl());
193 record_to_be_deleted
= record2
.get();
195 EXPECT_EQ(MDnsCache::RecordAdded
, cache_
.UpdateDnsRecord(record1
.Pass()));
196 EXPECT_EQ(MDnsCache::RecordAdded
, cache_
.UpdateDnsRecord(record2
.Pass()));
198 cache_
.FindDnsRecords(ARecordRdata::kType
, "ghs.l.google.com", &results
,
201 EXPECT_EQ(1u, results
.size());
203 EXPECT_EQ(default_time_
+ ttl2
, cache_
.next_expiration());
206 cache_
.FindDnsRecords(ARecordRdata::kType
, "ghs.l.google.com", &results
,
207 default_time_
+ ttl2
);
209 EXPECT_EQ(0u, results
.size());
211 EXPECT_CALL(record_removal_
, OnRecordRemoved(record_to_be_deleted
));
213 cache_
.CleanupRecords(default_time_
+ ttl2
, base::Bind(
214 &RecordRemovalMock::OnRecordRemoved
, base::Unretained(&record_removal_
)));
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.
219 EXPECT_EQ(default_time_
+ ttl1
, cache_
.next_expiration());
220 cache_
.FindDnsRecords(ARecordRdata::kType
, "ghs.l.google.com", &results
,
221 default_time_
+ ttl2
);
223 EXPECT_EQ(0u, results
.size());
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.
228 TEST_F(MDnsCacheTest
, RecordChange
) {
229 DnsRecordParser
parser(kTestResponsesDifferentAnswers
,
230 sizeof(kTestResponsesDifferentAnswers
),
233 scoped_ptr
<const RecordParsed
> record1
;
234 scoped_ptr
<const RecordParsed
> record2
;
235 std::vector
<const RecordParsed
*> results
;
237 record1
= RecordParsed::CreateFrom(&parser
, default_time_
);
238 record2
= RecordParsed::CreateFrom(&parser
, default_time_
);
240 EXPECT_EQ(MDnsCache::RecordAdded
, cache_
.UpdateDnsRecord(record1
.Pass()));
241 EXPECT_EQ(MDnsCache::RecordChanged
,
242 cache_
.UpdateDnsRecord(record2
.Pass()));
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.
247 TEST_F(MDnsCacheTest
, RecordNoChange
) {
248 DnsRecordParser
parser(kTestResponsesSameAnswers
,
249 sizeof(kTestResponsesSameAnswers
),
252 scoped_ptr
<const RecordParsed
> record1
;
253 scoped_ptr
<const RecordParsed
> record2
;
254 std::vector
<const RecordParsed
*> results
;
256 record1
= RecordParsed::CreateFrom(&parser
, default_time_
);
257 record2
= RecordParsed::CreateFrom(&parser
, default_time_
+
258 base::TimeDelta::FromSeconds(1));
260 EXPECT_EQ(MDnsCache::RecordAdded
, cache_
.UpdateDnsRecord(record1
.Pass()));
261 EXPECT_EQ(MDnsCache::NoChange
, cache_
.UpdateDnsRecord(record2
.Pass()));
264 // Test that the next expiration time of the cache is updated properly on record
266 TEST_F(MDnsCacheTest
, RecordPreemptExpirationTime
) {
267 DnsRecordParser
parser(kTestResponsesSameAnswers
,
268 sizeof(kTestResponsesSameAnswers
),
271 scoped_ptr
<const RecordParsed
> record1
;
272 scoped_ptr
<const RecordParsed
> record2
;
273 std::vector
<const RecordParsed
*> results
;
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());
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());
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.
290 TEST_F(MDnsCacheTest
, GoodbyePacket
) {
291 DnsRecordParser
parser(kTestResponsesGoodbyePacket
,
292 sizeof(kTestResponsesGoodbyePacket
),
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
;
300 record_goodbye
= RecordParsed::CreateFrom(&parser
, default_time_
);
301 record_hello
= RecordParsed::CreateFrom(&parser
, default_time_
);
302 parser
= DnsRecordParser(kTestResponsesGoodbyePacket
,
303 sizeof(kTestResponsesGoodbyePacket
),
305 record_goodbye2
= RecordParsed::CreateFrom(&parser
, default_time_
);
307 base::TimeDelta ttl
= base::TimeDelta::FromSeconds(record_hello
->ttl());
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());
321 TEST_F(MDnsCacheTest
, AnyRRType
) {
322 DnsRecordParser
parser(kTestResponseTwoRecords
,
323 sizeof(kTestResponseTwoRecords
),
326 scoped_ptr
<const RecordParsed
> record1
;
327 scoped_ptr
<const RecordParsed
> record2
;
328 std::vector
<const RecordParsed
*> results
;
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()));
335 cache_
.FindDnsRecords(0, "ghs.l.google.com", &results
, default_time_
);
337 EXPECT_EQ(2u, results
.size());
338 EXPECT_EQ(default_time_
, results
.front()->time_created());
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()));
348 TEST_F(MDnsCacheTest
, RemoveRecord
) {
349 DnsRecordParser
parser(kT1ResponseDatagram
, sizeof(kT1ResponseDatagram
),
350 sizeof(dns_protocol::Header
));
351 parser
.SkipQuestion();
353 scoped_ptr
<const RecordParsed
> record1
;
354 std::vector
<const RecordParsed
*> results
;
356 record1
= RecordParsed::CreateFrom(&parser
, default_time_
);
357 EXPECT_EQ(MDnsCache::RecordAdded
, cache_
.UpdateDnsRecord(record1
.Pass()));
359 cache_
.FindDnsRecords(dns_protocol::kTypeCNAME
, "codereview.chromium.org",
360 &results
, default_time_
);
362 EXPECT_EQ(1u, results
.size());
364 scoped_ptr
<const RecordParsed
> record_out
=
365 cache_
.RemoveRecord(results
.front());
367 EXPECT_EQ(record_out
.get(), results
.front());
369 cache_
.FindDnsRecords(dns_protocol::kTypeCNAME
, "codereview.chromium.org",
370 &results
, default_time_
);
372 EXPECT_EQ(0u, results
.size());