1*3f982cf4SFabien Sanglard // Copyright 2019 The Chromium Authors. All rights reserved.
2*3f982cf4SFabien Sanglard // Use of this source code is governed by a BSD-style license that can be
3*3f982cf4SFabien Sanglard // found in the LICENSE file.
4*3f982cf4SFabien Sanglard
5*3f982cf4SFabien Sanglard #include "discovery/mdns/mdns_receiver.h"
6*3f982cf4SFabien Sanglard
7*3f982cf4SFabien Sanglard #include <memory>
8*3f982cf4SFabien Sanglard #include <utility>
9*3f982cf4SFabien Sanglard #include <vector>
10*3f982cf4SFabien Sanglard
11*3f982cf4SFabien Sanglard #include "discovery/common/config.h"
12*3f982cf4SFabien Sanglard #include "discovery/mdns/mdns_records.h"
13*3f982cf4SFabien Sanglard #include "gmock/gmock.h"
14*3f982cf4SFabien Sanglard #include "gtest/gtest.h"
15*3f982cf4SFabien Sanglard #include "platform/api/time.h"
16*3f982cf4SFabien Sanglard #include "platform/test/fake_udp_socket.h"
17*3f982cf4SFabien Sanglard
18*3f982cf4SFabien Sanglard namespace openscreen {
19*3f982cf4SFabien Sanglard namespace discovery {
20*3f982cf4SFabien Sanglard
21*3f982cf4SFabien Sanglard using testing::_;
22*3f982cf4SFabien Sanglard using testing::Return;
23*3f982cf4SFabien Sanglard
24*3f982cf4SFabien Sanglard class MockMdnsReceiverDelegate : public MdnsReceiver::ResponseClient {
25*3f982cf4SFabien Sanglard public:
26*3f982cf4SFabien Sanglard MOCK_METHOD(void, OnMessageReceived, (const MdnsMessage&));
27*3f982cf4SFabien Sanglard };
28*3f982cf4SFabien Sanglard
TEST(MdnsReceiverTest,ReceiveQuery)29*3f982cf4SFabien Sanglard TEST(MdnsReceiverTest, ReceiveQuery) {
30*3f982cf4SFabien Sanglard // clang-format off
31*3f982cf4SFabien Sanglard const std::vector<uint8_t> kQueryBytes = {
32*3f982cf4SFabien Sanglard 0x00, 0x01, // ID = 1
33*3f982cf4SFabien Sanglard 0x00, 0x00, // FLAGS = None
34*3f982cf4SFabien Sanglard 0x00, 0x01, // Question count
35*3f982cf4SFabien Sanglard 0x00, 0x00, // Answer count
36*3f982cf4SFabien Sanglard 0x00, 0x00, // Authority count
37*3f982cf4SFabien Sanglard 0x00, 0x00, // Additional count
38*3f982cf4SFabien Sanglard // Question
39*3f982cf4SFabien Sanglard 0x07, 't', 'e', 's', 't', 'i', 'n', 'g',
40*3f982cf4SFabien Sanglard 0x05, 'l', 'o', 'c', 'a', 'l',
41*3f982cf4SFabien Sanglard 0x00,
42*3f982cf4SFabien Sanglard 0x00, 0x01, // TYPE = A (1)
43*3f982cf4SFabien Sanglard 0x00, 0x01, // CLASS = IN (1)
44*3f982cf4SFabien Sanglard };
45*3f982cf4SFabien Sanglard // clang-format on
46*3f982cf4SFabien Sanglard
47*3f982cf4SFabien Sanglard Config config;
48*3f982cf4SFabien Sanglard FakeUdpSocket socket;
49*3f982cf4SFabien Sanglard MockMdnsReceiverDelegate delegate;
50*3f982cf4SFabien Sanglard MdnsReceiver receiver(config);
51*3f982cf4SFabien Sanglard receiver.SetQueryCallback(
52*3f982cf4SFabien Sanglard [&delegate](const MdnsMessage& message, const IPEndpoint& endpoint) {
53*3f982cf4SFabien Sanglard delegate.OnMessageReceived(message);
54*3f982cf4SFabien Sanglard });
55*3f982cf4SFabien Sanglard receiver.Start();
56*3f982cf4SFabien Sanglard
57*3f982cf4SFabien Sanglard MdnsQuestion question(DomainName{"testing", "local"}, DnsType::kA,
58*3f982cf4SFabien Sanglard DnsClass::kIN, ResponseType::kMulticast);
59*3f982cf4SFabien Sanglard MdnsMessage message(1, MessageType::Query);
60*3f982cf4SFabien Sanglard message.AddQuestion(question);
61*3f982cf4SFabien Sanglard
62*3f982cf4SFabien Sanglard UdpPacket packet(kQueryBytes.data(), kQueryBytes.data() + kQueryBytes.size());
63*3f982cf4SFabien Sanglard packet.set_source(
64*3f982cf4SFabien Sanglard IPEndpoint{.address = IPAddress(192, 168, 1, 1), .port = 31337});
65*3f982cf4SFabien Sanglard packet.set_destination(
66*3f982cf4SFabien Sanglard IPEndpoint{.address = IPAddress(kDefaultMulticastGroupIPv4),
67*3f982cf4SFabien Sanglard .port = kDefaultMulticastPort});
68*3f982cf4SFabien Sanglard
69*3f982cf4SFabien Sanglard // Imitate a call to OnRead from NetworkRunner by calling it manually here
70*3f982cf4SFabien Sanglard EXPECT_CALL(delegate, OnMessageReceived(message)).Times(1);
71*3f982cf4SFabien Sanglard receiver.OnRead(&socket, std::move(packet));
72*3f982cf4SFabien Sanglard
73*3f982cf4SFabien Sanglard receiver.Stop();
74*3f982cf4SFabien Sanglard }
75*3f982cf4SFabien Sanglard
TEST(MdnsReceiverTest,ReceiveResponse)76*3f982cf4SFabien Sanglard TEST(MdnsReceiverTest, ReceiveResponse) {
77*3f982cf4SFabien Sanglard // clang-format off
78*3f982cf4SFabien Sanglard const std::vector<uint8_t> kResponseBytes = {
79*3f982cf4SFabien Sanglard 0x00, 0x01, // ID = 1
80*3f982cf4SFabien Sanglard 0x84, 0x00, // FLAGS = AA | RESPONSE
81*3f982cf4SFabien Sanglard 0x00, 0x00, // Question count
82*3f982cf4SFabien Sanglard 0x00, 0x01, // Answer count
83*3f982cf4SFabien Sanglard 0x00, 0x00, // Authority count
84*3f982cf4SFabien Sanglard 0x00, 0x00, // Additional count
85*3f982cf4SFabien Sanglard // Answer
86*3f982cf4SFabien Sanglard 0x07, 't', 'e', 's', 't', 'i', 'n', 'g',
87*3f982cf4SFabien Sanglard 0x05, 'l', 'o', 'c', 'a', 'l',
88*3f982cf4SFabien Sanglard 0x00,
89*3f982cf4SFabien Sanglard 0x00, 0x01, // TYPE = A (1)
90*3f982cf4SFabien Sanglard 0x00, 0x01, // CLASS = IN (1)
91*3f982cf4SFabien Sanglard 0x00, 0x00, 0x00, 0x78, // TTL = 120 seconds
92*3f982cf4SFabien Sanglard 0x00, 0x04, // RDLENGTH = 4 bytes
93*3f982cf4SFabien Sanglard 0xac, 0x00, 0x00, 0x01, // 172.0.0.1
94*3f982cf4SFabien Sanglard };
95*3f982cf4SFabien Sanglard
96*3f982cf4SFabien Sanglard constexpr uint16_t kIPv6AddressHextets[] = {
97*3f982cf4SFabien Sanglard 0xfe80, 0x0000, 0x0000, 0x0000,
98*3f982cf4SFabien Sanglard 0x0202, 0xb3ff, 0xfe1e, 0x8329,
99*3f982cf4SFabien Sanglard };
100*3f982cf4SFabien Sanglard // clang-format on
101*3f982cf4SFabien Sanglard
102*3f982cf4SFabien Sanglard Config config;
103*3f982cf4SFabien Sanglard FakeUdpSocket socket;
104*3f982cf4SFabien Sanglard MockMdnsReceiverDelegate delegate;
105*3f982cf4SFabien Sanglard MdnsReceiver receiver(config);
106*3f982cf4SFabien Sanglard receiver.AddResponseCallback(&delegate);
107*3f982cf4SFabien Sanglard receiver.Start();
108*3f982cf4SFabien Sanglard
109*3f982cf4SFabien Sanglard MdnsRecord record(DomainName{"testing", "local"}, DnsType::kA, DnsClass::kIN,
110*3f982cf4SFabien Sanglard RecordType::kShared, std::chrono::seconds(120),
111*3f982cf4SFabien Sanglard ARecordRdata(IPAddress{172, 0, 0, 1}));
112*3f982cf4SFabien Sanglard MdnsMessage message(1, MessageType::Response);
113*3f982cf4SFabien Sanglard message.AddAnswer(record);
114*3f982cf4SFabien Sanglard
115*3f982cf4SFabien Sanglard UdpPacket packet(kResponseBytes.size());
116*3f982cf4SFabien Sanglard packet.assign(kResponseBytes.data(),
117*3f982cf4SFabien Sanglard kResponseBytes.data() + kResponseBytes.size());
118*3f982cf4SFabien Sanglard packet.set_source(
119*3f982cf4SFabien Sanglard IPEndpoint{.address = IPAddress(kIPv6AddressHextets), .port = 31337});
120*3f982cf4SFabien Sanglard packet.set_destination(
121*3f982cf4SFabien Sanglard IPEndpoint{.address = IPAddress(kDefaultMulticastGroupIPv6),
122*3f982cf4SFabien Sanglard .port = kDefaultMulticastPort});
123*3f982cf4SFabien Sanglard
124*3f982cf4SFabien Sanglard // Imitate a call to OnRead from NetworkRunner by calling it manually here
125*3f982cf4SFabien Sanglard EXPECT_CALL(delegate, OnMessageReceived(message)).Times(1);
126*3f982cf4SFabien Sanglard receiver.OnRead(&socket, std::move(packet));
127*3f982cf4SFabien Sanglard
128*3f982cf4SFabien Sanglard receiver.Stop();
129*3f982cf4SFabien Sanglard receiver.RemoveResponseCallback(&delegate);
130*3f982cf4SFabien Sanglard }
131*3f982cf4SFabien Sanglard
132*3f982cf4SFabien Sanglard } // namespace discovery
133*3f982cf4SFabien Sanglard } // namespace openscreen
134