1 // Copyright 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.
7 #include "base/memory/ref_counted.h"
8 #include "base/message_loop/message_loop.h"
9 #include "net/base/rand_callback.h"
10 #include "net/base/test_completion_callback.h"
11 #include "net/dns/mdns_client_impl.h"
12 #include "net/dns/mock_mdns_socket_factory.h"
13 #include "net/dns/record_rdata.h"
14 #include "net/udp/udp_client_socket.h"
15 #include "testing/gmock/include/gmock/gmock.h"
16 #include "testing/gtest/include/gtest/gtest.h"
18 using ::testing::Invoke
;
19 using ::testing::InvokeWithoutArgs
;
20 using ::testing::StrictMock
;
21 using ::testing::NiceMock
;
22 using ::testing::Exactly
;
23 using ::testing::Return
;
24 using ::testing::SaveArg
;
31 const uint8 kSamplePacket1
[] = {
33 0x00, 0x00, // ID is zeroed out
34 0x81, 0x80, // Standard query response, RA, no error
35 0x00, 0x00, // No questions (for simplicity)
36 0x00, 0x02, // 2 RRs (answers)
37 0x00, 0x00, // 0 authority RRs
38 0x00, 0x00, // 0 additional RRs
41 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
42 0x04, '_', 't', 'c', 'p',
43 0x05, 'l', 'o', 'c', 'a', 'l',
45 0x00, 0x0c, // TYPE is PTR.
46 0x00, 0x01, // CLASS is IN.
47 0x00, 0x00, // TTL (4 bytes) is 1 second;
49 0x00, 0x08, // RDLENGTH is 8 bytes.
50 0x05, 'h', 'e', 'l', 'l', 'o',
54 0x08, '_', 'p', 'r', 'i', 'n', 't', 'e', 'r',
55 0xc0, 0x14, // Pointer to "._tcp.local"
56 0x00, 0x0c, // TYPE is PTR.
57 0x00, 0x01, // CLASS is IN.
58 0x00, 0x01, // TTL (4 bytes) is 20 hours, 47 minutes, 49 seconds.
60 0x00, 0x08, // RDLENGTH is 8 bytes.
61 0x05, 'h', 'e', 'l', 'l', 'o',
65 const uint8 kCorruptedPacketBadQuestion
[] = {
67 0x00, 0x00, // ID is zeroed out
68 0x81, 0x80, // Standard query response, RA, no error
69 0x00, 0x01, // One question
70 0x00, 0x02, // 2 RRs (answers)
71 0x00, 0x00, // 0 authority RRs
72 0x00, 0x00, // 0 additional RRs
74 // Question is corrupted and cannot be read.
75 0x99, 'h', 'e', 'l', 'l', 'o',
81 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
82 0x04, '_', 't', 'c', 'p',
83 0x05, 'l', 'o', 'c', 'a', 'l',
85 0x00, 0x0c, // TYPE is PTR.
86 0x00, 0x01, // CLASS is IN.
87 0x00, 0x01, // TTL (4 bytes) is 20 hours, 47 minutes, 48 seconds.
89 0x00, 0x99, // RDLENGTH is impossible
90 0x05, 'h', 'e', 'l', 'l', 'o',
94 0x08, '_', 'p', 'r', // Useless trailing data.
97 const uint8 kCorruptedPacketUnsalvagable
[] = {
99 0x00, 0x00, // ID is zeroed out
100 0x81, 0x80, // Standard query response, RA, no error
101 0x00, 0x00, // No questions (for simplicity)
102 0x00, 0x02, // 2 RRs (answers)
103 0x00, 0x00, // 0 authority RRs
104 0x00, 0x00, // 0 additional RRs
107 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
108 0x04, '_', 't', 'c', 'p',
109 0x05, 'l', 'o', 'c', 'a', 'l',
111 0x00, 0x0c, // TYPE is PTR.
112 0x00, 0x01, // CLASS is IN.
113 0x00, 0x01, // TTL (4 bytes) is 20 hours, 47 minutes, 48 seconds.
115 0x00, 0x99, // RDLENGTH is impossible
116 0x05, 'h', 'e', 'l', 'l', 'o',
120 0x08, '_', 'p', 'r', // Useless trailing data.
123 const uint8 kCorruptedPacketDoubleRecord
[] = {
125 0x00, 0x00, // ID is zeroed out
126 0x81, 0x80, // Standard query response, RA, no error
127 0x00, 0x00, // No questions (for simplicity)
128 0x00, 0x02, // 2 RRs (answers)
129 0x00, 0x00, // 0 authority RRs
130 0x00, 0x00, // 0 additional RRs
133 0x06, 'p', 'r', 'i', 'v', 'e', 't',
134 0x05, 'l', 'o', 'c', 'a', 'l',
136 0x00, 0x01, // TYPE is A.
137 0x00, 0x01, // CLASS is IN.
138 0x00, 0x01, // TTL (4 bytes) is 20 hours, 47 minutes, 48 seconds.
140 0x00, 0x04, // RDLENGTH is 4
144 // Answer 2 -- Same key
145 0x06, 'p', 'r', 'i', 'v', 'e', 't',
146 0x05, 'l', 'o', 'c', 'a', 'l',
148 0x00, 0x01, // TYPE is A.
149 0x00, 0x01, // CLASS is IN.
150 0x00, 0x01, // TTL (4 bytes) is 20 hours, 47 minutes, 48 seconds.
152 0x00, 0x04, // RDLENGTH is 4
157 const uint8 kCorruptedPacketSalvagable
[] = {
159 0x00, 0x00, // ID is zeroed out
160 0x81, 0x80, // Standard query response, RA, no error
161 0x00, 0x00, // No questions (for simplicity)
162 0x00, 0x02, // 2 RRs (answers)
163 0x00, 0x00, // 0 authority RRs
164 0x00, 0x00, // 0 additional RRs
167 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
168 0x04, '_', 't', 'c', 'p',
169 0x05, 'l', 'o', 'c', 'a', 'l',
171 0x00, 0x0c, // TYPE is PTR.
172 0x00, 0x01, // CLASS is IN.
173 0x00, 0x01, // TTL (4 bytes) is 20 hours, 47 minutes, 48 seconds.
175 0x00, 0x08, // RDLENGTH is 8 bytes.
176 0x99, 'h', 'e', 'l', 'l', 'o', // Bad RDATA format.
180 0x08, '_', 'p', 'r', 'i', 'n', 't', 'e', 'r',
181 0xc0, 0x14, // Pointer to "._tcp.local"
182 0x00, 0x0c, // TYPE is PTR.
183 0x00, 0x01, // CLASS is IN.
184 0x00, 0x01, // TTL (4 bytes) is 20 hours, 47 minutes, 49 seconds.
186 0x00, 0x08, // RDLENGTH is 8 bytes.
187 0x05, 'h', 'e', 'l', 'l', 'o',
191 const uint8 kSamplePacket2
[] = {
193 0x00, 0x00, // ID is zeroed out
194 0x81, 0x80, // Standard query response, RA, no error
195 0x00, 0x00, // No questions (for simplicity)
196 0x00, 0x02, // 2 RRs (answers)
197 0x00, 0x00, // 0 authority RRs
198 0x00, 0x00, // 0 additional RRs
201 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
202 0x04, '_', 't', 'c', 'p',
203 0x05, 'l', 'o', 'c', 'a', 'l',
205 0x00, 0x0c, // TYPE is PTR.
206 0x00, 0x01, // CLASS is IN.
207 0x00, 0x01, // TTL (4 bytes) is 20 hours, 47 minutes, 48 seconds.
209 0x00, 0x08, // RDLENGTH is 8 bytes.
210 0x05, 'z', 'z', 'z', 'z', 'z',
214 0x08, '_', 'p', 'r', 'i', 'n', 't', 'e', 'r',
215 0xc0, 0x14, // Pointer to "._tcp.local"
216 0x00, 0x0c, // TYPE is PTR.
217 0x00, 0x01, // CLASS is IN.
218 0x00, 0x01, // TTL (4 bytes) is 20 hours, 47 minutes, 48 seconds.
220 0x00, 0x08, // RDLENGTH is 8 bytes.
221 0x05, 'z', 'z', 'z', 'z', 'z',
225 const uint8 kQueryPacketPrivet
[] = {
227 0x00, 0x00, // ID is zeroed out
228 0x00, 0x00, // No flags.
229 0x00, 0x01, // One question.
230 0x00, 0x00, // 0 RRs (answers)
231 0x00, 0x00, // 0 authority RRs
232 0x00, 0x00, // 0 additional RRs
235 // This part is echoed back from the respective query.
236 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
237 0x04, '_', 't', 'c', 'p',
238 0x05, 'l', 'o', 'c', 'a', 'l',
240 0x00, 0x0c, // TYPE is PTR.
241 0x00, 0x01, // CLASS is IN.
244 const uint8 kQueryPacketPrivetA
[] = {
246 0x00, 0x00, // ID is zeroed out
247 0x00, 0x00, // No flags.
248 0x00, 0x01, // One question.
249 0x00, 0x00, // 0 RRs (answers)
250 0x00, 0x00, // 0 authority RRs
251 0x00, 0x00, // 0 additional RRs
254 // This part is echoed back from the respective query.
255 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
256 0x04, '_', 't', 'c', 'p',
257 0x05, 'l', 'o', 'c', 'a', 'l',
259 0x00, 0x01, // TYPE is A.
260 0x00, 0x01, // CLASS is IN.
263 const uint8 kSamplePacketAdditionalOnly
[] = {
265 0x00, 0x00, // ID is zeroed out
266 0x81, 0x80, // Standard query response, RA, no error
267 0x00, 0x00, // No questions (for simplicity)
268 0x00, 0x00, // 2 RRs (answers)
269 0x00, 0x00, // 0 authority RRs
270 0x00, 0x01, // 0 additional RRs
273 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
274 0x04, '_', 't', 'c', 'p',
275 0x05, 'l', 'o', 'c', 'a', 'l',
277 0x00, 0x0c, // TYPE is PTR.
278 0x00, 0x01, // CLASS is IN.
279 0x00, 0x01, // TTL (4 bytes) is 20 hours, 47 minutes, 48 seconds.
281 0x00, 0x08, // RDLENGTH is 8 bytes.
282 0x05, 'h', 'e', 'l', 'l', 'o',
286 const uint8 kSamplePacketNsec
[] = {
288 0x00, 0x00, // ID is zeroed out
289 0x81, 0x80, // Standard query response, RA, no error
290 0x00, 0x00, // No questions (for simplicity)
291 0x00, 0x01, // 1 RR (answers)
292 0x00, 0x00, // 0 authority RRs
293 0x00, 0x00, // 0 additional RRs
296 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
297 0x04, '_', 't', 'c', 'p',
298 0x05, 'l', 'o', 'c', 'a', 'l',
300 0x00, 0x2f, // TYPE is NSEC.
301 0x00, 0x01, // CLASS is IN.
302 0x00, 0x01, // TTL (4 bytes) is 20 hours, 47 minutes, 48 seconds.
304 0x00, 0x06, // RDLENGTH is 6 bytes.
306 0x00, 0x02, 0x00, 0x08 // Only A record present
309 const uint8 kSamplePacketAPrivet
[] = {
311 0x00, 0x00, // ID is zeroed out
312 0x81, 0x80, // Standard query response, RA, no error
313 0x00, 0x00, // No questions (for simplicity)
314 0x00, 0x01, // 1 RR (answers)
315 0x00, 0x00, // 0 authority RRs
316 0x00, 0x00, // 0 additional RRs
319 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
320 0x04, '_', 't', 'c', 'p',
321 0x05, 'l', 'o', 'c', 'a', 'l',
323 0x00, 0x01, // TYPE is A.
324 0x00, 0x01, // CLASS is IN.
325 0x00, 0x00, // TTL (4 bytes) is 5 seconds
327 0x00, 0x04, // RDLENGTH is 4 bytes.
332 const uint8 kSamplePacketGoodbye
[] = {
334 0x00, 0x00, // ID is zeroed out
335 0x81, 0x80, // Standard query response, RA, no error
336 0x00, 0x00, // No questions (for simplicity)
337 0x00, 0x01, // 2 RRs (answers)
338 0x00, 0x00, // 0 authority RRs
339 0x00, 0x00, // 0 additional RRs
342 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
343 0x04, '_', 't', 'c', 'p',
344 0x05, 'l', 'o', 'c', 'a', 'l',
346 0x00, 0x0c, // TYPE is PTR.
347 0x00, 0x01, // CLASS is IN.
348 0x00, 0x00, // TTL (4 bytes) is zero;
350 0x00, 0x08, // RDLENGTH is 8 bytes.
351 0x05, 'z', 'z', 'z', 'z', 'z',
355 std::string
MakeString(const uint8
* data
, unsigned size
) {
356 return std::string(reinterpret_cast<const char*>(data
), size
);
359 class PtrRecordCopyContainer
{
361 PtrRecordCopyContainer() {}
362 ~PtrRecordCopyContainer() {}
364 bool is_set() const { return set_
; }
366 void SaveWithDummyArg(int unused
, const RecordParsed
* value
) {
370 void Save(const RecordParsed
* value
) {
372 name_
= value
->name();
373 ptrdomain_
= value
->rdata
<PtrRecordRdata
>()->ptrdomain();
377 bool IsRecordWith(std::string name
, std::string ptrdomain
) {
378 return set_
&& name_
== name
&& ptrdomain_
== ptrdomain
;
381 const std::string
& name() { return name_
; }
382 const std::string
& ptrdomain() { return ptrdomain_
; }
383 int ttl() { return ttl_
; }
388 std::string ptrdomain_
;
392 class MDnsTest
: public ::testing::Test
{
394 virtual void SetUp() OVERRIDE
;
395 void DeleteTransaction();
396 void DeleteBothListeners();
397 void RunFor(base::TimeDelta time_period
);
400 MOCK_METHOD2(MockableRecordCallback
, void(MDnsTransaction::Result result
,
401 const RecordParsed
* record
));
403 MOCK_METHOD2(MockableRecordCallback2
, void(MDnsTransaction::Result result
,
404 const RecordParsed
* record
));
408 void ExpectPacket(const uint8
* packet
, unsigned size
);
409 void SimulatePacketReceive(const uint8
* packet
, unsigned size
);
411 MDnsClientImpl test_client_
;
412 IPEndPoint mdns_ipv4_endpoint_
;
413 StrictMock
<MockMDnsSocketFactory
> socket_factory_
;
415 // Transactions and listeners that can be deleted by class methods for
417 scoped_ptr
<MDnsTransaction
> transaction_
;
418 scoped_ptr
<MDnsListener
> listener1_
;
419 scoped_ptr
<MDnsListener
> listener2_
;
422 class MockListenerDelegate
: public MDnsListener::Delegate
{
424 MOCK_METHOD2(OnRecordUpdate
,
425 void(MDnsListener::UpdateType update
,
426 const RecordParsed
* records
));
427 MOCK_METHOD2(OnNsecRecord
, void(const std::string
&, unsigned));
428 MOCK_METHOD0(OnCachePurged
, void());
431 void MDnsTest::SetUp() {
432 test_client_
.StartListening(&socket_factory_
);
435 void MDnsTest::SimulatePacketReceive(const uint8
* packet
, unsigned size
) {
436 socket_factory_
.SimulateReceive(packet
, size
);
439 void MDnsTest::ExpectPacket(const uint8
* packet
, unsigned size
) {
440 EXPECT_CALL(socket_factory_
, OnSendTo(MakeString(packet
, size
)))
444 void MDnsTest::DeleteTransaction() {
445 transaction_
.reset();
448 void MDnsTest::DeleteBothListeners() {
453 void MDnsTest::RunFor(base::TimeDelta time_period
) {
454 base::CancelableCallback
<void()> callback(base::Bind(&MDnsTest::Stop
,
455 base::Unretained(this)));
456 base::MessageLoop::current()->PostDelayedTask(
457 FROM_HERE
, callback
.callback(), time_period
);
459 base::MessageLoop::current()->Run();
463 void MDnsTest::Stop() {
464 base::MessageLoop::current()->Quit();
467 TEST_F(MDnsTest
, PassiveListeners
) {
468 StrictMock
<MockListenerDelegate
> delegate_privet
;
469 StrictMock
<MockListenerDelegate
> delegate_printer
;
471 PtrRecordCopyContainer record_privet
;
472 PtrRecordCopyContainer record_printer
;
474 scoped_ptr
<MDnsListener
> listener_privet
=
475 test_client_
.CreateListener(dns_protocol::kTypePTR
, "_privet._tcp.local",
477 scoped_ptr
<MDnsListener
> listener_printer
=
478 test_client_
.CreateListener(dns_protocol::kTypePTR
, "_printer._tcp.local",
481 ASSERT_TRUE(listener_privet
->Start());
482 ASSERT_TRUE(listener_printer
->Start());
484 // Send the same packet twice to ensure no records are double-counted.
486 EXPECT_CALL(delegate_privet
, OnRecordUpdate(MDnsListener::RECORD_ADDED
, _
))
490 &PtrRecordCopyContainer::SaveWithDummyArg
));
492 EXPECT_CALL(delegate_printer
, OnRecordUpdate(MDnsListener::RECORD_ADDED
, _
))
496 &PtrRecordCopyContainer::SaveWithDummyArg
));
499 SimulatePacketReceive(kSamplePacket1
, sizeof(kSamplePacket1
));
500 SimulatePacketReceive(kSamplePacket1
, sizeof(kSamplePacket1
));
502 EXPECT_TRUE(record_privet
.IsRecordWith("_privet._tcp.local",
503 "hello._privet._tcp.local"));
505 EXPECT_TRUE(record_printer
.IsRecordWith("_printer._tcp.local",
506 "hello._printer._tcp.local"));
508 listener_privet
.reset();
509 listener_printer
.reset();
512 TEST_F(MDnsTest
, PassiveListenersCacheCleanup
) {
513 StrictMock
<MockListenerDelegate
> delegate_privet
;
515 PtrRecordCopyContainer record_privet
;
516 PtrRecordCopyContainer record_privet2
;
518 scoped_ptr
<MDnsListener
> listener_privet
=
519 test_client_
.CreateListener(dns_protocol::kTypePTR
, "_privet._tcp.local",
522 ASSERT_TRUE(listener_privet
->Start());
524 EXPECT_CALL(delegate_privet
, OnRecordUpdate(MDnsListener::RECORD_ADDED
, _
))
528 &PtrRecordCopyContainer::SaveWithDummyArg
));
530 SimulatePacketReceive(kSamplePacket1
, sizeof(kSamplePacket1
));
532 EXPECT_TRUE(record_privet
.IsRecordWith("_privet._tcp.local",
533 "hello._privet._tcp.local"));
535 // Expect record is removed when its TTL expires.
536 EXPECT_CALL(delegate_privet
, OnRecordUpdate(MDnsListener::RECORD_REMOVED
, _
))
538 .WillOnce(DoAll(InvokeWithoutArgs(this, &MDnsTest::Stop
),
539 Invoke(&record_privet2
,
540 &PtrRecordCopyContainer::SaveWithDummyArg
)));
542 RunFor(base::TimeDelta::FromSeconds(record_privet
.ttl() + 1));
544 EXPECT_TRUE(record_privet2
.IsRecordWith("_privet._tcp.local",
545 "hello._privet._tcp.local"));
548 TEST_F(MDnsTest
, MalformedPacket
) {
549 StrictMock
<MockListenerDelegate
> delegate_printer
;
551 PtrRecordCopyContainer record_printer
;
553 scoped_ptr
<MDnsListener
> listener_printer
=
554 test_client_
.CreateListener(dns_protocol::kTypePTR
, "_printer._tcp.local",
557 ASSERT_TRUE(listener_printer
->Start());
559 EXPECT_CALL(delegate_printer
, OnRecordUpdate(MDnsListener::RECORD_ADDED
, _
))
563 &PtrRecordCopyContainer::SaveWithDummyArg
));
565 // First, send unsalvagable packet to ensure we can deal with it.
566 SimulatePacketReceive(kCorruptedPacketUnsalvagable
,
567 sizeof(kCorruptedPacketUnsalvagable
));
569 // Regression test: send a packet where the question cannot be read.
570 SimulatePacketReceive(kCorruptedPacketBadQuestion
,
571 sizeof(kCorruptedPacketBadQuestion
));
573 // Then send salvagable packet to ensure we can extract useful records.
574 SimulatePacketReceive(kCorruptedPacketSalvagable
,
575 sizeof(kCorruptedPacketSalvagable
));
577 EXPECT_TRUE(record_printer
.IsRecordWith("_printer._tcp.local",
578 "hello._printer._tcp.local"));
581 TEST_F(MDnsTest
, TransactionWithEmptyCache
) {
582 ExpectPacket(kQueryPacketPrivet
, sizeof(kQueryPacketPrivet
));
584 scoped_ptr
<MDnsTransaction
> transaction_privet
=
585 test_client_
.CreateTransaction(
586 dns_protocol::kTypePTR
, "_privet._tcp.local",
587 MDnsTransaction::QUERY_NETWORK
|
588 MDnsTransaction::QUERY_CACHE
|
589 MDnsTransaction::SINGLE_RESULT
,
590 base::Bind(&MDnsTest::MockableRecordCallback
,
591 base::Unretained(this)));
593 ASSERT_TRUE(transaction_privet
->Start());
595 PtrRecordCopyContainer record_privet
;
597 EXPECT_CALL(*this, MockableRecordCallback(MDnsTransaction::RESULT_RECORD
, _
))
599 .WillOnce(Invoke(&record_privet
,
600 &PtrRecordCopyContainer::SaveWithDummyArg
));
602 SimulatePacketReceive(kSamplePacket1
, sizeof(kSamplePacket1
));
604 EXPECT_TRUE(record_privet
.IsRecordWith("_privet._tcp.local",
605 "hello._privet._tcp.local"));
608 TEST_F(MDnsTest
, TransactionCacheOnlyNoResult
) {
609 scoped_ptr
<MDnsTransaction
> transaction_privet
=
610 test_client_
.CreateTransaction(
611 dns_protocol::kTypePTR
, "_privet._tcp.local",
612 MDnsTransaction::QUERY_CACHE
|
613 MDnsTransaction::SINGLE_RESULT
,
614 base::Bind(&MDnsTest::MockableRecordCallback
,
615 base::Unretained(this)));
618 MockableRecordCallback(MDnsTransaction::RESULT_NO_RESULTS
, _
))
621 ASSERT_TRUE(transaction_privet
->Start());
624 TEST_F(MDnsTest
, TransactionWithCache
) {
625 // Listener to force the client to listen
626 StrictMock
<MockListenerDelegate
> delegate_irrelevant
;
627 scoped_ptr
<MDnsListener
> listener_irrelevant
=
628 test_client_
.CreateListener(dns_protocol::kTypeA
,
629 "codereview.chromium.local",
630 &delegate_irrelevant
);
632 ASSERT_TRUE(listener_irrelevant
->Start());
634 SimulatePacketReceive(kSamplePacket1
, sizeof(kSamplePacket1
));
637 PtrRecordCopyContainer record_privet
;
639 EXPECT_CALL(*this, MockableRecordCallback(MDnsTransaction::RESULT_RECORD
, _
))
640 .WillOnce(Invoke(&record_privet
,
641 &PtrRecordCopyContainer::SaveWithDummyArg
));
643 scoped_ptr
<MDnsTransaction
> transaction_privet
=
644 test_client_
.CreateTransaction(
645 dns_protocol::kTypePTR
, "_privet._tcp.local",
646 MDnsTransaction::QUERY_NETWORK
|
647 MDnsTransaction::QUERY_CACHE
|
648 MDnsTransaction::SINGLE_RESULT
,
649 base::Bind(&MDnsTest::MockableRecordCallback
,
650 base::Unretained(this)));
652 ASSERT_TRUE(transaction_privet
->Start());
654 EXPECT_TRUE(record_privet
.IsRecordWith("_privet._tcp.local",
655 "hello._privet._tcp.local"));
658 TEST_F(MDnsTest
, AdditionalRecords
) {
659 StrictMock
<MockListenerDelegate
> delegate_privet
;
661 PtrRecordCopyContainer record_privet
;
663 scoped_ptr
<MDnsListener
> listener_privet
=
664 test_client_
.CreateListener(dns_protocol::kTypePTR
, "_privet._tcp.local",
667 ASSERT_TRUE(listener_privet
->Start());
669 EXPECT_CALL(delegate_privet
, OnRecordUpdate(MDnsListener::RECORD_ADDED
, _
))
673 &PtrRecordCopyContainer::SaveWithDummyArg
));
675 SimulatePacketReceive(kSamplePacketAdditionalOnly
,
676 sizeof(kSamplePacketAdditionalOnly
));
678 EXPECT_TRUE(record_privet
.IsRecordWith("_privet._tcp.local",
679 "hello._privet._tcp.local"));
682 TEST_F(MDnsTest
, TransactionTimeout
) {
683 ExpectPacket(kQueryPacketPrivet
, sizeof(kQueryPacketPrivet
));
685 scoped_ptr
<MDnsTransaction
> transaction_privet
=
686 test_client_
.CreateTransaction(
687 dns_protocol::kTypePTR
, "_privet._tcp.local",
688 MDnsTransaction::QUERY_NETWORK
|
689 MDnsTransaction::QUERY_CACHE
|
690 MDnsTransaction::SINGLE_RESULT
,
691 base::Bind(&MDnsTest::MockableRecordCallback
,
692 base::Unretained(this)));
694 ASSERT_TRUE(transaction_privet
->Start());
697 MockableRecordCallback(MDnsTransaction::RESULT_NO_RESULTS
, NULL
))
699 .WillOnce(InvokeWithoutArgs(this, &MDnsTest::Stop
));
701 RunFor(base::TimeDelta::FromSeconds(4));
704 TEST_F(MDnsTest
, TransactionMultipleRecords
) {
705 ExpectPacket(kQueryPacketPrivet
, sizeof(kQueryPacketPrivet
));
707 scoped_ptr
<MDnsTransaction
> transaction_privet
=
708 test_client_
.CreateTransaction(
709 dns_protocol::kTypePTR
, "_privet._tcp.local",
710 MDnsTransaction::QUERY_NETWORK
|
711 MDnsTransaction::QUERY_CACHE
,
712 base::Bind(&MDnsTest::MockableRecordCallback
,
713 base::Unretained(this)));
715 ASSERT_TRUE(transaction_privet
->Start());
717 PtrRecordCopyContainer record_privet
;
718 PtrRecordCopyContainer record_privet2
;
720 EXPECT_CALL(*this, MockableRecordCallback(MDnsTransaction::RESULT_RECORD
, _
))
722 .WillOnce(Invoke(&record_privet
,
723 &PtrRecordCopyContainer::SaveWithDummyArg
))
724 .WillOnce(Invoke(&record_privet2
,
725 &PtrRecordCopyContainer::SaveWithDummyArg
));
727 SimulatePacketReceive(kSamplePacket1
, sizeof(kSamplePacket1
));
728 SimulatePacketReceive(kSamplePacket2
, sizeof(kSamplePacket2
));
730 EXPECT_TRUE(record_privet
.IsRecordWith("_privet._tcp.local",
731 "hello._privet._tcp.local"));
733 EXPECT_TRUE(record_privet2
.IsRecordWith("_privet._tcp.local",
734 "zzzzz._privet._tcp.local"));
736 EXPECT_CALL(*this, MockableRecordCallback(MDnsTransaction::RESULT_DONE
, NULL
))
737 .WillOnce(InvokeWithoutArgs(this, &MDnsTest::Stop
));
739 RunFor(base::TimeDelta::FromSeconds(4));
742 TEST_F(MDnsTest
, TransactionReentrantDelete
) {
743 ExpectPacket(kQueryPacketPrivet
, sizeof(kQueryPacketPrivet
));
745 transaction_
= test_client_
.CreateTransaction(
746 dns_protocol::kTypePTR
, "_privet._tcp.local",
747 MDnsTransaction::QUERY_NETWORK
|
748 MDnsTransaction::QUERY_CACHE
|
749 MDnsTransaction::SINGLE_RESULT
,
750 base::Bind(&MDnsTest::MockableRecordCallback
,
751 base::Unretained(this)));
753 ASSERT_TRUE(transaction_
->Start());
755 EXPECT_CALL(*this, MockableRecordCallback(MDnsTransaction::RESULT_NO_RESULTS
,
758 .WillOnce(DoAll(InvokeWithoutArgs(this, &MDnsTest::DeleteTransaction
),
759 InvokeWithoutArgs(this, &MDnsTest::Stop
)));
761 RunFor(base::TimeDelta::FromSeconds(4));
763 EXPECT_EQ(NULL
, transaction_
.get());
766 TEST_F(MDnsTest
, TransactionReentrantDeleteFromCache
) {
767 StrictMock
<MockListenerDelegate
> delegate_irrelevant
;
768 scoped_ptr
<MDnsListener
> listener_irrelevant
= test_client_
.CreateListener(
769 dns_protocol::kTypeA
, "codereview.chromium.local",
770 &delegate_irrelevant
);
771 ASSERT_TRUE(listener_irrelevant
->Start());
773 SimulatePacketReceive(kSamplePacket1
, sizeof(kSamplePacket1
));
775 transaction_
= test_client_
.CreateTransaction(
776 dns_protocol::kTypePTR
, "_privet._tcp.local",
777 MDnsTransaction::QUERY_NETWORK
|
778 MDnsTransaction::QUERY_CACHE
,
779 base::Bind(&MDnsTest::MockableRecordCallback
,
780 base::Unretained(this)));
782 EXPECT_CALL(*this, MockableRecordCallback(MDnsTransaction::RESULT_RECORD
, _
))
784 .WillOnce(InvokeWithoutArgs(this, &MDnsTest::DeleteTransaction
));
786 ASSERT_TRUE(transaction_
->Start());
788 EXPECT_EQ(NULL
, transaction_
.get());
791 TEST_F(MDnsTest
, TransactionReentrantCacheLookupStart
) {
792 ExpectPacket(kQueryPacketPrivet
, sizeof(kQueryPacketPrivet
));
794 scoped_ptr
<MDnsTransaction
> transaction1
=
795 test_client_
.CreateTransaction(
796 dns_protocol::kTypePTR
, "_privet._tcp.local",
797 MDnsTransaction::QUERY_NETWORK
|
798 MDnsTransaction::QUERY_CACHE
|
799 MDnsTransaction::SINGLE_RESULT
,
800 base::Bind(&MDnsTest::MockableRecordCallback
,
801 base::Unretained(this)));
803 scoped_ptr
<MDnsTransaction
> transaction2
=
804 test_client_
.CreateTransaction(
805 dns_protocol::kTypePTR
, "_printer._tcp.local",
806 MDnsTransaction::QUERY_CACHE
|
807 MDnsTransaction::SINGLE_RESULT
,
808 base::Bind(&MDnsTest::MockableRecordCallback2
,
809 base::Unretained(this)));
811 EXPECT_CALL(*this, MockableRecordCallback2(MDnsTransaction::RESULT_RECORD
,
815 EXPECT_CALL(*this, MockableRecordCallback(MDnsTransaction::RESULT_RECORD
,
818 .WillOnce(IgnoreResult(InvokeWithoutArgs(transaction2
.get(),
819 &MDnsTransaction::Start
)));
821 ASSERT_TRUE(transaction1
->Start());
823 SimulatePacketReceive(kSamplePacket1
, sizeof(kSamplePacket1
));
826 TEST_F(MDnsTest
, GoodbyePacketNotification
) {
827 StrictMock
<MockListenerDelegate
> delegate_privet
;
829 scoped_ptr
<MDnsListener
> listener_privet
= test_client_
.CreateListener(
830 dns_protocol::kTypePTR
, "_privet._tcp.local", &delegate_privet
);
831 ASSERT_TRUE(listener_privet
->Start());
833 SimulatePacketReceive(kSamplePacketGoodbye
, sizeof(kSamplePacketGoodbye
));
835 RunFor(base::TimeDelta::FromSeconds(2));
838 TEST_F(MDnsTest
, GoodbyePacketRemoval
) {
839 StrictMock
<MockListenerDelegate
> delegate_privet
;
841 scoped_ptr
<MDnsListener
> listener_privet
=
842 test_client_
.CreateListener(dns_protocol::kTypePTR
, "_privet._tcp.local",
844 ASSERT_TRUE(listener_privet
->Start());
846 EXPECT_CALL(delegate_privet
, OnRecordUpdate(MDnsListener::RECORD_ADDED
, _
))
849 SimulatePacketReceive(kSamplePacket2
, sizeof(kSamplePacket2
));
851 SimulatePacketReceive(kSamplePacketGoodbye
, sizeof(kSamplePacketGoodbye
));
853 EXPECT_CALL(delegate_privet
, OnRecordUpdate(MDnsListener::RECORD_REMOVED
, _
))
856 RunFor(base::TimeDelta::FromSeconds(2));
859 // In order to reliably test reentrant listener deletes, we create two listeners
860 // and have each of them delete both, so we're guaranteed to try and deliver a
861 // callback to at least one deleted listener.
863 TEST_F(MDnsTest
, ListenerReentrantDelete
) {
864 StrictMock
<MockListenerDelegate
> delegate_privet
;
866 listener1_
= test_client_
.CreateListener(dns_protocol::kTypePTR
,
867 "_privet._tcp.local",
870 listener2_
= test_client_
.CreateListener(dns_protocol::kTypePTR
,
871 "_privet._tcp.local",
874 ASSERT_TRUE(listener1_
->Start());
876 ASSERT_TRUE(listener2_
->Start());
878 EXPECT_CALL(delegate_privet
, OnRecordUpdate(MDnsListener::RECORD_ADDED
, _
))
880 .WillOnce(InvokeWithoutArgs(this, &MDnsTest::DeleteBothListeners
));
882 SimulatePacketReceive(kSamplePacket1
, sizeof(kSamplePacket1
));
884 EXPECT_EQ(NULL
, listener1_
.get());
885 EXPECT_EQ(NULL
, listener2_
.get());
888 ACTION_P(SaveIPAddress
, ip_container
) {
889 ::testing::StaticAssertTypeEq
<const RecordParsed
*, arg1_type
>();
890 ::testing::StaticAssertTypeEq
<IPAddressNumber
*, ip_container_type
>();
892 *ip_container
= arg1
->template rdata
<ARecordRdata
>()->address();
895 TEST_F(MDnsTest
, DoubleRecordDisagreeing
) {
896 IPAddressNumber address
;
897 StrictMock
<MockListenerDelegate
> delegate_privet
;
899 scoped_ptr
<MDnsListener
> listener_privet
=
900 test_client_
.CreateListener(dns_protocol::kTypeA
, "privet.local",
903 ASSERT_TRUE(listener_privet
->Start());
905 EXPECT_CALL(delegate_privet
, OnRecordUpdate(MDnsListener::RECORD_ADDED
, _
))
907 .WillOnce(SaveIPAddress(&address
));
909 SimulatePacketReceive(kCorruptedPacketDoubleRecord
,
910 sizeof(kCorruptedPacketDoubleRecord
));
912 EXPECT_EQ("2.3.4.5", IPAddressToString(address
));
915 TEST_F(MDnsTest
, NsecWithListener
) {
916 StrictMock
<MockListenerDelegate
> delegate_privet
;
917 scoped_ptr
<MDnsListener
> listener_privet
=
918 test_client_
.CreateListener(dns_protocol::kTypeA
, "_privet._tcp.local",
921 // Test to make sure nsec callback is NOT called for PTR
922 // (which is marked as existing).
923 StrictMock
<MockListenerDelegate
> delegate_privet2
;
924 scoped_ptr
<MDnsListener
> listener_privet2
=
925 test_client_
.CreateListener(dns_protocol::kTypePTR
, "_privet._tcp.local",
928 ASSERT_TRUE(listener_privet
->Start());
930 EXPECT_CALL(delegate_privet
,
931 OnNsecRecord("_privet._tcp.local", dns_protocol::kTypeA
));
933 SimulatePacketReceive(kSamplePacketNsec
,
934 sizeof(kSamplePacketNsec
));
937 TEST_F(MDnsTest
, NsecWithTransactionFromNetwork
) {
938 scoped_ptr
<MDnsTransaction
> transaction_privet
=
939 test_client_
.CreateTransaction(
940 dns_protocol::kTypeA
, "_privet._tcp.local",
941 MDnsTransaction::QUERY_NETWORK
|
942 MDnsTransaction::QUERY_CACHE
|
943 MDnsTransaction::SINGLE_RESULT
,
944 base::Bind(&MDnsTest::MockableRecordCallback
,
945 base::Unretained(this)));
947 EXPECT_CALL(socket_factory_
, OnSendTo(_
)).Times(2);
949 ASSERT_TRUE(transaction_privet
->Start());
952 MockableRecordCallback(MDnsTransaction::RESULT_NSEC
, NULL
));
954 SimulatePacketReceive(kSamplePacketNsec
,
955 sizeof(kSamplePacketNsec
));
958 TEST_F(MDnsTest
, NsecWithTransactionFromCache
) {
959 // Force mDNS to listen.
960 StrictMock
<MockListenerDelegate
> delegate_irrelevant
;
961 scoped_ptr
<MDnsListener
> listener_irrelevant
=
962 test_client_
.CreateListener(dns_protocol::kTypePTR
, "_privet._tcp.local",
963 &delegate_irrelevant
);
964 listener_irrelevant
->Start();
966 SimulatePacketReceive(kSamplePacketNsec
,
967 sizeof(kSamplePacketNsec
));
970 MockableRecordCallback(MDnsTransaction::RESULT_NSEC
, NULL
));
972 scoped_ptr
<MDnsTransaction
> transaction_privet_a
=
973 test_client_
.CreateTransaction(
974 dns_protocol::kTypeA
, "_privet._tcp.local",
975 MDnsTransaction::QUERY_NETWORK
|
976 MDnsTransaction::QUERY_CACHE
|
977 MDnsTransaction::SINGLE_RESULT
,
978 base::Bind(&MDnsTest::MockableRecordCallback
,
979 base::Unretained(this)));
981 ASSERT_TRUE(transaction_privet_a
->Start());
983 // Test that a PTR transaction does NOT consider the same NSEC record to be a
984 // valid answer to the query
986 scoped_ptr
<MDnsTransaction
> transaction_privet_ptr
=
987 test_client_
.CreateTransaction(
988 dns_protocol::kTypePTR
, "_privet._tcp.local",
989 MDnsTransaction::QUERY_NETWORK
|
990 MDnsTransaction::QUERY_CACHE
|
991 MDnsTransaction::SINGLE_RESULT
,
992 base::Bind(&MDnsTest::MockableRecordCallback
,
993 base::Unretained(this)));
995 EXPECT_CALL(socket_factory_
, OnSendTo(_
)).Times(2);
997 ASSERT_TRUE(transaction_privet_ptr
->Start());
1000 TEST_F(MDnsTest
, NsecConflictRemoval
) {
1001 StrictMock
<MockListenerDelegate
> delegate_privet
;
1002 scoped_ptr
<MDnsListener
> listener_privet
=
1003 test_client_
.CreateListener(dns_protocol::kTypeA
, "_privet._tcp.local",
1006 ASSERT_TRUE(listener_privet
->Start());
1008 const RecordParsed
* record1
;
1009 const RecordParsed
* record2
;
1011 EXPECT_CALL(delegate_privet
, OnRecordUpdate(MDnsListener::RECORD_ADDED
, _
))
1012 .WillOnce(SaveArg
<1>(&record1
));
1014 SimulatePacketReceive(kSamplePacketAPrivet
,
1015 sizeof(kSamplePacketAPrivet
));
1017 EXPECT_CALL(delegate_privet
, OnRecordUpdate(MDnsListener::RECORD_REMOVED
, _
))
1018 .WillOnce(SaveArg
<1>(&record2
));
1020 EXPECT_CALL(delegate_privet
,
1021 OnNsecRecord("_privet._tcp.local", dns_protocol::kTypeA
));
1023 SimulatePacketReceive(kSamplePacketNsec
,
1024 sizeof(kSamplePacketNsec
));
1026 EXPECT_EQ(record1
, record2
);
1030 TEST_F(MDnsTest
, RefreshQuery
) {
1031 StrictMock
<MockListenerDelegate
> delegate_privet
;
1032 scoped_ptr
<MDnsListener
> listener_privet
=
1033 test_client_
.CreateListener(dns_protocol::kTypeA
, "_privet._tcp.local",
1036 listener_privet
->SetActiveRefresh(true);
1037 ASSERT_TRUE(listener_privet
->Start());
1039 EXPECT_CALL(delegate_privet
, OnRecordUpdate(MDnsListener::RECORD_ADDED
, _
));
1041 SimulatePacketReceive(kSamplePacketAPrivet
,
1042 sizeof(kSamplePacketAPrivet
));
1044 // Expecting 2 calls (one for ipv4 and one for ipv6) for each of the 2
1045 // scheduled refresh queries.
1046 EXPECT_CALL(socket_factory_
, OnSendTo(
1047 MakeString(kQueryPacketPrivetA
, sizeof(kQueryPacketPrivetA
))))
1050 EXPECT_CALL(delegate_privet
, OnRecordUpdate(MDnsListener::RECORD_REMOVED
, _
));
1052 RunFor(base::TimeDelta::FromSeconds(6));
1055 // Note: These tests assume that the ipv4 socket will always be created first.
1056 // This is a simplifying assumption based on the way the code works now.
1057 class SimpleMockSocketFactory
: public MDnsSocketFactory
{
1059 virtual void CreateSockets(
1060 ScopedVector
<DatagramServerSocket
>* sockets
) OVERRIDE
{
1062 sockets
->swap(sockets_
);
1065 void PushSocket(DatagramServerSocket
* socket
) {
1066 sockets_
.push_back(socket
);
1070 ScopedVector
<DatagramServerSocket
> sockets_
;
1073 class MockMDnsConnectionDelegate
: public MDnsConnection::Delegate
{
1075 virtual void HandlePacket(DnsResponse
* response
, int size
) {
1076 HandlePacketInternal(std::string(response
->io_buffer()->data(), size
));
1079 MOCK_METHOD1(HandlePacketInternal
, void(std::string packet
));
1081 MOCK_METHOD1(OnConnectionError
, void(int error
));
1084 class MDnsConnectionTest
: public ::testing::Test
{
1086 MDnsConnectionTest() : connection_(&delegate_
) {
1090 // Follow successful connection initialization.
1091 virtual void SetUp() OVERRIDE
{
1092 socket_ipv4_
= new MockMDnsDatagramServerSocket(ADDRESS_FAMILY_IPV4
);
1093 socket_ipv6_
= new MockMDnsDatagramServerSocket(ADDRESS_FAMILY_IPV6
);
1094 factory_
.PushSocket(socket_ipv6_
);
1095 factory_
.PushSocket(socket_ipv4_
);
1098 bool InitConnection() {
1099 return connection_
.Init(&factory_
);
1102 StrictMock
<MockMDnsConnectionDelegate
> delegate_
;
1104 MockMDnsDatagramServerSocket
* socket_ipv4_
;
1105 MockMDnsDatagramServerSocket
* socket_ipv6_
;
1106 SimpleMockSocketFactory factory_
;
1107 MDnsConnection connection_
;
1108 TestCompletionCallback callback_
;
1111 TEST_F(MDnsConnectionTest
, ReceiveSynchronous
) {
1112 std::string sample_packet
= MakeString(kSamplePacket1
,
1113 sizeof(kSamplePacket1
));
1115 socket_ipv6_
->SetResponsePacket(sample_packet
);
1116 EXPECT_CALL(*socket_ipv4_
, RecvFrom(_
, _
, _
, _
))
1117 .WillOnce(Return(ERR_IO_PENDING
));
1118 EXPECT_CALL(*socket_ipv6_
, RecvFrom(_
, _
, _
, _
))
1120 Invoke(socket_ipv6_
, &MockMDnsDatagramServerSocket::HandleRecvNow
))
1121 .WillOnce(Return(ERR_IO_PENDING
));
1123 EXPECT_CALL(delegate_
, HandlePacketInternal(sample_packet
));
1125 ASSERT_TRUE(InitConnection());
1128 TEST_F(MDnsConnectionTest
, ReceiveAsynchronous
) {
1129 std::string sample_packet
= MakeString(kSamplePacket1
,
1130 sizeof(kSamplePacket1
));
1131 socket_ipv6_
->SetResponsePacket(sample_packet
);
1132 EXPECT_CALL(*socket_ipv4_
, RecvFrom(_
, _
, _
, _
))
1133 .WillOnce(Return(ERR_IO_PENDING
));
1134 EXPECT_CALL(*socket_ipv6_
, RecvFrom(_
, _
, _
, _
))
1136 Invoke(socket_ipv6_
, &MockMDnsDatagramServerSocket::HandleRecvLater
))
1137 .WillOnce(Return(ERR_IO_PENDING
));
1139 ASSERT_TRUE(InitConnection());
1141 EXPECT_CALL(delegate_
, HandlePacketInternal(sample_packet
));
1143 base::MessageLoop::current()->RunUntilIdle();
1146 TEST_F(MDnsConnectionTest
, Send
) {
1147 std::string sample_packet
= MakeString(kSamplePacket1
,
1148 sizeof(kSamplePacket1
));
1150 scoped_refptr
<IOBufferWithSize
> buf(
1151 new IOBufferWithSize(sizeof kSamplePacket1
));
1152 memcpy(buf
->data(), kSamplePacket1
, sizeof(kSamplePacket1
));
1154 EXPECT_CALL(*socket_ipv4_
, RecvFrom(_
, _
, _
, _
))
1155 .WillOnce(Return(ERR_IO_PENDING
));
1156 EXPECT_CALL(*socket_ipv6_
, RecvFrom(_
, _
, _
, _
))
1157 .WillOnce(Return(ERR_IO_PENDING
));
1159 ASSERT_TRUE(InitConnection());
1161 EXPECT_CALL(*socket_ipv4_
,
1162 SendToInternal(sample_packet
, "224.0.0.251:5353", _
));
1163 EXPECT_CALL(*socket_ipv6_
,
1164 SendToInternal(sample_packet
, "[ff02::fb]:5353", _
));
1166 connection_
.Send(buf
, buf
->size());
1169 TEST_F(MDnsConnectionTest
, Error
) {
1170 CompletionCallback callback
;
1172 EXPECT_CALL(*socket_ipv4_
, RecvFrom(_
, _
, _
, _
))
1173 .WillOnce(Return(ERR_IO_PENDING
));
1174 EXPECT_CALL(*socket_ipv6_
, RecvFrom(_
, _
, _
, _
))
1175 .WillOnce(DoAll(SaveArg
<3>(&callback
), Return(ERR_IO_PENDING
)));
1177 ASSERT_TRUE(InitConnection());
1179 EXPECT_CALL(delegate_
, OnConnectionError(ERR_SOCKET_NOT_CONNECTED
));
1180 callback
.Run(ERR_SOCKET_NOT_CONNECTED
);