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