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
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.
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
45static 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
69static 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
100static 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
128class 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
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.
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
321TEST_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
376