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 "base/memory/ref_counted.h" | |
6 #include "base/message_loop.h" | |
7 #include "net/base/rand_callback.h" | |
8 #include "net/base/test_completion_callback.h" | |
9 #include "net/dns/mdns_client_impl.h" | |
10 #include "net/dns/record_rdata.h" | |
11 #include "net/udp/udp_client_socket.h" | |
12 #include "testing/gmock/include/gmock/gmock.h" | |
13 #include "testing/gtest/include/gtest/gtest.h" | |
14 | |
15 using ::testing::Invoke; | |
16 using ::testing::InvokeWithoutArgs; | |
17 using ::testing::StrictMock; | |
18 using ::testing::Exactly; | |
19 using ::testing::Return; | |
20 using ::testing::SaveArg; | |
21 using ::testing::_; | |
22 | |
23 namespace net { | |
24 | |
25 namespace { | |
26 | |
27 const char kSamplePacket1[] = { | |
28 // Header | |
29 0x00, 0x00, // ID is zeroed out | |
30 0x81, 0x80, // Standard query response, RA, no error | |
31 0x00, 0x00, // No questions (for simplicity) | |
32 0x00, 0x02, // 2 RRs (answers) | |
33 0x00, 0x00, // 0 authority RRs | |
34 0x00, 0x00, // 0 additional RRs | |
35 | |
36 // Answer 1 | |
37 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't', | |
38 0x04, '_', 't', 'c', 'p', | |
39 0x05, 'l', 'o', 'c', 'a', 'l', | |
40 0x00, | |
41 0x00, 0x0c, // TYPE is PTR. | |
42 0x00, 0x01, // CLASS is IN. | |
43 0x00, 0x00, // TTL (4 bytes) is 1 second; | |
44 0x00, 0x01, | |
45 0x00, 0x08, // RDLENGTH is 8 bytes. | |
46 0x05, 'h', 'e', 'l', 'l', 'o', | |
47 0xc0, 0x0c, | |
48 | |
49 // Answer 2 | |
50 0x08, '_', 'p', 'r', 'i', 'n', 't', 'e', 'r', | |
51 0xc0, 0x14, // Pointer to "._tcp.local" | |
52 0x00, 0x0c, // TYPE is PTR. | |
53 0x00, 0x01, // CLASS is IN. | |
54 0x00, 0x01, // TTL (4 bytes) is 20 hours, 47 minutes, 49 seconds. | |
55 0x24, 0x75, | |
56 0x00, 0x08, // RDLENGTH is 8 bytes. | |
57 0x05, 'h', 'e', 'l', 'l', 'o', | |
58 0xc0, 0x32 | |
59 }; | |
60 | |
61 const char kCorruptedPacketBadQuestion[] = { | |
62 // Header | |
63 0x00, 0x00, // ID is zeroed out | |
64 0x81, 0x80, // Standard query response, RA, no error | |
65 0x00, 0x01, // One question | |
66 0x00, 0x02, // 2 RRs (answers) | |
67 0x00, 0x00, // 0 authority RRs | |
68 0x00, 0x00, // 0 additional RRs | |
69 | |
70 // Question is corrupted and cannot be read. | |
71 0x99, 'h', 'e', 'l', 'l', 'o', | |
72 0x00, | |
73 0x00, 0x00, | |
74 0x00, 0x00, | |
75 | |
76 // Answer 1 | |
77 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't', | |
78 0x04, '_', 't', 'c', 'p', | |
79 0x05, 'l', 'o', 'c', 'a', 'l', | |
80 0x00, | |
81 0x00, 0x0c, // TYPE is PTR. | |
82 0x00, 0x01, // CLASS is IN. | |
83 0x00, 0x01, // TTL (4 bytes) is 20 hours, 47 minutes, 48 seconds. | |
84 0x24, 0x74, | |
85 0x00, 0x99, // RDLENGTH is impossible | |
86 0x05, 'h', 'e', 'l', 'l', 'o', | |
87 0xc0, 0x0c, | |
88 | |
89 // Answer 2 | |
90 0x08, '_', 'p', 'r', // Useless trailing data. | |
91 }; | |
92 | |
93 const char kCorruptedPacketUnsalvagable[] = { | |
94 // Header | |
95 0x00, 0x00, // ID is zeroed out | |
96 0x81, 0x80, // Standard query response, RA, no error | |
97 0x00, 0x00, // No questions (for simplicity) | |
98 0x00, 0x02, // 2 RRs (answers) | |
99 0x00, 0x00, // 0 authority RRs | |
100 0x00, 0x00, // 0 additional RRs | |
101 | |
102 // Answer 1 | |
103 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't', | |
104 0x04, '_', 't', 'c', 'p', | |
105 0x05, 'l', 'o', 'c', 'a', 'l', | |
106 0x00, | |
107 0x00, 0x0c, // TYPE is PTR. | |
108 0x00, 0x01, // CLASS is IN. | |
109 0x00, 0x01, // TTL (4 bytes) is 20 hours, 47 minutes, 48 seconds. | |
110 0x24, 0x74, | |
111 0x00, 0x99, // RDLENGTH is impossible | |
112 0x05, 'h', 'e', 'l', 'l', 'o', | |
113 0xc0, 0x0c, | |
114 | |
115 // Answer 2 | |
116 0x08, '_', 'p', 'r', // Useless trailing data. | |
117 }; | |
118 | |
119 const char kCorruptedPacketSalvagable[] = { | |
120 // Header | |
121 0x00, 0x00, // ID is zeroed out | |
122 0x81, 0x80, // Standard query response, RA, no error | |
123 0x00, 0x00, // No questions (for simplicity) | |
124 0x00, 0x02, // 2 RRs (answers) | |
125 0x00, 0x00, // 0 authority RRs | |
126 0x00, 0x00, // 0 additional RRs | |
127 | |
128 // Answer 1 | |
129 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't', | |
130 0x04, '_', 't', 'c', 'p', | |
131 0x05, 'l', 'o', 'c', 'a', 'l', | |
132 0x00, | |
133 0x00, 0x0c, // TYPE is PTR. | |
134 0x00, 0x01, // CLASS is IN. | |
135 0x00, 0x01, // TTL (4 bytes) is 20 hours, 47 minutes, 48 seconds. | |
136 0x24, 0x74, | |
137 0x00, 0x08, // RDLENGTH is 8 bytes. | |
138 0x99, 'h', 'e', 'l', 'l', 'o', // Bad RDATA format. | |
139 0xc0, 0x0c, | |
140 | |
141 // Answer 2 | |
142 0x08, '_', 'p', 'r', 'i', 'n', 't', 'e', 'r', | |
143 0xc0, 0x14, // Pointer to "._tcp.local" | |
144 0x00, 0x0c, // TYPE is PTR. | |
145 0x00, 0x01, // CLASS is IN. | |
146 0x00, 0x01, // TTL (4 bytes) is 20 hours, 47 minutes, 49 seconds. | |
147 0x24, 0x75, | |
148 0x00, 0x08, // RDLENGTH is 8 bytes. | |
149 0x05, 'h', 'e', 'l', 'l', 'o', | |
150 0xc0, 0x32 | |
151 }; | |
152 | |
153 const char kSamplePacket2[] = { | |
154 // Header | |
155 0x00, 0x00, // ID is zeroed out | |
156 0x81, 0x80, // Standard query response, RA, no error | |
157 0x00, 0x00, // No questions (for simplicity) | |
158 0x00, 0x02, // 2 RRs (answers) | |
159 0x00, 0x00, // 0 authority RRs | |
160 0x00, 0x00, // 0 additional RRs | |
161 | |
162 // Answer 1 | |
163 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't', | |
164 0x04, '_', 't', 'c', 'p', | |
165 0x05, 'l', 'o', 'c', 'a', 'l', | |
166 0x00, | |
167 0x00, 0x0c, // TYPE is PTR. | |
168 0x00, 0x01, // CLASS is IN. | |
169 0x00, 0x01, // TTL (4 bytes) is 20 hours, 47 minutes, 48 seconds. | |
170 0x24, 0x74, | |
171 0x00, 0x08, // RDLENGTH is 8 bytes. | |
172 0x05, 'z', 'z', 'z', 'z', 'z', | |
173 0xc0, 0x0c, | |
174 | |
175 // Answer 2 | |
176 0x08, '_', 'p', 'r', 'i', 'n', 't', 'e', 'r', | |
177 0xc0, 0x14, // Pointer to "._tcp.local" | |
178 0x00, 0x0c, // TYPE is PTR. | |
179 0x00, 0x01, // CLASS is IN. | |
180 0x00, 0x01, // TTL (4 bytes) is 20 hours, 47 minutes, 48 seconds. | |
181 0x24, 0x74, | |
182 0x00, 0x08, // RDLENGTH is 8 bytes. | |
183 0x05, 'z', 'z', 'z', 'z', 'z', | |
184 0xc0, 0x32 | |
185 }; | |
186 | |
187 const char kQueryPacketPrivet[] = { | |
188 // Header | |
189 0x00, 0x00, // ID is zeroed out | |
190 0x01, 0x00, // RD flag set | |
191 0x00, 0x01, // One question. | |
192 0x00, 0x00, // 0 RRs (answers) | |
193 0x00, 0x00, // 0 authority RRs | |
194 0x00, 0x00, // 0 additional RRs | |
195 | |
196 // Question | |
197 // This part is echoed back from the respective query. | |
198 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't', | |
199 0x04, '_', 't', 'c', 'p', | |
200 0x05, 'l', 'o', 'c', 'a', 'l', | |
201 0x00, | |
202 0x00, 0x0c, // TYPE is PTR. | |
203 0x00, 0x01, // CLASS is IN. | |
204 }; | |
205 | |
206 const char kSamplePacketAdditionalOnly[] = { | |
207 // Header | |
208 0x00, 0x00, // ID is zeroed out | |
209 0x81, 0x80, // Standard query response, RA, no error | |
210 0x00, 0x00, // No questions (for simplicity) | |
211 0x00, 0x00, // 2 RRs (answers) | |
212 0x00, 0x00, // 0 authority RRs | |
213 0x00, 0x01, // 0 additional RRs | |
214 | |
215 // Answer 1 | |
216 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't', | |
217 0x04, '_', 't', 'c', 'p', | |
218 0x05, 'l', 'o', 'c', 'a', 'l', | |
219 0x00, | |
220 0x00, 0x0c, // TYPE is PTR. | |
221 0x00, 0x01, // CLASS is IN. | |
222 0x00, 0x01, // TTL (4 bytes) is 20 hours, 47 minutes, 48 seconds. | |
223 0x24, 0x74, | |
224 0x00, 0x08, // RDLENGTH is 8 bytes. | |
225 0x05, 'h', 'e', 'l', 'l', 'o', | |
226 0xc0, 0x0c, | |
227 }; | |
228 | |
229 class MockMDnsConnectionFactory; | |
230 | |
231 class MockMDnsConnection : public MDnsConnection { | |
szym
2013/06/10 21:58:29
I think this could be equivalently built from MDns
Noam Samuel
2013/06/11 18:31:14
Should I call the factory interface DatagramServer
Noam Samuel
2013/06/11 20:35:03
Done with name MDnsDatagramServerSocketFactory.
O
| |
232 public: | |
233 MockMDnsConnection(MDnsConnection::Delegate* delegate, | |
234 MockMDnsConnectionFactory* factory) | |
235 : delegate_(delegate), factory_(factory) { | |
236 } | |
237 | |
238 virtual ~MockMDnsConnection() { | |
239 } | |
240 | |
241 virtual int Init() OVERRIDE { | |
242 return OK; | |
243 } | |
244 | |
245 virtual int Send(IOBuffer* buffer, unsigned size) OVERRIDE; | |
246 | |
247 void SimulateRecieve(const char* packet, int size); | |
248 | |
249 private: | |
250 DnsResponse response; | |
251 MDnsConnection::Delegate* delegate_; | |
252 MockMDnsConnectionFactory* factory_; | |
253 }; | |
254 | |
255 class MockMDnsConnectionFactory : public MDnsConnectionFactory { | |
256 public: | |
257 MockMDnsConnectionFactory() {} | |
258 virtual ~MockMDnsConnectionFactory() {} | |
259 virtual scoped_ptr<MDnsConnection> CreateConnection( | |
260 MDnsConnection::Delegate* delegate) OVERRIDE; | |
261 | |
262 MOCK_METHOD1(OnSend, void(std::string packet)); | |
263 | |
264 MockMDnsConnection* latest_connection() { return latest_connection_; } | |
265 | |
266 private: | |
267 MockMDnsConnection* latest_connection_; | |
268 }; | |
269 | |
270 class MockMDnsConnectionDelegate : public MDnsConnection::Delegate { | |
271 public: | |
272 virtual void HandlePacket(DnsResponse* response, int size) { | |
273 HandlePacketInternal(std::string(response->io_buffer()->data(), size)); | |
274 } | |
275 | |
276 MOCK_METHOD1(HandlePacketInternal, void(std::string packet)); | |
277 | |
278 MOCK_METHOD1(OnConnectionError, void(int error)); | |
279 }; | |
280 | |
281 int MockMDnsConnection::Send(IOBuffer* buffer, unsigned size) { | |
282 factory_->OnSend(std::string(buffer->data(), size)); | |
283 return OK; | |
284 } | |
285 | |
286 void MockMDnsConnection::SimulateRecieve(const char* packet, int size) { | |
287 memcpy(response.io_buffer()->data(), packet, size); | |
288 delegate_->HandlePacket(&response, size); | |
289 } | |
290 | |
291 scoped_ptr<MDnsConnection> MockMDnsConnectionFactory::CreateConnection( | |
292 MDnsConnection::Delegate* delegate) { | |
293 latest_connection_ = new MockMDnsConnection(delegate, this); | |
294 | |
295 return scoped_ptr<MDnsConnection>(latest_connection_); | |
296 } | |
297 | |
298 class RecordParsedCopyContainer { | |
szym
2013/06/10 21:58:29
It seems you could avoid the clone by following th
Noam Samuel
2013/06/11 20:35:03
Done.
| |
299 public: | |
300 RecordParsedCopyContainer() {} | |
301 ~RecordParsedCopyContainer() {} | |
302 | |
303 bool is_set() const { return value_.get() != NULL; } | |
304 | |
305 void SaveWithDummyArg(int unused, const RecordParsed* value) { | |
306 Save(value); | |
307 } | |
308 | |
309 void Save(const RecordParsed* value) { | |
310 value_ = value->Clone(); | |
311 } | |
312 | |
313 const RecordParsed* operator->() const { | |
314 return value_.get(); | |
315 } | |
316 | |
317 private: | |
318 scoped_ptr<const RecordParsed> value_; | |
319 }; | |
320 | |
321 class MDnsTest : public ::testing::Test { | |
322 public: | |
323 MDnsTest(); | |
324 virtual ~MDnsTest(); | |
325 void DeleteTransaction(); | |
326 void DeleteBothListeners(); | |
327 void RunUntilIdle(); | |
328 void RunFor(base::TimeDelta time_period); | |
329 void Stop(); | |
330 | |
331 MOCK_METHOD2(MockableRecordCallback, void(MDnsTransactionResult result, | |
332 const RecordParsed* record)); | |
333 | |
334 protected: | |
335 void ExpectPacket(const char* packet, unsigned size); | |
336 void SendPacket(const char* packet, unsigned size); | |
szym
2013/06/10 21:58:29
This name is a bit misleading. You are simulating
Noam Samuel
2013/06/11 20:35:03
Done.
| |
337 void ExpectPtrRecord( | |
szym
2013/06/10 21:58:29
This name is misleading. Should be ExpectContainsP
Noam Samuel
2013/06/11 20:35:03
Done.
| |
338 const std::string& name, | |
339 const std::string& ptrdomain, | |
340 const RecordParsedCopyContainer& record); | |
341 | |
342 base::MessageLoop* message_loop_; | |
343 | |
344 scoped_ptr<MDnsClientImpl> test_client_; | |
345 IPEndPoint mdns_ipv4_endpoint_; | |
346 StrictMock<MockMDnsConnectionFactory> connection_factory_; | |
347 | |
348 // Transactions and listeners that can be deleted by class methods for | |
349 // reentrancy tests. | |
350 scoped_ptr<MDnsTransaction> transaction_; | |
351 scoped_ptr<MDnsListener> listener1_; | |
352 scoped_ptr<MDnsListener> listener2_; | |
353 }; | |
354 | |
355 class MockListenerDelegate : public MDnsListener::Delegate { | |
356 public: | |
357 MOCK_METHOD2(OnRecordUpdate, | |
358 void(MDnsUpdateType update, const RecordParsed* records)); | |
359 MOCK_METHOD2(OnNsecRecord, void(const std::string&, unsigned)); | |
360 MOCK_METHOD0(OnCachePurged, void()); | |
361 }; | |
362 | |
363 MDnsTest::MDnsTest() | |
364 : message_loop_(base::MessageLoop::current()) { | |
365 test_client_.reset(new MDnsClientImpl(&connection_factory_)); | |
366 } | |
367 | |
368 MDnsTest::~MDnsTest() { | |
369 } | |
370 | |
371 void MDnsTest::SendPacket(const char* packet, unsigned size) { | |
372 connection_factory_.latest_connection()->SimulateRecieve(packet, size); | |
373 } | |
374 | |
375 void MDnsTest::ExpectPtrRecord( | |
376 const std::string& name, | |
377 const std::string& ptrdomain, | |
378 const RecordParsedCopyContainer& record) { | |
379 EXPECT_TRUE(record.is_set()); | |
380 EXPECT_EQ(name, record->name()); | |
381 EXPECT_EQ(dns_protocol::kTypePTR, record->type()); | |
382 EXPECT_EQ(dns_protocol::kClassIN, record->klass()); | |
383 const PtrRecordRdata* rdata = record->rdata<PtrRecordRdata>(); | |
384 EXPECT_TRUE(rdata != NULL); | |
385 EXPECT_EQ(ptrdomain, rdata->ptrdomain()); | |
386 } | |
387 | |
388 void MDnsTest::ExpectPacket( | |
389 const char* packet, | |
390 unsigned size) { | |
391 EXPECT_CALL(connection_factory_, OnSend(std::string(packet, size))); | |
392 } | |
393 | |
394 void MDnsTest::DeleteTransaction() { | |
395 transaction_.reset(); | |
396 } | |
397 | |
398 void MDnsTest::DeleteBothListeners() { | |
399 listener1_.reset(); | |
400 listener2_.reset(); | |
401 } | |
402 | |
403 void MDnsTest::RunUntilIdle() { | |
404 base::MessageLoop::current()->RunUntilIdle(); | |
405 } | |
406 | |
407 void MDnsTest::RunFor(base::TimeDelta time_period) { | |
408 base::CancelableCallback<void()> callback(base::Bind(&MDnsTest::Stop, | |
409 base::Unretained(this))); | |
410 base::MessageLoop::current()->PostDelayedTask( | |
411 FROM_HERE, callback.callback(), time_period); | |
412 | |
413 base::MessageLoop::current()->Run(); | |
414 callback.Cancel(); | |
415 } | |
416 | |
417 void MDnsTest::Stop() { | |
418 base::MessageLoop::current()->Quit(); | |
419 } | |
420 | |
421 TEST_F(MDnsTest, PassiveListeners) { | |
422 StrictMock<MockListenerDelegate> delegate_privet; | |
423 StrictMock<MockListenerDelegate> delegate_printer; | |
424 StrictMock<MockListenerDelegate> delegate_ptr; | |
425 | |
426 RecordParsedCopyContainer record_privet; | |
427 RecordParsedCopyContainer record_printer; | |
428 | |
429 scoped_ptr<MDnsListener> listener_privet = test_client_->CreateListener( | |
430 dns_protocol::kTypePTR, "_privet._tcp.local", &delegate_privet); | |
431 scoped_ptr<MDnsListener> listener_printer = test_client_->CreateListener( | |
432 dns_protocol::kTypePTR, "_printer._tcp.local", &delegate_printer); | |
433 scoped_ptr<MDnsListener> listener_ptr = test_client_->CreateListener( | |
434 dns_protocol::kTypePTR, "", &delegate_ptr); | |
435 | |
436 listener_privet->Start(); | |
437 listener_printer->Start(); | |
438 listener_ptr->Start(); | |
439 | |
440 ASSERT_TRUE(test_client_->IsListeningForTests()); | |
441 | |
442 // Send the same packet twice to ensure no records are double-counted. | |
443 | |
444 EXPECT_CALL(delegate_privet, OnRecordUpdate(kMDnsRecordAdded, _)) | |
445 .Times(Exactly(1)) | |
446 .WillOnce(Invoke( | |
447 &record_privet, | |
448 &RecordParsedCopyContainer::SaveWithDummyArg)); | |
449 | |
450 EXPECT_CALL(delegate_printer, OnRecordUpdate(kMDnsRecordAdded, _)) | |
451 .Times(Exactly(1)) | |
452 .WillOnce(Invoke( | |
453 &record_printer, | |
454 &RecordParsedCopyContainer::SaveWithDummyArg)); | |
455 | |
456 EXPECT_CALL(delegate_ptr, OnRecordUpdate(kMDnsRecordAdded, _)) | |
457 .Times(Exactly(2)); | |
458 | |
459 SendPacket(kSamplePacket1, sizeof(kSamplePacket1)); | |
460 SendPacket(kSamplePacket1, sizeof(kSamplePacket1)); | |
461 | |
462 RunUntilIdle(); | |
463 | |
464 ExpectPtrRecord("_privet._tcp.local", "hello._privet._tcp.local", | |
465 record_privet); | |
466 | |
467 ExpectPtrRecord("_printer._tcp.local", "hello._printer._tcp.local", | |
468 record_printer); | |
469 | |
470 listener_privet.reset(); | |
471 listener_printer.reset(); | |
472 | |
473 ASSERT_TRUE(test_client_->IsListeningForTests()); | |
474 | |
475 EXPECT_CALL(delegate_ptr, OnRecordUpdate(kMDnsRecordAdded, _)) | |
476 .Times(Exactly(2)); | |
477 | |
478 SendPacket(kSamplePacket2, sizeof(kSamplePacket2)); | |
479 | |
480 RunUntilIdle(); | |
481 | |
482 // Test to make sure mdns listener is not active with no listeners present. | |
483 listener_ptr.reset(); | |
484 | |
485 RunUntilIdle(); | |
486 | |
487 ASSERT_FALSE(test_client_->IsListeningForTests()); | |
488 } | |
489 | |
490 TEST_F(MDnsTest, PassiveListenersCleanup) { | |
491 StrictMock<MockListenerDelegate> delegate_privet; | |
492 | |
493 RecordParsedCopyContainer record_privet; | |
494 RecordParsedCopyContainer record_privet2; | |
495 | |
496 scoped_ptr<MDnsListener> listener_privet = test_client_->CreateListener( | |
497 dns_protocol::kTypePTR, "_privet._tcp.local", &delegate_privet); | |
498 | |
499 listener_privet->Start(); | |
500 | |
501 ASSERT_TRUE(test_client_->IsListeningForTests()); | |
502 | |
503 EXPECT_CALL(delegate_privet, OnRecordUpdate(kMDnsRecordAdded, _)) | |
504 .Times(Exactly(1)) | |
505 .WillOnce(Invoke( | |
506 &record_privet, | |
507 &RecordParsedCopyContainer::SaveWithDummyArg)); | |
508 | |
509 SendPacket(kSamplePacket1, sizeof(kSamplePacket1)); | |
510 | |
511 RunUntilIdle(); | |
512 | |
513 ExpectPtrRecord("_privet._tcp.local", "hello._privet._tcp.local", | |
514 record_privet); | |
515 | |
516 EXPECT_CALL(delegate_privet, OnRecordUpdate(kMDnsRecordRemoved, _)) | |
517 .Times(Exactly(1)) | |
518 .WillOnce(DoAll(InvokeWithoutArgs(this, &MDnsTest::Stop), | |
519 Invoke(&record_privet2, | |
520 &RecordParsedCopyContainer::SaveWithDummyArg))); | |
521 | |
522 RunFor(base::TimeDelta::FromSeconds(record_privet->ttl() + 1)); | |
523 | |
524 RunUntilIdle(); | |
525 | |
526 ExpectPtrRecord("_privet._tcp.local", "hello._privet._tcp.local", | |
527 record_privet2); | |
528 } | |
529 | |
530 TEST_F(MDnsTest, MalformedPacket) { | |
531 StrictMock<MockListenerDelegate> delegate_printer; | |
532 | |
533 RecordParsedCopyContainer record_printer; | |
534 | |
535 scoped_ptr<MDnsListener> listener_printer = test_client_->CreateListener( | |
536 dns_protocol::kTypePTR, "_printer._tcp.local", &delegate_printer); | |
537 | |
538 listener_printer->Start(); | |
539 | |
540 ASSERT_TRUE(test_client_->IsListeningForTests()); | |
541 | |
542 EXPECT_CALL(delegate_printer, OnRecordUpdate(kMDnsRecordAdded, _)) | |
543 .Times(Exactly(1)) | |
544 .WillOnce(Invoke( | |
545 &record_printer, | |
546 &RecordParsedCopyContainer::SaveWithDummyArg)); | |
547 | |
548 // First, send unsalvagable packet to ensure we can deal with it. | |
549 SendPacket(kCorruptedPacketUnsalvagable, | |
550 sizeof(kCorruptedPacketUnsalvagable)); | |
551 | |
552 // Regression test: send a packet where the question cannot be read. | |
553 SendPacket(kCorruptedPacketBadQuestion, | |
554 sizeof(kCorruptedPacketBadQuestion)); | |
555 | |
556 // Then send salvagable packet to ensure we can extract useful records. | |
557 SendPacket(kCorruptedPacketSalvagable, | |
558 sizeof(kCorruptedPacketSalvagable)); | |
559 | |
560 RunUntilIdle(); | |
561 | |
562 ExpectPtrRecord("_printer._tcp.local", "hello._printer._tcp.local", | |
563 record_printer); | |
564 } | |
565 | |
566 TEST_F(MDnsTest, TransactionNoCache) { | |
567 ExpectPacket(kQueryPacketPrivet, sizeof(kQueryPacketPrivet)); | |
568 | |
569 scoped_ptr<MDnsTransaction> transaction_privet = | |
570 test_client_->CreateTransaction( | |
571 dns_protocol::kTypePTR, "_privet._tcp.local", | |
572 kMDnsTransactionQueryNetwork | | |
573 kMDnsTransactionQueryCache | | |
574 kMDnsTransactionSingleResult, | |
575 base::Bind(&MDnsTest::MockableRecordCallback, | |
576 base::Unretained(this))); | |
577 | |
578 transaction_privet->Start(); | |
579 | |
580 EXPECT_TRUE(test_client_->IsListeningForTests()); | |
581 | |
582 RecordParsedCopyContainer record_privet; | |
583 | |
584 EXPECT_CALL(*this, MockableRecordCallback(kMDnsTransactionRecord, _)) | |
585 .Times(Exactly(1)) | |
586 .WillOnce(Invoke(&record_privet, | |
587 &RecordParsedCopyContainer::SaveWithDummyArg)); | |
588 | |
589 SendPacket(kSamplePacket1, sizeof(kSamplePacket1)); | |
590 | |
591 RunUntilIdle(); | |
592 | |
593 ExpectPtrRecord("_privet._tcp.local", "hello._privet._tcp.local", | |
594 record_privet); | |
595 | |
596 EXPECT_FALSE(test_client_->IsListeningForTests()); | |
597 } | |
598 | |
599 | |
600 TEST_F(MDnsTest, TransactionCacheOnlyNoResult) { | |
601 scoped_ptr<MDnsTransaction> transaction_privet = | |
602 test_client_->CreateTransaction( | |
603 dns_protocol::kTypePTR, "_privet._tcp.local", | |
604 kMDnsTransactionQueryCache | | |
605 kMDnsTransactionSingleResult, | |
606 base::Bind(&MDnsTest::MockableRecordCallback, | |
607 base::Unretained(this))); | |
608 | |
609 EXPECT_CALL(*this, MockableRecordCallback(kMDnsTransactionNoResults, _)) | |
610 .Times(Exactly(1)); | |
611 | |
612 transaction_privet->Start(); | |
613 | |
614 EXPECT_FALSE(test_client_->IsListeningForTests()); | |
615 | |
616 RunUntilIdle(); | |
617 } | |
618 | |
619 | |
620 TEST_F(MDnsTest, TransactionWithCache) { | |
621 // Listener to force the client to listen | |
622 StrictMock<MockListenerDelegate> delegate_irrelevant; | |
623 scoped_ptr<MDnsListener> listener_irrelevant = test_client_->CreateListener( | |
624 dns_protocol::kTypeA, "codereview.chromium.local", | |
625 &delegate_irrelevant); | |
626 | |
627 listener_irrelevant->Start(); | |
628 | |
629 EXPECT_TRUE(test_client_->IsListeningForTests()); | |
630 | |
631 SendPacket(kSamplePacket1, sizeof(kSamplePacket1)); | |
632 | |
633 RunUntilIdle(); | |
634 | |
635 RecordParsedCopyContainer record_privet; | |
636 | |
637 EXPECT_CALL(*this, MockableRecordCallback(kMDnsTransactionRecord, _)) | |
638 .WillOnce(Invoke(&record_privet, | |
639 &RecordParsedCopyContainer::SaveWithDummyArg)); | |
640 | |
641 scoped_ptr<MDnsTransaction> transaction_privet = | |
642 test_client_->CreateTransaction( | |
643 dns_protocol::kTypePTR, "_privet._tcp.local", | |
644 kMDnsTransactionQueryNetwork | | |
645 kMDnsTransactionQueryCache | | |
646 kMDnsTransactionSingleResult, | |
647 base::Bind(&MDnsTest::MockableRecordCallback, | |
648 base::Unretained(this))); | |
649 | |
650 transaction_privet->Start(); | |
651 | |
652 RunUntilIdle(); | |
653 | |
654 ExpectPtrRecord("_privet._tcp.local", "hello._privet._tcp.local", | |
655 record_privet); | |
656 } | |
657 | |
658 TEST_F(MDnsTest, AdditionalRecords) { | |
659 StrictMock<MockListenerDelegate> delegate_privet; | |
660 | |
661 RecordParsedCopyContainer record_privet; | |
662 | |
663 scoped_ptr<MDnsListener> listener_privet = test_client_->CreateListener( | |
664 dns_protocol::kTypePTR, "_privet._tcp.local", | |
665 &delegate_privet); | |
666 | |
667 listener_privet->Start(); | |
668 | |
669 ASSERT_TRUE(test_client_->IsListeningForTests()); | |
670 | |
671 EXPECT_CALL(delegate_privet, OnRecordUpdate(kMDnsRecordAdded, _)) | |
672 .Times(Exactly(1)) | |
673 .WillOnce(Invoke( | |
674 &record_privet, | |
675 &RecordParsedCopyContainer::SaveWithDummyArg)); | |
676 | |
677 SendPacket(kSamplePacketAdditionalOnly, sizeof(kSamplePacket1)); | |
678 | |
679 RunUntilIdle(); | |
680 | |
681 ExpectPtrRecord("_privet._tcp.local", "hello._privet._tcp.local", | |
682 record_privet); | |
683 } | |
684 | |
685 TEST_F(MDnsTest, TransactionTimeout) { | |
686 ExpectPacket(kQueryPacketPrivet, sizeof(kQueryPacketPrivet)); | |
687 | |
688 scoped_ptr<MDnsTransaction> transaction_privet = | |
689 test_client_->CreateTransaction( | |
690 dns_protocol::kTypePTR, "_privet._tcp.local", | |
691 kMDnsTransactionQueryNetwork | | |
692 kMDnsTransactionQueryCache | | |
693 kMDnsTransactionSingleResult, | |
694 base::Bind(&MDnsTest::MockableRecordCallback, | |
695 base::Unretained(this))); | |
696 | |
697 transaction_privet->Start(); | |
698 | |
699 EXPECT_TRUE(test_client_->IsListeningForTests()); | |
700 | |
701 EXPECT_CALL(*this, MockableRecordCallback(kMDnsTransactionNoResults, NULL)) | |
702 .Times(Exactly(1)) | |
703 .WillOnce(InvokeWithoutArgs(this, &MDnsTest::Stop)); | |
704 | |
705 RunFor(base::TimeDelta::FromSeconds(4)); | |
706 | |
707 EXPECT_FALSE(test_client_->IsListeningForTests()); | |
708 } | |
709 | |
710 TEST_F(MDnsTest, TransactionMultipleRecords) { | |
711 ExpectPacket(kQueryPacketPrivet, sizeof(kQueryPacketPrivet)); | |
712 | |
713 scoped_ptr<MDnsTransaction> transaction_privet = | |
714 test_client_->CreateTransaction( | |
715 dns_protocol::kTypePTR, "_privet._tcp.local", | |
716 kMDnsTransactionQueryNetwork | | |
717 kMDnsTransactionQueryCache , | |
718 base::Bind(&MDnsTest::MockableRecordCallback, | |
719 base::Unretained(this))); | |
720 | |
721 transaction_privet->Start(); | |
722 | |
723 EXPECT_TRUE(test_client_->IsListeningForTests()); | |
724 | |
725 RecordParsedCopyContainer record_privet; | |
726 RecordParsedCopyContainer record_privet2; | |
727 | |
728 EXPECT_CALL(*this, MockableRecordCallback(kMDnsTransactionRecord, _)) | |
729 .Times(Exactly(2)) | |
730 .WillOnce(Invoke(&record_privet, | |
731 &RecordParsedCopyContainer::SaveWithDummyArg)) | |
732 .WillOnce(Invoke(&record_privet2, | |
733 &RecordParsedCopyContainer::SaveWithDummyArg)); | |
734 | |
735 SendPacket(kSamplePacket1, sizeof(kSamplePacket1)); | |
736 SendPacket(kSamplePacket2, sizeof(kSamplePacket2)); | |
737 | |
738 RunUntilIdle(); | |
739 | |
740 ExpectPtrRecord("_privet._tcp.local", "hello._privet._tcp.local", | |
741 record_privet); | |
742 | |
743 ExpectPtrRecord("_privet._tcp.local", "zzzzz._privet._tcp.local", | |
744 record_privet2); | |
745 | |
746 EXPECT_CALL(*this, MockableRecordCallback(kMDnsTransactionDone, NULL)) | |
747 .WillOnce(InvokeWithoutArgs(this, &MDnsTest::Stop)); | |
748 | |
749 RunFor(base::TimeDelta::FromSeconds(4)); | |
750 | |
751 EXPECT_FALSE(test_client_->IsListeningForTests()); | |
752 } | |
753 | |
754 | |
755 TEST_F(MDnsTest, TransactionReentrantDelete) { | |
756 ExpectPacket(kQueryPacketPrivet, sizeof(kQueryPacketPrivet)); | |
757 | |
758 transaction_ = test_client_->CreateTransaction( | |
759 dns_protocol::kTypePTR, "_privet._tcp.local", | |
760 kMDnsTransactionQueryNetwork | | |
761 kMDnsTransactionQueryCache | | |
762 kMDnsTransactionSingleResult, | |
763 base::Bind(&MDnsTest::MockableRecordCallback, | |
764 base::Unretained(this))); | |
765 | |
766 transaction_->Start(); | |
767 | |
768 EXPECT_TRUE(test_client_->IsListeningForTests()); | |
769 | |
770 EXPECT_CALL(*this, MockableRecordCallback(kMDnsTransactionNoResults, NULL)) | |
771 .Times(Exactly(1)) | |
772 .WillOnce(DoAll(InvokeWithoutArgs(this, &MDnsTest::DeleteTransaction), | |
773 InvokeWithoutArgs(this, &MDnsTest::Stop))); | |
774 | |
775 | |
776 RunFor(base::TimeDelta::FromSeconds(4)); | |
777 | |
778 EXPECT_EQ(NULL, transaction_.get()); | |
779 | |
780 EXPECT_FALSE(test_client_->IsListeningForTests()); | |
781 } | |
782 | |
783 | |
784 TEST_F(MDnsTest, TransactionReentrantDeleteFromCache) { | |
785 StrictMock<MockListenerDelegate> delegate_irrelevant; | |
786 scoped_ptr<MDnsListener> listener_irrelevant = test_client_->CreateListener( | |
787 dns_protocol::kTypeA, "codereview.chromium.local", | |
788 &delegate_irrelevant); | |
789 listener_irrelevant->Start(); | |
790 | |
791 ASSERT_TRUE(test_client_->IsListeningForTests()); | |
792 | |
793 SendPacket(kSamplePacket1, sizeof(kSamplePacket1)); | |
794 | |
795 transaction_ = test_client_->CreateTransaction( | |
796 dns_protocol::kTypePTR, "_privet._tcp.local", | |
797 kMDnsTransactionQueryNetwork | | |
798 kMDnsTransactionQueryCache, | |
799 base::Bind(&MDnsTest::MockableRecordCallback, | |
800 base::Unretained(this))); | |
801 | |
802 | |
803 EXPECT_CALL(*this, MockableRecordCallback(kMDnsTransactionRecord, _)) | |
804 .Times(Exactly(1)) | |
805 .WillOnce(InvokeWithoutArgs(this, &MDnsTest::DeleteTransaction)); | |
806 | |
807 transaction_->Start(); | |
808 | |
809 | |
810 RunUntilIdle(); | |
811 | |
812 EXPECT_EQ(NULL, transaction_.get()); | |
813 } | |
814 | |
815 | |
816 // In order to reliably test reentrant listener deletes, we create two listeners | |
817 // and have each of them delete both, so we're guaranteed to try and deliver a | |
818 // callback to at least one deleted listener. | |
819 | |
820 TEST_F(MDnsTest, ListenerReentrantDelete) { | |
821 StrictMock<MockListenerDelegate> delegate_privet; | |
822 | |
823 listener1_ = test_client_->CreateListener( | |
824 dns_protocol::kTypePTR, "_privet._tcp.local", | |
825 &delegate_privet); | |
826 | |
827 listener2_ = test_client_->CreateListener( | |
828 dns_protocol::kTypePTR, "_privet._tcp.local", | |
829 &delegate_privet); | |
830 | |
831 listener1_->Start(); | |
832 | |
833 listener2_->Start(); | |
834 | |
835 EXPECT_CALL(delegate_privet, OnRecordUpdate(kMDnsRecordAdded, _)) | |
836 .Times(Exactly(1)) | |
837 .WillOnce(InvokeWithoutArgs(this, &MDnsTest::DeleteBothListeners)); | |
838 | |
839 EXPECT_TRUE(test_client_->IsListeningForTests()); | |
840 | |
841 SendPacket(kSamplePacket1, sizeof(kSamplePacket1)); | |
842 | |
843 RunUntilIdle(); | |
844 | |
845 EXPECT_EQ(NULL, listener1_.get()); | |
846 EXPECT_EQ(NULL, listener2_.get()); | |
847 | |
848 EXPECT_FALSE(test_client_->IsListeningForTests()); | |
849 } | |
850 | |
851 class MockDatagramServerSocket : public DatagramServerSocket { | |
852 public: | |
853 int Listen(const IPEndPoint& address) { | |
854 return ListenInternal(address.ToString()); | |
855 } | |
856 | |
857 MOCK_METHOD1(ListenInternal, int(const std::string& address)); | |
858 | |
859 void SetResponsePacket(std::string response_packet) { | |
860 response_packet_ = response_packet; | |
861 } | |
862 | |
863 int RespondImmediately(IOBuffer* buffer, int size, IPEndPoint* address, | |
864 const CompletionCallback& callback) { | |
865 int to_copy = std::min(response_packet_.size(), (unsigned long)size); | |
866 memcpy(buffer->data(), response_packet_.data(), to_copy); | |
867 return to_copy; | |
868 } | |
869 | |
870 int RespondDelayed(IOBuffer* buffer, int size, IPEndPoint* address, | |
871 const CompletionCallback& callback) { | |
872 int rv = RespondImmediately(buffer, size, address, callback); | |
873 MessageLoop::current()->PostTask(FROM_HERE, base::Bind(callback, rv)); | |
874 return ERR_IO_PENDING; | |
875 } | |
876 | |
877 MOCK_METHOD4(RecvFrom, int(IOBuffer* buffer, int size, | |
878 IPEndPoint* address, | |
879 const CompletionCallback& callback)); | |
880 | |
881 int SendTo(IOBuffer* buf, int buf_len, const IPEndPoint& address, | |
882 const CompletionCallback& callback) { | |
883 return SendToInternal(std::string(buf->data(), buf_len), address.ToString(), | |
884 callback); | |
885 } | |
886 | |
887 MOCK_METHOD3(SendToInternal, int(const std::string&, const std::string, | |
888 const CompletionCallback&)); | |
889 | |
890 MOCK_METHOD1(SetReceiveBufferSize, bool(int32 size)); | |
891 MOCK_METHOD1(SetSendBufferSize, bool(int32 size)); | |
892 | |
893 MOCK_METHOD0(Close, void()); | |
894 | |
895 MOCK_CONST_METHOD1(GetPeerAddress, int(IPEndPoint* address)); | |
896 MOCK_CONST_METHOD1(GetLocalAddress, int(IPEndPoint* address)); | |
897 MOCK_CONST_METHOD0(NetLog, const BoundNetLog&()); | |
898 | |
899 MOCK_METHOD0(AllowAddressReuse, void()); | |
900 MOCK_METHOD0(AllowBroadcast, void()); | |
901 | |
902 int JoinGroup(const IPAddressNumber& group_address) const { | |
903 return JoinGroupInternal(IPAddressToString(group_address)); | |
904 } | |
905 | |
906 MOCK_CONST_METHOD1(JoinGroupInternal, int(const std::string& group)); | |
907 | |
908 int LeaveGroup(const IPAddressNumber& group_address) const { | |
909 return JoinGroupInternal(IPAddressToString(group_address)); | |
szym
2013/06/10 21:58:29
Shouldn't this be LeaveGroupInternal?
Noam Samuel
2013/06/11 20:35:03
Done.
| |
910 } | |
911 | |
912 MOCK_CONST_METHOD1(LeaveGroupInternal, int(const std::string& group)); | |
913 | |
914 MOCK_METHOD1(SetMulticastTimeToLive, int(int ttl)); | |
915 | |
916 MOCK_METHOD1(SetMulticastLoopbackMode, int(bool loopback)); | |
917 | |
918 private: | |
919 std::string response_packet_; | |
920 }; | |
921 | |
922 class MDnsConnectionTest : public ::testing::Test { | |
923 public: | |
924 MDnsConnectionTest() : connection_(&socket_ipv4_, &socket_ipv6_, &delegate_) { | |
925 } | |
926 | |
927 protected: | |
928 // Follow successful connection initialization | |
929 void InitConnection() { | |
930 EXPECT_CALL(socket_ipv4_, AllowAddressReuse()); | |
931 EXPECT_CALL(socket_ipv6_, AllowAddressReuse()); | |
932 | |
933 EXPECT_CALL(socket_ipv4_, SetMulticastLoopbackMode(false)); | |
934 EXPECT_CALL(socket_ipv6_, SetMulticastLoopbackMode(false)); | |
935 | |
936 EXPECT_CALL(socket_ipv4_, ListenInternal("0.0.0.0:5353")) | |
937 .WillOnce(Return(OK)); | |
938 EXPECT_CALL(socket_ipv6_, ListenInternal("[::]:5353")) | |
939 .WillOnce(Return(OK)); | |
940 | |
941 EXPECT_CALL(socket_ipv4_, JoinGroupInternal("224.0.0.251")) | |
942 .WillOnce(Return(OK)); | |
943 EXPECT_CALL(socket_ipv6_, JoinGroupInternal("ff02::fb")) | |
944 .WillOnce(Return(OK)); | |
945 | |
946 connection_.Init(); | |
947 } | |
948 | |
949 StrictMock<MockMDnsConnectionDelegate> delegate_; | |
950 | |
951 MockDatagramServerSocket socket_ipv4_; | |
952 MockDatagramServerSocket socket_ipv6_; | |
953 MDnsConnectionImpl connection_; | |
954 TestCompletionCallback callback_; | |
955 }; | |
956 | |
957 TEST_F(MDnsConnectionTest, RecieveSynchronous) { | |
szym
2013/06/10 21:58:29
nit: Receive
Noam Samuel
2013/06/11 20:35:03
Done.
| |
958 std::string sample_packet = | |
959 std::string(kSamplePacket1, sizeof(kSamplePacket1)); | |
960 socket_ipv6_.SetResponsePacket(sample_packet); | |
961 EXPECT_CALL(socket_ipv4_, RecvFrom(_, _, _, _)) | |
962 .WillOnce(Return(ERR_IO_PENDING)); | |
963 EXPECT_CALL(socket_ipv6_, RecvFrom(_, _, _, _)) | |
964 .WillOnce( | |
965 Invoke(&socket_ipv6_, &MockDatagramServerSocket::RespondImmediately)) | |
966 .WillOnce(Return(ERR_IO_PENDING)); | |
967 | |
968 EXPECT_CALL(delegate_, HandlePacketInternal(sample_packet)); | |
969 | |
970 InitConnection(); | |
971 } | |
972 | |
973 TEST_F(MDnsConnectionTest, RecieveAsynchronous) { | |
974 std::string sample_packet = | |
975 std::string(kSamplePacket1, sizeof(kSamplePacket1)); | |
976 socket_ipv6_.SetResponsePacket(sample_packet); | |
977 EXPECT_CALL(socket_ipv4_, RecvFrom(_, _, _, _)) | |
978 .WillOnce(Return(ERR_IO_PENDING)); | |
979 EXPECT_CALL(socket_ipv6_, RecvFrom(_, _, _, _)) | |
980 .WillOnce( | |
981 Invoke(&socket_ipv6_, &MockDatagramServerSocket::RespondDelayed)) | |
982 .WillOnce(Return(ERR_IO_PENDING)); | |
983 | |
984 InitConnection(); | |
985 | |
986 EXPECT_CALL(delegate_, HandlePacketInternal(sample_packet)); | |
987 | |
988 base::MessageLoop::current()->RunUntilIdle(); | |
989 } | |
990 | |
991 TEST_F(MDnsConnectionTest, Send) { | |
992 std::string sample_packet = | |
993 std::string(kSamplePacket1, sizeof(kSamplePacket1)); | |
994 | |
995 scoped_refptr<IOBufferWithSize> buf( | |
996 new IOBufferWithSize(sizeof kSamplePacket1)); | |
997 memcpy(buf->data(), kSamplePacket1, sizeof(kSamplePacket1)); | |
998 | |
999 EXPECT_CALL(socket_ipv4_, RecvFrom(_, _, _, _)) | |
1000 .WillOnce(Return(ERR_IO_PENDING)); | |
1001 EXPECT_CALL(socket_ipv6_, RecvFrom(_, _, _, _)) | |
1002 .WillOnce(Return(ERR_IO_PENDING)); | |
1003 | |
1004 InitConnection(); | |
1005 | |
1006 EXPECT_CALL(socket_ipv4_, | |
1007 SendToInternal(sample_packet, "224.0.0.251:5353", _)); | |
1008 EXPECT_CALL(socket_ipv6_, | |
1009 SendToInternal(sample_packet, "[ff02::fb]:5353", _)); | |
1010 | |
1011 connection_.Send(buf, buf->size()); | |
1012 } | |
1013 | |
1014 TEST_F(MDnsConnectionTest, Error) { | |
1015 CompletionCallback callback; | |
1016 | |
1017 EXPECT_CALL(socket_ipv4_, RecvFrom(_, _, _, _)) | |
1018 .WillOnce(Return(ERR_IO_PENDING)); | |
1019 EXPECT_CALL(socket_ipv6_, RecvFrom(_, _, _, _)) | |
1020 .WillOnce(DoAll(SaveArg<3>(&callback), Return(ERR_IO_PENDING))); | |
1021 | |
1022 InitConnection(); | |
1023 | |
1024 EXPECT_CALL(delegate_, OnConnectionError(ERR_SOCKET_NOT_CONNECTED)); | |
1025 callback.Run(ERR_SOCKET_NOT_CONNECTED); | |
1026 } | |
1027 | |
1028 } // namespace | |
1029 | |
1030 } // namespace net | |
OLD | NEW |