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