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_writer.h"
6*3f982cf4SFabien Sanglard
7*3f982cf4SFabien Sanglard #include <memory>
8*3f982cf4SFabien Sanglard #include <vector>
9*3f982cf4SFabien Sanglard
10*3f982cf4SFabien Sanglard #include "discovery/mdns/testing/mdns_test_util.h"
11*3f982cf4SFabien Sanglard #include "gmock/gmock.h"
12*3f982cf4SFabien Sanglard #include "gtest/gtest.h"
13*3f982cf4SFabien Sanglard
14*3f982cf4SFabien Sanglard namespace openscreen {
15*3f982cf4SFabien Sanglard namespace discovery {
16*3f982cf4SFabien Sanglard
17*3f982cf4SFabien Sanglard using testing::ElementsAreArray;
18*3f982cf4SFabien Sanglard
19*3f982cf4SFabien Sanglard namespace {
20*3f982cf4SFabien Sanglard
21*3f982cf4SFabien Sanglard constexpr std::chrono::seconds kTtl{120};
22*3f982cf4SFabien Sanglard
23*3f982cf4SFabien Sanglard template <class T>
TestWriteEntrySucceeds(const T & entry,const uint8_t * expected_data,size_t expected_size)24*3f982cf4SFabien Sanglard void TestWriteEntrySucceeds(const T& entry,
25*3f982cf4SFabien Sanglard const uint8_t* expected_data,
26*3f982cf4SFabien Sanglard size_t expected_size) {
27*3f982cf4SFabien Sanglard std::vector<uint8_t> buffer(expected_size);
28*3f982cf4SFabien Sanglard MdnsWriter writer(buffer.data(), buffer.size());
29*3f982cf4SFabien Sanglard EXPECT_TRUE(writer.Write(entry));
30*3f982cf4SFabien Sanglard EXPECT_EQ(writer.remaining(), UINT64_C(0));
31*3f982cf4SFabien Sanglard EXPECT_THAT(buffer, ElementsAreArray(expected_data, expected_size));
32*3f982cf4SFabien Sanglard }
33*3f982cf4SFabien Sanglard
34*3f982cf4SFabien Sanglard template <class T>
TestWriteEntryInsufficientBuffer(const T & entry)35*3f982cf4SFabien Sanglard void TestWriteEntryInsufficientBuffer(const T& entry) {
36*3f982cf4SFabien Sanglard std::vector<uint8_t> buffer(entry.MaxWireSize() - 1);
37*3f982cf4SFabien Sanglard MdnsWriter writer(buffer.data(), buffer.size());
38*3f982cf4SFabien Sanglard EXPECT_FALSE(writer.Write(entry));
39*3f982cf4SFabien Sanglard // There should be no side effects for failing to write an entry. The
40*3f982cf4SFabien Sanglard // underlying pointer should not have changed.
41*3f982cf4SFabien Sanglard EXPECT_EQ(writer.offset(), UINT64_C(0));
42*3f982cf4SFabien Sanglard }
43*3f982cf4SFabien Sanglard
44*3f982cf4SFabien Sanglard } // namespace
45*3f982cf4SFabien Sanglard
TEST(MdnsWriterTest,WriteDomainName)46*3f982cf4SFabien Sanglard TEST(MdnsWriterTest, WriteDomainName) {
47*3f982cf4SFabien Sanglard // clang-format off
48*3f982cf4SFabien Sanglard constexpr uint8_t kExpectedResult[] = {
49*3f982cf4SFabien Sanglard 0x07, 't', 'e', 's', 't', 'i', 'n', 'g',
50*3f982cf4SFabien Sanglard 0x05, 'l', 'o', 'c', 'a', 'l',
51*3f982cf4SFabien Sanglard 0x00
52*3f982cf4SFabien Sanglard };
53*3f982cf4SFabien Sanglard // clang-format on
54*3f982cf4SFabien Sanglard uint8_t result[sizeof(kExpectedResult)];
55*3f982cf4SFabien Sanglard MdnsWriter writer(result, sizeof(kExpectedResult));
56*3f982cf4SFabien Sanglard ASSERT_TRUE(writer.Write(DomainName{"testing", "local"}));
57*3f982cf4SFabien Sanglard EXPECT_EQ(0UL, writer.remaining());
58*3f982cf4SFabien Sanglard EXPECT_EQ(0, memcmp(kExpectedResult, result, sizeof(result)));
59*3f982cf4SFabien Sanglard }
60*3f982cf4SFabien Sanglard
TEST(MdnsWriterTest,WriteDomainName_CompressedMessage)61*3f982cf4SFabien Sanglard TEST(MdnsWriterTest, WriteDomainName_CompressedMessage) {
62*3f982cf4SFabien Sanglard // clang-format off
63*3f982cf4SFabien Sanglard constexpr uint8_t kExpectedResultCompressed[] = {
64*3f982cf4SFabien Sanglard 0x07, 't', 'e', 's', 't', 'i', 'n', 'g',
65*3f982cf4SFabien Sanglard 0x05, 'l', 'o', 'c', 'a', 'l',
66*3f982cf4SFabien Sanglard 0x00,
67*3f982cf4SFabien Sanglard 0x06, 'p', 'r', 'e', 'f', 'i', 'x',
68*3f982cf4SFabien Sanglard 0xC0, 0x08, // byte 8
69*3f982cf4SFabien Sanglard 0x03, 'n', 'e', 'w',
70*3f982cf4SFabien Sanglard 0xC0, 0x0F, // byte 15
71*3f982cf4SFabien Sanglard 0xC0, 0x0F, // byte 15
72*3f982cf4SFabien Sanglard };
73*3f982cf4SFabien Sanglard // clang-format on
74*3f982cf4SFabien Sanglard uint8_t result[sizeof(kExpectedResultCompressed)];
75*3f982cf4SFabien Sanglard MdnsWriter writer(result, sizeof(kExpectedResultCompressed));
76*3f982cf4SFabien Sanglard ASSERT_TRUE(writer.Write(DomainName{"testing", "local"}));
77*3f982cf4SFabien Sanglard ASSERT_TRUE(writer.Write(DomainName{"prefix", "local"}));
78*3f982cf4SFabien Sanglard ASSERT_TRUE(writer.Write(DomainName{"new", "prefix", "local"}));
79*3f982cf4SFabien Sanglard ASSERT_TRUE(writer.Write(DomainName{"prefix", "local"}));
80*3f982cf4SFabien Sanglard EXPECT_EQ(0UL, writer.remaining());
81*3f982cf4SFabien Sanglard EXPECT_THAT(std::vector<uint8_t>(result, result + sizeof(result)),
82*3f982cf4SFabien Sanglard ElementsAreArray(kExpectedResultCompressed));
83*3f982cf4SFabien Sanglard }
84*3f982cf4SFabien Sanglard
TEST(MdnsWriterTest,WriteDomainName_NotEnoughSpace)85*3f982cf4SFabien Sanglard TEST(MdnsWriterTest, WriteDomainName_NotEnoughSpace) {
86*3f982cf4SFabien Sanglard // clang-format off
87*3f982cf4SFabien Sanglard constexpr uint8_t kExpectedResultCompressed[] = {
88*3f982cf4SFabien Sanglard 0x07, 't', 'e', 's', 't', 'i', 'n', 'g',
89*3f982cf4SFabien Sanglard 0x05, 'l', 'o', 'c', 'a', 'l',
90*3f982cf4SFabien Sanglard 0x00,
91*3f982cf4SFabien Sanglard 0x09, 'd', 'i', 'f', 'f', 'e', 'r', 'e', 'n', 't',
92*3f982cf4SFabien Sanglard 0x06, 'd', 'o', 'm', 'a', 'i', 'n',
93*3f982cf4SFabien Sanglard 0x00
94*3f982cf4SFabien Sanglard };
95*3f982cf4SFabien Sanglard // clang-format on
96*3f982cf4SFabien Sanglard uint8_t result[sizeof(kExpectedResultCompressed)];
97*3f982cf4SFabien Sanglard MdnsWriter writer(result, sizeof(kExpectedResultCompressed));
98*3f982cf4SFabien Sanglard ASSERT_TRUE(writer.Write(DomainName{"testing", "local"}));
99*3f982cf4SFabien Sanglard // Not enough space to write this domain name. Failure to write it must not
100*3f982cf4SFabien Sanglard // affect correct successful write of the next domain name.
101*3f982cf4SFabien Sanglard ASSERT_FALSE(writer.Write(DomainName{"a", "different", "domain"}));
102*3f982cf4SFabien Sanglard ASSERT_TRUE(writer.Write(DomainName{"different", "domain"}));
103*3f982cf4SFabien Sanglard EXPECT_EQ(0UL, writer.remaining());
104*3f982cf4SFabien Sanglard EXPECT_THAT(std::vector<uint8_t>(result, result + sizeof(result)),
105*3f982cf4SFabien Sanglard ElementsAreArray(kExpectedResultCompressed));
106*3f982cf4SFabien Sanglard }
107*3f982cf4SFabien Sanglard
TEST(MdnsWriterTest,WriteDomainName_Long)108*3f982cf4SFabien Sanglard TEST(MdnsWriterTest, WriteDomainName_Long) {
109*3f982cf4SFabien Sanglard constexpr char kLongLabel[] =
110*3f982cf4SFabien Sanglard "12345678901234567890123456789012345678901234567890";
111*3f982cf4SFabien Sanglard // clang-format off
112*3f982cf4SFabien Sanglard constexpr uint8_t kExpectedResult[] = {
113*3f982cf4SFabien Sanglard 0x32, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3',
114*3f982cf4SFabien Sanglard '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6',
115*3f982cf4SFabien Sanglard '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
116*3f982cf4SFabien Sanglard '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
117*3f982cf4SFabien Sanglard 0x32, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3',
118*3f982cf4SFabien Sanglard '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6',
119*3f982cf4SFabien Sanglard '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
120*3f982cf4SFabien Sanglard '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
121*3f982cf4SFabien Sanglard 0x32, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3',
122*3f982cf4SFabien Sanglard '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6',
123*3f982cf4SFabien Sanglard '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
124*3f982cf4SFabien Sanglard '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
125*3f982cf4SFabien Sanglard 0x32, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3',
126*3f982cf4SFabien Sanglard '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6',
127*3f982cf4SFabien Sanglard '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
128*3f982cf4SFabien Sanglard '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
129*3f982cf4SFabien Sanglard 0x00,
130*3f982cf4SFabien Sanglard };
131*3f982cf4SFabien Sanglard // clang-format on
132*3f982cf4SFabien Sanglard DomainName name{kLongLabel, kLongLabel, kLongLabel, kLongLabel};
133*3f982cf4SFabien Sanglard uint8_t result[sizeof(kExpectedResult)];
134*3f982cf4SFabien Sanglard MdnsWriter writer(result, sizeof(kExpectedResult));
135*3f982cf4SFabien Sanglard ASSERT_TRUE(writer.Write(name));
136*3f982cf4SFabien Sanglard EXPECT_EQ(0UL, writer.remaining());
137*3f982cf4SFabien Sanglard EXPECT_EQ(0, memcmp(kExpectedResult, result, sizeof(result)));
138*3f982cf4SFabien Sanglard }
139*3f982cf4SFabien Sanglard
TEST(MdnsWriterTest,WriteDomainName_Empty)140*3f982cf4SFabien Sanglard TEST(MdnsWriterTest, WriteDomainName_Empty) {
141*3f982cf4SFabien Sanglard DomainName name;
142*3f982cf4SFabien Sanglard uint8_t result[256];
143*3f982cf4SFabien Sanglard MdnsWriter writer(result, sizeof(result));
144*3f982cf4SFabien Sanglard EXPECT_FALSE(writer.Write(name));
145*3f982cf4SFabien Sanglard // The writer should not have moved its internal pointer when it fails to
146*3f982cf4SFabien Sanglard // write. It should fail without any side effects.
147*3f982cf4SFabien Sanglard EXPECT_EQ(0u, writer.offset());
148*3f982cf4SFabien Sanglard }
149*3f982cf4SFabien Sanglard
TEST(MdnsWriterTest,WriteDomainName_NoCompressionForBigOffsets)150*3f982cf4SFabien Sanglard TEST(MdnsWriterTest, WriteDomainName_NoCompressionForBigOffsets) {
151*3f982cf4SFabien Sanglard // clang-format off
152*3f982cf4SFabien Sanglard constexpr uint8_t kExpectedResultCompressed[] = {
153*3f982cf4SFabien Sanglard 0x07, 't', 'e', 's', 't', 'i', 'n', 'g',
154*3f982cf4SFabien Sanglard 0x05, 'l', 'o', 'c', 'a', 'l',
155*3f982cf4SFabien Sanglard 0x00,
156*3f982cf4SFabien Sanglard 0x07, 't', 'e', 's', 't', 'i', 'n', 'g',
157*3f982cf4SFabien Sanglard 0x05, 'l', 'o', 'c', 'a', 'l',
158*3f982cf4SFabien Sanglard 0x00,
159*3f982cf4SFabien Sanglard };
160*3f982cf4SFabien Sanglard // clang-format on
161*3f982cf4SFabien Sanglard
162*3f982cf4SFabien Sanglard DomainName name{"testing", "local"};
163*3f982cf4SFabien Sanglard // Maximum supported value for label pointer offset is 0x3FFF.
164*3f982cf4SFabien Sanglard // Labels written into a buffer at greater offsets must not
165*3f982cf4SFabien Sanglard // produce compression label pointers.
166*3f982cf4SFabien Sanglard std::vector<uint8_t> buffer(0x4000 + sizeof(kExpectedResultCompressed));
167*3f982cf4SFabien Sanglard {
168*3f982cf4SFabien Sanglard MdnsWriter writer(buffer.data(), buffer.size());
169*3f982cf4SFabien Sanglard writer.Skip(0x4000);
170*3f982cf4SFabien Sanglard ASSERT_TRUE(writer.Write(name));
171*3f982cf4SFabien Sanglard ASSERT_TRUE(writer.Write(name));
172*3f982cf4SFabien Sanglard EXPECT_EQ(0UL, writer.remaining());
173*3f982cf4SFabien Sanglard }
174*3f982cf4SFabien Sanglard buffer.erase(buffer.begin(), buffer.begin() + 0x4000);
175*3f982cf4SFabien Sanglard EXPECT_THAT(buffer, ElementsAreArray(kExpectedResultCompressed));
176*3f982cf4SFabien Sanglard }
177*3f982cf4SFabien Sanglard
TEST(MdnsWriterTest,WriteRawRecordRdata)178*3f982cf4SFabien Sanglard TEST(MdnsWriterTest, WriteRawRecordRdata) {
179*3f982cf4SFabien Sanglard // clang-format off
180*3f982cf4SFabien Sanglard constexpr uint8_t kExpectedRdata[] = {
181*3f982cf4SFabien Sanglard 0x00, 0x08, // RDLENGTH = 8 bytes
182*3f982cf4SFabien Sanglard 0x05, 'c', 'n', 'a', 'm', 'e', 0xc0, 0x00,
183*3f982cf4SFabien Sanglard };
184*3f982cf4SFabien Sanglard // clang-format on
185*3f982cf4SFabien Sanglard TestWriteEntrySucceeds(
186*3f982cf4SFabien Sanglard RawRecordRdata(kExpectedRdata + 2, sizeof(kExpectedRdata) - 2),
187*3f982cf4SFabien Sanglard kExpectedRdata, sizeof(kExpectedRdata));
188*3f982cf4SFabien Sanglard }
189*3f982cf4SFabien Sanglard
TEST(MdnsWriterTest,WriteRawRecordRdata_InsufficientBuffer)190*3f982cf4SFabien Sanglard TEST(MdnsWriterTest, WriteRawRecordRdata_InsufficientBuffer) {
191*3f982cf4SFabien Sanglard // clang-format off
192*3f982cf4SFabien Sanglard constexpr uint8_t kRawRdata[] = {
193*3f982cf4SFabien Sanglard 0x05, 'c', 'n', 'a', 'm', 'e', 0xc0, 0x00,
194*3f982cf4SFabien Sanglard };
195*3f982cf4SFabien Sanglard // clang-format on
196*3f982cf4SFabien Sanglard TestWriteEntryInsufficientBuffer(
197*3f982cf4SFabien Sanglard RawRecordRdata(kRawRdata, sizeof(kRawRdata)));
198*3f982cf4SFabien Sanglard }
199*3f982cf4SFabien Sanglard
TEST(MdnsWriterTest,WriteSrvRecordRdata)200*3f982cf4SFabien Sanglard TEST(MdnsWriterTest, WriteSrvRecordRdata) {
201*3f982cf4SFabien Sanglard constexpr uint8_t kExpectedRdata[] = {
202*3f982cf4SFabien Sanglard 0x00, 0x15, // RDLENGTH = 21
203*3f982cf4SFabien Sanglard 0x00, 0x05, // PRIORITY = 5
204*3f982cf4SFabien Sanglard 0x00, 0x06, // WEIGHT = 6
205*3f982cf4SFabien Sanglard 0x1f, 0x49, // PORT = 8009
206*3f982cf4SFabien Sanglard 0x07, 't', 'e', 's', 't', 'i', 'n', 'g',
207*3f982cf4SFabien Sanglard 0x05, 'l', 'o', 'c', 'a', 'l', 0x00,
208*3f982cf4SFabien Sanglard };
209*3f982cf4SFabien Sanglard TestWriteEntrySucceeds(
210*3f982cf4SFabien Sanglard SrvRecordRdata(5, 6, 8009, DomainName{"testing", "local"}),
211*3f982cf4SFabien Sanglard kExpectedRdata, sizeof(kExpectedRdata));
212*3f982cf4SFabien Sanglard }
213*3f982cf4SFabien Sanglard
TEST(MdnsWriterTest,WriteSrvRecordRdata_InsufficientBuffer)214*3f982cf4SFabien Sanglard TEST(MdnsWriterTest, WriteSrvRecordRdata_InsufficientBuffer) {
215*3f982cf4SFabien Sanglard TestWriteEntryInsufficientBuffer(
216*3f982cf4SFabien Sanglard SrvRecordRdata(5, 6, 8009, DomainName{"testing", "local"}));
217*3f982cf4SFabien Sanglard }
218*3f982cf4SFabien Sanglard
TEST(MdnsWriterTest,WriteARecordRdata)219*3f982cf4SFabien Sanglard TEST(MdnsWriterTest, WriteARecordRdata) {
220*3f982cf4SFabien Sanglard constexpr uint8_t kExpectedRdata[] = {
221*3f982cf4SFabien Sanglard 0x00, 0x4, // RDLENGTH = 4
222*3f982cf4SFabien Sanglard 0x08, 0x08, 0x08, 0x08, // ADDRESS = 8.8.8.8
223*3f982cf4SFabien Sanglard };
224*3f982cf4SFabien Sanglard TestWriteEntrySucceeds(ARecordRdata(IPAddress{8, 8, 8, 8}), kExpectedRdata,
225*3f982cf4SFabien Sanglard sizeof(kExpectedRdata));
226*3f982cf4SFabien Sanglard }
227*3f982cf4SFabien Sanglard
TEST(MdnsWriterTest,WriteARecordRdata_InsufficientBuffer)228*3f982cf4SFabien Sanglard TEST(MdnsWriterTest, WriteARecordRdata_InsufficientBuffer) {
229*3f982cf4SFabien Sanglard TestWriteEntryInsufficientBuffer(ARecordRdata(IPAddress{8, 8, 8, 8}));
230*3f982cf4SFabien Sanglard }
231*3f982cf4SFabien Sanglard
TEST(MdnsWriterTest,WriteAAAARecordRdata)232*3f982cf4SFabien Sanglard TEST(MdnsWriterTest, WriteAAAARecordRdata) {
233*3f982cf4SFabien Sanglard // clang-format off
234*3f982cf4SFabien Sanglard constexpr uint8_t kExpectedRdata[] = {
235*3f982cf4SFabien Sanglard 0x00, 0x10, // RDLENGTH = 16
236*3f982cf4SFabien Sanglard // ADDRESS = FE80:0000:0000:0000:0202:B3FF:FE1E:8329
237*3f982cf4SFabien Sanglard 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
238*3f982cf4SFabien Sanglard 0x02, 0x02, 0xb3, 0xff, 0xfe, 0x1e, 0x83, 0x29,
239*3f982cf4SFabien Sanglard };
240*3f982cf4SFabien Sanglard // clang-format on
241*3f982cf4SFabien Sanglard TestWriteEntrySucceeds(
242*3f982cf4SFabien Sanglard AAAARecordRdata(IPAddress(IPAddress::Version::kV6, kExpectedRdata + 2)),
243*3f982cf4SFabien Sanglard kExpectedRdata, sizeof(kExpectedRdata));
244*3f982cf4SFabien Sanglard }
245*3f982cf4SFabien Sanglard
TEST(MdnsWriterTest,WriteAAAARecordRdata_InsufficientBuffer)246*3f982cf4SFabien Sanglard TEST(MdnsWriterTest, WriteAAAARecordRdata_InsufficientBuffer) {
247*3f982cf4SFabien Sanglard // clang-format off
248*3f982cf4SFabien Sanglard constexpr uint16_t kAAAARdata[] = {
249*3f982cf4SFabien Sanglard // ADDRESS = FE80:0000:0000:0000:0202:B3FF:FE1E:8329
250*3f982cf4SFabien Sanglard 0xfe80, 0x0000, 0x0000, 0x0000,
251*3f982cf4SFabien Sanglard 0x0202, 0xb3ff, 0xfe1e, 0x8329,
252*3f982cf4SFabien Sanglard };
253*3f982cf4SFabien Sanglard // clang-format on
254*3f982cf4SFabien Sanglard TestWriteEntryInsufficientBuffer(AAAARecordRdata(IPAddress(kAAAARdata)));
255*3f982cf4SFabien Sanglard }
256*3f982cf4SFabien Sanglard
TEST(MdnsWriterTest,WriteNSECRecordRdata)257*3f982cf4SFabien Sanglard TEST(MdnsWriterTest, WriteNSECRecordRdata) {
258*3f982cf4SFabien Sanglard const DomainName domain{"testing", "local"};
259*3f982cf4SFabien Sanglard NsecRecordRdata(DomainName{"mydevice", "testing", "local"}, DnsType::kA,
260*3f982cf4SFabien Sanglard DnsType::kTXT, DnsType::kSRV, DnsType::kNSEC);
261*3f982cf4SFabien Sanglard
262*3f982cf4SFabien Sanglard // clang-format off
263*3f982cf4SFabien Sanglard constexpr uint8_t kExpectedRdata[] = {
264*3f982cf4SFabien Sanglard 0x00, 0x20, // RDLENGTH = 32
265*3f982cf4SFabien Sanglard 0x08, 'm', 'y', 'd', 'e', 'v', 'i', 'c', 'e',
266*3f982cf4SFabien Sanglard 0x07, 't', 'e', 's', 't', 'i', 'n', 'g',
267*3f982cf4SFabien Sanglard 0x05, 'l', 'o', 'c', 'a', 'l',
268*3f982cf4SFabien Sanglard 0x00,
269*3f982cf4SFabien Sanglard // It takes 8 bytes to encode the kA and kSRV records because:
270*3f982cf4SFabien Sanglard // - Both record types have value less than 256, so they are both in window
271*3f982cf4SFabien Sanglard // block 1.
272*3f982cf4SFabien Sanglard // - The bitmap length for this block is always a single byte
273*3f982cf4SFabien Sanglard // - DnsTypes have the following values:
274*3f982cf4SFabien Sanglard // - kA = 1 (encoded in byte 1)
275*3f982cf4SFabien Sanglard // kTXT = 16 (encoded in byte 3)
276*3f982cf4SFabien Sanglard // - kSRV = 33 (encoded in byte 5)
277*3f982cf4SFabien Sanglard // - kNSEC = 47 (encoded in 6 bytes)
278*3f982cf4SFabien Sanglard // - The largest of these is 47, so 6 bytes are needed to encode this data.
279*3f982cf4SFabien Sanglard // So the full encoded version is:
280*3f982cf4SFabien Sanglard // 00000000 00000110 01000000 00000000 10000000 00000000 0100000 00000001
281*3f982cf4SFabien Sanglard // |window| | size | | 0-7 | | 8-15 | |16-23 | |24-31 | |32-39 | |40-47 |
282*3f982cf4SFabien Sanglard 0x00, 0x06, 0x40, 0x00, 0x80, 0x00, 0x40, 0x01
283*3f982cf4SFabien Sanglard };
284*3f982cf4SFabien Sanglard // clang-format on
285*3f982cf4SFabien Sanglard TestWriteEntrySucceeds(
286*3f982cf4SFabien Sanglard NsecRecordRdata(DomainName{"mydevice", "testing", "local"}, DnsType::kA,
287*3f982cf4SFabien Sanglard DnsType::kTXT, DnsType::kSRV, DnsType::kNSEC),
288*3f982cf4SFabien Sanglard kExpectedRdata, sizeof(kExpectedRdata));
289*3f982cf4SFabien Sanglard }
290*3f982cf4SFabien Sanglard
TEST(MdnsWriterTest,WriteNSECRecordRdata_InsufficientBuffer)291*3f982cf4SFabien Sanglard TEST(MdnsWriterTest, WriteNSECRecordRdata_InsufficientBuffer) {
292*3f982cf4SFabien Sanglard TestWriteEntryInsufficientBuffer(
293*3f982cf4SFabien Sanglard NsecRecordRdata(DomainName{"mydevice", "testing", "local"}, DnsType::kA,
294*3f982cf4SFabien Sanglard DnsType::kTXT, DnsType::kSRV, DnsType::kNSEC));
295*3f982cf4SFabien Sanglard }
296*3f982cf4SFabien Sanglard
TEST(MdnsWriterTest,WritePtrRecordRdata)297*3f982cf4SFabien Sanglard TEST(MdnsWriterTest, WritePtrRecordRdata) {
298*3f982cf4SFabien Sanglard // clang-format off
299*3f982cf4SFabien Sanglard constexpr uint8_t kExpectedRdata[] = {
300*3f982cf4SFabien Sanglard 0x00, 0x18, // RDLENGTH = 24
301*3f982cf4SFabien Sanglard 0x08, 'm', 'y', 'd', 'e', 'v', 'i', 'c', 'e',
302*3f982cf4SFabien Sanglard 0x07, 't', 'e', 's', 't', 'i', 'n', 'g',
303*3f982cf4SFabien Sanglard 0x05, 'l', 'o', 'c', 'a', 'l',
304*3f982cf4SFabien Sanglard 0x00,
305*3f982cf4SFabien Sanglard };
306*3f982cf4SFabien Sanglard // clang-format on
307*3f982cf4SFabien Sanglard TestWriteEntrySucceeds(
308*3f982cf4SFabien Sanglard PtrRecordRdata(DomainName{"mydevice", "testing", "local"}),
309*3f982cf4SFabien Sanglard kExpectedRdata, sizeof(kExpectedRdata));
310*3f982cf4SFabien Sanglard }
311*3f982cf4SFabien Sanglard
TEST(MdnsWriterTest,WritePtrRecordRdata_InsufficientBuffer)312*3f982cf4SFabien Sanglard TEST(MdnsWriterTest, WritePtrRecordRdata_InsufficientBuffer) {
313*3f982cf4SFabien Sanglard TestWriteEntryInsufficientBuffer(
314*3f982cf4SFabien Sanglard PtrRecordRdata(DomainName{"mydevice", "testing", "local"}));
315*3f982cf4SFabien Sanglard }
316*3f982cf4SFabien Sanglard
TEST(MdnsWriterTest,WriteTxtRecordRdata)317*3f982cf4SFabien Sanglard TEST(MdnsWriterTest, WriteTxtRecordRdata) {
318*3f982cf4SFabien Sanglard // clang-format off
319*3f982cf4SFabien Sanglard constexpr uint8_t kExpectedRdata[] = {
320*3f982cf4SFabien Sanglard 0x00, 0x0C, // RDLENGTH = 12
321*3f982cf4SFabien Sanglard 0x05, 'f', 'o', 'o', '=', '1',
322*3f982cf4SFabien Sanglard 0x05, 'b', 'a', 'r', '=', '2',
323*3f982cf4SFabien Sanglard };
324*3f982cf4SFabien Sanglard // clang-format on
325*3f982cf4SFabien Sanglard TestWriteEntrySucceeds(MakeTxtRecord({"foo=1", "bar=2"}), kExpectedRdata,
326*3f982cf4SFabien Sanglard sizeof(kExpectedRdata));
327*3f982cf4SFabien Sanglard }
328*3f982cf4SFabien Sanglard
TEST(MdnsWriterTest,WriteTxtRecordRdata_Empty)329*3f982cf4SFabien Sanglard TEST(MdnsWriterTest, WriteTxtRecordRdata_Empty) {
330*3f982cf4SFabien Sanglard constexpr uint8_t kExpectedRdata[] = {
331*3f982cf4SFabien Sanglard 0x00, 0x01, // RDLENGTH = 1
332*3f982cf4SFabien Sanglard 0x00, // empty string
333*3f982cf4SFabien Sanglard };
334*3f982cf4SFabien Sanglard TestWriteEntrySucceeds(TxtRecordRdata(), kExpectedRdata,
335*3f982cf4SFabien Sanglard sizeof(kExpectedRdata));
336*3f982cf4SFabien Sanglard }
337*3f982cf4SFabien Sanglard
TEST(MdnsWriterTest,WriteTxtRecordRdata_InsufficientBuffer)338*3f982cf4SFabien Sanglard TEST(MdnsWriterTest, WriteTxtRecordRdata_InsufficientBuffer) {
339*3f982cf4SFabien Sanglard TestWriteEntryInsufficientBuffer(MakeTxtRecord({"foo=1", "bar=2"}));
340*3f982cf4SFabien Sanglard }
341*3f982cf4SFabien Sanglard
TEST(MdnsWriterTest,WriteMdnsRecord_ARecordRdata)342*3f982cf4SFabien Sanglard TEST(MdnsWriterTest, WriteMdnsRecord_ARecordRdata) {
343*3f982cf4SFabien Sanglard // clang-format off
344*3f982cf4SFabien Sanglard constexpr uint8_t kExpectedResult[] = {
345*3f982cf4SFabien Sanglard 0x07, 't', 'e', 's', 't', 'i', 'n', 'g',
346*3f982cf4SFabien Sanglard 0x05, 'l', 'o', 'c', 'a', 'l',
347*3f982cf4SFabien Sanglard 0x00,
348*3f982cf4SFabien Sanglard 0x00, 0x01, // TYPE = A (1)
349*3f982cf4SFabien Sanglard 0x80, 0x01, // CLASS = IN (1) | CACHE_FLUSH_BIT
350*3f982cf4SFabien Sanglard 0x00, 0x00, 0x00, 0x78, // TTL = 120 seconds
351*3f982cf4SFabien Sanglard 0x00, 0x04, // RDLENGTH = 4 bytes
352*3f982cf4SFabien Sanglard 0xac, 0x00, 0x00, 0x01, // 172.0.0.1
353*3f982cf4SFabien Sanglard };
354*3f982cf4SFabien Sanglard // clang-format on
355*3f982cf4SFabien Sanglard TestWriteEntrySucceeds(MdnsRecord(DomainName{"testing", "local"}, DnsType::kA,
356*3f982cf4SFabien Sanglard DnsClass::kIN, RecordType::kUnique, kTtl,
357*3f982cf4SFabien Sanglard ARecordRdata(IPAddress{172, 0, 0, 1})),
358*3f982cf4SFabien Sanglard kExpectedResult, sizeof(kExpectedResult));
359*3f982cf4SFabien Sanglard }
360*3f982cf4SFabien Sanglard
TEST(MdnsWriterTest,WriteMdnsRecord_PtrRecordRdata)361*3f982cf4SFabien Sanglard TEST(MdnsWriterTest, WriteMdnsRecord_PtrRecordRdata) {
362*3f982cf4SFabien Sanglard // clang-format off
363*3f982cf4SFabien Sanglard constexpr uint8_t kExpectedResult[] = {
364*3f982cf4SFabien Sanglard 0x08, '_', 's', 'e', 'r', 'v', 'i', 'c', 'e',
365*3f982cf4SFabien Sanglard 0x07, 't', 'e', 's', 't', 'i', 'n', 'g',
366*3f982cf4SFabien Sanglard 0x05, 'l', 'o', 'c', 'a', 'l',
367*3f982cf4SFabien Sanglard 0x00,
368*3f982cf4SFabien Sanglard 0x00, 0x0c, // TYPE = PTR (12)
369*3f982cf4SFabien Sanglard 0x00, 0x01, // CLASS = IN (1)
370*3f982cf4SFabien Sanglard 0x00, 0x00, 0x00, 0x78, // TTL = 120 seconds
371*3f982cf4SFabien Sanglard 0x00, 0x02, // RDLENGTH = 2 bytes
372*3f982cf4SFabien Sanglard 0xc0, 0x09, // Domain name label pointer to byte
373*3f982cf4SFabien Sanglard };
374*3f982cf4SFabien Sanglard // clang-format on
375*3f982cf4SFabien Sanglard TestWriteEntrySucceeds(
376*3f982cf4SFabien Sanglard MdnsRecord(DomainName{"_service", "testing", "local"}, DnsType::kPTR,
377*3f982cf4SFabien Sanglard DnsClass::kIN, RecordType::kShared, kTtl,
378*3f982cf4SFabien Sanglard PtrRecordRdata(DomainName{"testing", "local"})),
379*3f982cf4SFabien Sanglard kExpectedResult, sizeof(kExpectedResult));
380*3f982cf4SFabien Sanglard }
381*3f982cf4SFabien Sanglard
TEST(MdnsWriterTest,WriteMdnsRecord_InsufficientBuffer)382*3f982cf4SFabien Sanglard TEST(MdnsWriterTest, WriteMdnsRecord_InsufficientBuffer) {
383*3f982cf4SFabien Sanglard TestWriteEntryInsufficientBuffer(MdnsRecord(
384*3f982cf4SFabien Sanglard DomainName{"testing", "local"}, DnsType::kA, DnsClass::kIN,
385*3f982cf4SFabien Sanglard RecordType::kUnique, kTtl, ARecordRdata(IPAddress{172, 0, 0, 1})));
386*3f982cf4SFabien Sanglard }
387*3f982cf4SFabien Sanglard
TEST(MdnsWriterTest,WriteMdnsQuestion)388*3f982cf4SFabien Sanglard TEST(MdnsWriterTest, WriteMdnsQuestion) {
389*3f982cf4SFabien Sanglard // clang-format off
390*3f982cf4SFabien Sanglard constexpr uint8_t kExpectedResult[] = {
391*3f982cf4SFabien Sanglard 0x04, 'w', 'i', 'r', 'e',
392*3f982cf4SFabien Sanglard 0x06, 'f', 'o', 'r', 'm', 'a', 't',
393*3f982cf4SFabien Sanglard 0x05, 'l', 'o', 'c', 'a', 'l',
394*3f982cf4SFabien Sanglard 0x00,
395*3f982cf4SFabien Sanglard 0x00, 0x0c, // TYPE = PTR (12)
396*3f982cf4SFabien Sanglard 0x80, 0x01, // CLASS = IN (1) | UNICAST_BIT
397*3f982cf4SFabien Sanglard };
398*3f982cf4SFabien Sanglard // clang-format on
399*3f982cf4SFabien Sanglard TestWriteEntrySucceeds(
400*3f982cf4SFabien Sanglard MdnsQuestion(DomainName{"wire", "format", "local"}, DnsType::kPTR,
401*3f982cf4SFabien Sanglard DnsClass::kIN, ResponseType::kUnicast),
402*3f982cf4SFabien Sanglard kExpectedResult, sizeof(kExpectedResult));
403*3f982cf4SFabien Sanglard }
404*3f982cf4SFabien Sanglard
TEST(MdnsWriterTest,WriteMdnsQuestion_InsufficientBuffer)405*3f982cf4SFabien Sanglard TEST(MdnsWriterTest, WriteMdnsQuestion_InsufficientBuffer) {
406*3f982cf4SFabien Sanglard TestWriteEntryInsufficientBuffer(
407*3f982cf4SFabien Sanglard MdnsQuestion(DomainName{"wire", "format", "local"}, DnsType::kPTR,
408*3f982cf4SFabien Sanglard DnsClass::kIN, ResponseType::kUnicast));
409*3f982cf4SFabien Sanglard }
410*3f982cf4SFabien Sanglard
TEST(MdnsWriterTest,WriteMdnsMessage)411*3f982cf4SFabien Sanglard TEST(MdnsWriterTest, WriteMdnsMessage) {
412*3f982cf4SFabien Sanglard // clang-format off
413*3f982cf4SFabien Sanglard constexpr uint8_t kExpectedMessage[] = {
414*3f982cf4SFabien Sanglard 0x00, 0x01, // ID = 1
415*3f982cf4SFabien Sanglard 0x00, 0x00, // FLAGS = None
416*3f982cf4SFabien Sanglard 0x00, 0x01, // Question count
417*3f982cf4SFabien Sanglard 0x00, 0x00, // Answer count
418*3f982cf4SFabien Sanglard 0x00, 0x01, // Authority count
419*3f982cf4SFabien Sanglard 0x00, 0x00, // Additional count
420*3f982cf4SFabien Sanglard // Question
421*3f982cf4SFabien Sanglard 0x08, 'q', 'u', 'e', 's', 't', 'i', 'o', 'n',
422*3f982cf4SFabien Sanglard 0x00,
423*3f982cf4SFabien Sanglard 0x00, 0x0c, // TYPE = PTR (12)
424*3f982cf4SFabien Sanglard 0x00, 0x01, // CLASS = IN (1)
425*3f982cf4SFabien Sanglard // Authority Record
426*3f982cf4SFabien Sanglard 0x04, 'a', 'u', 't', 'h',
427*3f982cf4SFabien Sanglard 0x00,
428*3f982cf4SFabien Sanglard 0x00, 0x10, // TYPE = TXT (16)
429*3f982cf4SFabien Sanglard 0x00, 0x01, // CLASS = IN (1)
430*3f982cf4SFabien Sanglard 0x00, 0x00, 0x00, 0x78, // TTL = 120 seconds
431*3f982cf4SFabien Sanglard 0x00, 0x0c, // RDLENGTH = 12 bytes
432*3f982cf4SFabien Sanglard 0x05, 'f', 'o', 'o', '=', '1',
433*3f982cf4SFabien Sanglard 0x05, 'b', 'a', 'r', '=', '2',
434*3f982cf4SFabien Sanglard };
435*3f982cf4SFabien Sanglard // clang-format on
436*3f982cf4SFabien Sanglard MdnsQuestion question(DomainName{"question"}, DnsType::kPTR, DnsClass::kIN,
437*3f982cf4SFabien Sanglard ResponseType::kMulticast);
438*3f982cf4SFabien Sanglard
439*3f982cf4SFabien Sanglard MdnsRecord auth_record(DomainName{"auth"}, DnsType::kTXT, DnsClass::kIN,
440*3f982cf4SFabien Sanglard RecordType::kShared, kTtl,
441*3f982cf4SFabien Sanglard MakeTxtRecord({"foo=1", "bar=2"}));
442*3f982cf4SFabien Sanglard
443*3f982cf4SFabien Sanglard MdnsMessage message(1, MessageType::Query);
444*3f982cf4SFabien Sanglard message.AddQuestion(question);
445*3f982cf4SFabien Sanglard message.AddAuthorityRecord(auth_record);
446*3f982cf4SFabien Sanglard
447*3f982cf4SFabien Sanglard std::vector<uint8_t> buffer(sizeof(kExpectedMessage));
448*3f982cf4SFabien Sanglard MdnsWriter writer(buffer.data(), buffer.size());
449*3f982cf4SFabien Sanglard EXPECT_TRUE(writer.Write(message));
450*3f982cf4SFabien Sanglard EXPECT_EQ(writer.remaining(), UINT64_C(0));
451*3f982cf4SFabien Sanglard EXPECT_THAT(buffer, ElementsAreArray(kExpectedMessage));
452*3f982cf4SFabien Sanglard }
453*3f982cf4SFabien Sanglard
TEST(MdnsWriterTest,WriteMdnsMessage_InsufficientBuffer)454*3f982cf4SFabien Sanglard TEST(MdnsWriterTest, WriteMdnsMessage_InsufficientBuffer) {
455*3f982cf4SFabien Sanglard MdnsQuestion question(DomainName{"question"}, DnsType::kPTR, DnsClass::kIN,
456*3f982cf4SFabien Sanglard ResponseType::kMulticast);
457*3f982cf4SFabien Sanglard
458*3f982cf4SFabien Sanglard MdnsRecord auth_record(DomainName{"auth"}, DnsType::kTXT, DnsClass::kIN,
459*3f982cf4SFabien Sanglard RecordType::kShared, kTtl,
460*3f982cf4SFabien Sanglard MakeTxtRecord({"foo=1", "bar=2"}));
461*3f982cf4SFabien Sanglard
462*3f982cf4SFabien Sanglard MdnsMessage message(1, MessageType::Query);
463*3f982cf4SFabien Sanglard message.AddQuestion(question);
464*3f982cf4SFabien Sanglard message.AddAuthorityRecord(auth_record);
465*3f982cf4SFabien Sanglard TestWriteEntryInsufficientBuffer(message);
466*3f982cf4SFabien Sanglard }
467*3f982cf4SFabien Sanglard
468*3f982cf4SFabien Sanglard } // namespace discovery
469*3f982cf4SFabien Sanglard } // namespace openscreen
470