OLD | NEW |
| (Empty) |
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: | |
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. | |
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. | |
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. | |
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. | |
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. | |
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. | |
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 | |
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 | |
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 | |
OLD | NEW |