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_sender.h"
6
7 #include <memory>
8 #include <vector>
9
10 #include "discovery/mdns/mdns_records.h"
11 #include "gmock/gmock.h"
12 #include "gtest/gtest.h"
13 #include "platform/test/fake_udp_socket.h"
14 #include "platform/test/mock_udp_socket.h"
15
16 namespace openscreen {
17 namespace discovery {
18
19 using testing::_;
20 using testing::Args;
21 using testing::Return;
22 using testing::StrictMock;
23 using testing::WithArgs;
24
25 namespace {
26
ACTION_P(VoidPointerMatchesBytes,expected_data)27 ACTION_P(VoidPointerMatchesBytes, expected_data) {
28 const uint8_t* actual_data = static_cast<const uint8_t*>(arg0);
29 for (size_t i = 0; i < expected_data.size(); ++i) {
30 EXPECT_EQ(actual_data[i], expected_data[i]);
31 }
32 }
33
34 } // namespace
35
36 class MdnsSenderTest : public testing::Test {
37 public:
MdnsSenderTest()38 MdnsSenderTest()
39 : a_question_(DomainName{"testing", "local"},
40 DnsType::kA,
41 DnsClass::kIN,
42 ResponseType::kMulticast),
43 a_record_(DomainName{"testing", "local"},
44 DnsType::kA,
45 DnsClass::kIN,
46 RecordType::kShared,
47 std::chrono::seconds(120),
48 ARecordRdata(IPAddress{172, 0, 0, 1})),
49 query_message_(1, MessageType::Query),
50 response_message_(1, MessageType::Response),
51 ipv4_multicast_endpoint_{
52 .address = IPAddress(kDefaultMulticastGroupIPv4),
53 .port = kDefaultMulticastPort},
54 ipv6_multicast_endpoint_{
55 .address = IPAddress(kDefaultMulticastGroupIPv6),
56 .port = kDefaultMulticastPort} {
57 query_message_.AddQuestion(a_question_);
58 response_message_.AddAnswer(a_record_);
59 }
60
61 protected:
62 // clang-format off
63 const std::vector<uint8_t> kQueryBytes = {
64 0x00, 0x01, // ID = 1
65 0x00, 0x00, // FLAGS = None
66 0x00, 0x01, // Question count
67 0x00, 0x00, // Answer count
68 0x00, 0x00, // Authority count
69 0x00, 0x00, // Additional count
70 // Question
71 0x07, 't', 'e', 's', 't', 'i', 'n', 'g',
72 0x05, 'l', 'o', 'c', 'a', 'l',
73 0x00,
74 0x00, 0x01, // TYPE = A (1)
75 0x00, 0x01, // CLASS = IN (1)
76 };
77
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 // clang-format on
96
97 MdnsQuestion a_question_;
98 MdnsRecord a_record_;
99 MdnsMessage query_message_;
100 MdnsMessage response_message_;
101 IPEndpoint ipv4_multicast_endpoint_;
102 IPEndpoint ipv6_multicast_endpoint_;
103 };
104
TEST_F(MdnsSenderTest,SendMulticast)105 TEST_F(MdnsSenderTest, SendMulticast) {
106 StrictMock<MockUdpSocket> socket;
107 EXPECT_CALL(socket, IsIPv4()).WillRepeatedly(Return(true));
108 EXPECT_CALL(socket, IsIPv6()).WillRepeatedly(Return(true));
109 MdnsSender sender(&socket);
110 EXPECT_CALL(socket, SendMessage(_, kQueryBytes.size(), _))
111 .WillOnce(WithArgs<0>(VoidPointerMatchesBytes(kQueryBytes)));
112 EXPECT_EQ(sender.SendMulticast(query_message_), Error::Code::kNone);
113 }
114
TEST_F(MdnsSenderTest,SendUnicastIPv4)115 TEST_F(MdnsSenderTest, SendUnicastIPv4) {
116 IPEndpoint endpoint{.address = IPAddress{192, 168, 1, 1}, .port = 31337};
117
118 StrictMock<MockUdpSocket> socket;
119 MdnsSender sender(&socket);
120 EXPECT_CALL(socket, SendMessage(_, kResponseBytes.size(), _))
121 .WillOnce(WithArgs<0>(VoidPointerMatchesBytes(kResponseBytes)));
122 EXPECT_EQ(sender.SendMessage(response_message_, endpoint),
123 Error::Code::kNone);
124 }
125
TEST_F(MdnsSenderTest,SendUnicastIPv6)126 TEST_F(MdnsSenderTest, SendUnicastIPv6) {
127 constexpr uint16_t kIPv6AddressHextets[] = {
128 0xfe80, 0x0000, 0x0000, 0x0000, 0x0202, 0xb3ff, 0xfe1e, 0x8329,
129 };
130 IPEndpoint endpoint{.address = IPAddress(kIPv6AddressHextets), .port = 31337};
131
132 StrictMock<MockUdpSocket> socket;
133 MdnsSender sender(&socket);
134 EXPECT_CALL(socket, SendMessage(_, kResponseBytes.size(), _))
135 .WillOnce(WithArgs<0>(VoidPointerMatchesBytes(kResponseBytes)));
136 EXPECT_EQ(sender.SendMessage(response_message_, endpoint),
137 Error::Code::kNone);
138 }
139
TEST_F(MdnsSenderTest,MessageTooBig)140 TEST_F(MdnsSenderTest, MessageTooBig) {
141 MdnsMessage big_message_(1, MessageType::Query);
142 for (size_t i = 0; i < 100; ++i) {
143 big_message_.AddQuestion(a_question_);
144 big_message_.AddAnswer(a_record_);
145 }
146
147 StrictMock<MockUdpSocket> socket;
148 EXPECT_CALL(socket, IsIPv4()).WillRepeatedly(Return(true));
149 EXPECT_CALL(socket, IsIPv6()).WillRepeatedly(Return(true));
150 MdnsSender sender(&socket);
151 EXPECT_EQ(sender.SendMulticast(big_message_),
152 Error::Code::kInsufficientBuffer);
153 }
154
TEST_F(MdnsSenderTest,ReturnsErrorOnSocketFailure)155 TEST_F(MdnsSenderTest, ReturnsErrorOnSocketFailure) {
156 FakeUdpSocket::MockClient socket_client;
157 FakeUdpSocket socket(nullptr, &socket_client);
158 MdnsSender sender(&socket);
159 Error error = Error(Error::Code::kConnectionFailed, "error message");
160 socket.EnqueueSendResult(error);
161 EXPECT_CALL(socket_client, OnSendError(_, error)).Times(1);
162 EXPECT_EQ(sender.SendMulticast(query_message_), Error::Code::kNone);
163 EXPECT_EQ(socket.send_queue_size(), size_t{0});
164 }
165
166 } // namespace discovery
167 } // namespace openscreen
168