• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_reader.h"
6 
7 #include <memory>
8 
9 #include "discovery/common/config.h"
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 namespace {
18 
19 constexpr std::chrono::seconds kTtl{120};
20 
21 template <class T>
TestReadEntrySucceeds(const uint8_t * data,size_t size,const T & expected)22 void TestReadEntrySucceeds(const uint8_t* data,
23                            size_t size,
24                            const T& expected) {
25   Config config;
26   MdnsReader reader(config, data, size);
27   T entry;
28   EXPECT_TRUE(reader.Read(&entry));
29   EXPECT_EQ(entry, expected);
30   EXPECT_EQ(reader.remaining(), UINT64_C(0));
31 }
32 
33 template <>
TestReadEntrySucceeds(const uint8_t * data,size_t size,const MdnsMessage & expected)34 void TestReadEntrySucceeds<MdnsMessage>(const uint8_t* data,
35                                         size_t size,
36                                         const MdnsMessage& expected) {
37   MdnsReader reader(Config{}, data, size);
38   const ErrorOr<MdnsMessage> message = reader.Read();
39   EXPECT_TRUE(message.is_value());
40   EXPECT_EQ(message.value(), expected);
41   EXPECT_EQ(reader.remaining(), UINT64_C(0));
42 }
43 
44 template <class T>
TestReadEntryFails(const uint8_t * data,size_t size)45 void TestReadEntryFails(const uint8_t* data, size_t size) {
46   Config config;
47   MdnsReader reader(config, data, size);
48   T entry;
49   EXPECT_FALSE(reader.Read(&entry));
50   // There should be no side effects for failing to read an entry. The
51   // underlying pointer should not have changed.
52   EXPECT_EQ(reader.offset(), UINT64_C(0));
53 }
54 
55 template <>
TestReadEntryFails(const uint8_t * data,size_t size)56 void TestReadEntryFails<MdnsMessage>(const uint8_t* data, size_t size) {
57   Config config;
58   MdnsReader reader(config, data, size);
59   const ErrorOr<MdnsMessage> message = reader.Read();
60   EXPECT_TRUE(message.is_error());
61 
62   // There should be no side effects for failing to read an entry. The
63   // underlying pointer should not have changed.
64   EXPECT_EQ(reader.offset(), UINT64_C(0));
65 }
66 }  // namespace
67 
TEST(MdnsReaderTest,ReadDomainName)68 TEST(MdnsReaderTest, ReadDomainName) {
69   constexpr uint8_t kMessage[] = {
70       // First name
71       0x07, 't', 'e', 's', 't', 'i', 'n', 'g',  // Byte: 0
72       0x05, 'l', 'o', 'c', 'a', 'l',            // Byte: 8
73       0x00,                                     // Byte: 14
74       // Second name
75       0x07, 's', 'e', 'r', 'v', 'i', 'c', 'e',  // Byte: 15
76       0xc0, 0x00,                               // Byte: 23
77       // Third name
78       0x06, 'd', 'e', 'v', 'i', 'c', 'e',  // Byte: 25
79       0xc0, 0x0f,                          // Byte: 32
80       // Fourth name
81       0xc0, 0x20,  // Byte: 34
82   };
83   Config config;
84   MdnsReader reader(config, kMessage, sizeof(kMessage));
85   EXPECT_EQ(reader.begin(), kMessage);
86   EXPECT_EQ(reader.length(), sizeof(kMessage));
87   EXPECT_EQ(reader.offset(), 0u);
88   DomainName name;
89   EXPECT_TRUE(reader.Read(&name));
90   EXPECT_EQ(name.ToString(), "testing.local");
91   EXPECT_TRUE(reader.Read(&name));
92   EXPECT_EQ(name.ToString(), "service.testing.local");
93   EXPECT_TRUE(reader.Read(&name));
94   EXPECT_EQ(name.ToString(), "device.service.testing.local");
95   EXPECT_TRUE(reader.Read(&name));
96   EXPECT_EQ(name.ToString(), "service.testing.local");
97   EXPECT_EQ(reader.offset(), sizeof(kMessage));
98   EXPECT_EQ(0UL, reader.remaining());
99   EXPECT_FALSE(reader.Read(&name));
100 }
101 
TEST(MdnsReaderTest,ReadDomainName_Empty)102 TEST(MdnsReaderTest, ReadDomainName_Empty) {
103   constexpr uint8_t kDomainName[] = {0x00};
104   TestReadEntrySucceeds(kDomainName, sizeof(kDomainName), DomainName());
105 }
106 
TEST(MdnsReaderTest,ReadDomainName_TooShort)107 TEST(MdnsReaderTest, ReadDomainName_TooShort) {
108   // Length 0x03 is longer than available data for the domain name
109   constexpr uint8_t kDomainName[] = {0x03, 'a', 'b'};
110   TestReadEntryFails<DomainName>(kDomainName, sizeof(kDomainName));
111 }
112 
TEST(MdnsReaderTest,ReadDomainName_TooLong)113 TEST(MdnsReaderTest, ReadDomainName_TooLong) {
114   std::vector<uint8_t> kDomainName;
115   for (uint8_t letter = 'a'; letter <= 'z'; ++letter) {
116     constexpr uint8_t repetitions = 10;
117     kDomainName.push_back(repetitions);
118     kDomainName.insert(kDomainName.end(), repetitions, letter);
119   }
120   kDomainName.push_back(0);
121   TestReadEntryFails<DomainName>(kDomainName.data(), kDomainName.size());
122 }
123 
TEST(MdnsReaderTest,ReadDomainName_LabelPointerOutOfBounds)124 TEST(MdnsReaderTest, ReadDomainName_LabelPointerOutOfBounds) {
125   constexpr uint8_t kDomainName[] = {0xc0, 0x02};
126   TestReadEntryFails<DomainName>(kDomainName, sizeof(kDomainName));
127 }
128 
TEST(MdnsReaderTest,ReadDomainName_InvalidLabel)129 TEST(MdnsReaderTest, ReadDomainName_InvalidLabel) {
130   constexpr uint8_t kDomainName[] = {0x80};
131   TestReadEntryFails<DomainName>(kDomainName, sizeof(kDomainName));
132 }
133 
TEST(MdnsReaderTest,ReadDomainName_CircularCompression)134 TEST(MdnsReaderTest, ReadDomainName_CircularCompression) {
135   // clang-format off
136   constexpr uint8_t kDomainName[] = {
137       // NOTE: Circular label pointer at end of name.
138       0x07, 't', 'e', 's', 't', 'i', 'n', 'g',  // Byte: 0
139       0x05, 'l', 'o', 'c', 'a', 'l',            // Byte: 8
140       0xc0, 0x00,                               // Byte: 14
141   };
142   // clang-format on
143   TestReadEntryFails<DomainName>(kDomainName, sizeof(kDomainName));
144 }
145 
TEST(MdnsReaderTest,ReadRawRecordRdata)146 TEST(MdnsReaderTest, ReadRawRecordRdata) {
147   // clang-format off
148   constexpr uint8_t kRawRecordRdata[] = {
149       0x00, 0x08,  // RDLENGTH = 8 bytes
150       0x05, 'c', 'n', 'a', 'm', 'e', 0xc0, 0x00,
151   };
152   // clang-format on
153   TestReadEntrySucceeds(
154       kRawRecordRdata, sizeof(kRawRecordRdata),
155       RawRecordRdata(kRawRecordRdata + 2, sizeof(kRawRecordRdata) - 2));
156 }
157 
TEST(MdnsReaderTest,ReadRawRecordRdata_TooLong)158 TEST(MdnsReaderTest, ReadRawRecordRdata_TooLong) {
159   // clang-format off
160   constexpr uint8_t kRawRecordRdata[] = {
161       0x00, 0x08,  // RDLENGTH = 8 bytes
162       0x05, 'c', 'n', 'a', 'm', 'e', 0xc0, 0x00,
163   };
164   // clang-format on
165 
166   Config config;
167   config.maximum_valid_rdata_size = 1;
168   MdnsReader reader(config, kRawRecordRdata, sizeof(kRawRecordRdata));
169   RawRecordRdata entry;
170   EXPECT_FALSE(reader.Read(&entry));
171   // There should be no side effects for failing to read an entry. The
172   // underlying pointer should not have changed.
173   EXPECT_EQ(reader.offset(), UINT64_C(0));
174 }
175 
TEST(MdnsReaderTest,ReadRawRecord_Empty)176 TEST(MdnsReaderTest, ReadRawRecord_Empty) {
177   // clang-format off
178   constexpr uint8_t kRawRecordRdata[] = {
179       0x00, 0x00,  // RDLENGTH = 0 bytes
180   };
181   // clang-format on
182   TestReadEntrySucceeds(kRawRecordRdata, sizeof(kRawRecordRdata),
183                         RawRecordRdata());
184 }
185 
TEST(MdnsReaderTest,ReadRawRecordRdata_TooShort)186 TEST(MdnsReaderTest, ReadRawRecordRdata_TooShort) {
187   // clang-format off
188   constexpr uint8_t kRawRecordRdata[] = {
189       0x00, 0x05,  // RDLENGTH = 5 bytes is longer that available data
190       0x03, 'f', 'o', 'o'
191   };
192   // clang-format on
193   TestReadEntryFails<RawRecordRdata>(kRawRecordRdata, sizeof(kRawRecordRdata));
194 }
195 
TEST(MdnsReaderTest,ReadSrvRecordRdata)196 TEST(MdnsReaderTest, ReadSrvRecordRdata) {
197   // clang-format off
198   constexpr uint8_t kSrvRecordRdata[] = {
199       0x00, 0x15,  // RDLENGTH = 21
200       0x00, 0x05,  // PRIORITY = 5
201       0x00, 0x06,  // WEIGHT = 6
202       0x1f, 0x49,  // PORT = 8009
203       0x07, 't', 'e', 's', 't', 'i', 'n', 'g',
204       0x05, 'l', 'o', 'c', 'a', 'l', 0x00,
205   };
206   // clang-format on
207   TestReadEntrySucceeds(
208       kSrvRecordRdata, sizeof(kSrvRecordRdata),
209       SrvRecordRdata(5, 6, 8009, DomainName{"testing", "local"}));
210 }
211 
TEST(MdnsReaderTest,ReadSrvRecordRdata_TooShort)212 TEST(MdnsReaderTest, ReadSrvRecordRdata_TooShort) {
213   // clang-format off
214   constexpr uint8_t kSrvRecordRdata[] = {
215       0x00, 0x14,  // RDLENGTH = 21
216       0x00, 0x05,  // PRIORITY = 5
217       0x00, 0x06,  // WEIGHT = 6
218                    // Missing bytes
219   };
220   // clang-format on
221   TestReadEntryFails<SrvRecordRdata>(kSrvRecordRdata, sizeof(kSrvRecordRdata));
222 }
223 
TEST(MdnsReaderTest,ReadSrvRecordRdata_WrongLength)224 TEST(MdnsReaderTest, ReadSrvRecordRdata_WrongLength) {
225   // clang-format off
226   constexpr uint8_t kSrvRecordRdata[] = {
227       0x00, 0x14,  // RDLENGTH = 20
228       0x00, 0x05,  // PRIORITY = 5
229       0x00, 0x06,  // WEIGHT = 6
230       0x1f, 0x49,  // PORT = 8009
231       0x07, 't', 'e', 's', 't', 'i', 'n', 'g',
232       0x05, 'l', 'o', 'c', 'a', 'l', 0x00,
233   };
234   // clang-format on
235   TestReadEntryFails<SrvRecordRdata>(kSrvRecordRdata, sizeof(kSrvRecordRdata));
236 }
237 
TEST(MdnsReaderTest,ReadARecordRdata)238 TEST(MdnsReaderTest, ReadARecordRdata) {
239   constexpr uint8_t kARecordRdata[] = {
240       0x00, 0x4,               // RDLENGTH = 4
241       0x08, 0x08, 0x08, 0x08,  // ADDRESS = 8.8.8.8
242   };
243   TestReadEntrySucceeds(kARecordRdata, sizeof(kARecordRdata),
244                         ARecordRdata(IPAddress{8, 8, 8, 8}));
245 }
246 
TEST(MdnsReaderTest,ReadARecordRdata_TooShort)247 TEST(MdnsReaderTest, ReadARecordRdata_TooShort) {
248   constexpr uint8_t kARecordRdata[] = {
249       0x00, 0x4,   // RDLENGTH = 4
250       0x08, 0x08,  // Address missing bytes
251   };
252   TestReadEntryFails<ARecordRdata>(kARecordRdata, sizeof(kARecordRdata));
253 }
254 
TEST(MdnsReaderTest,ReadARecordRdata_WrongLength)255 TEST(MdnsReaderTest, ReadARecordRdata_WrongLength) {
256   constexpr uint8_t kARecordRdata[] = {
257       0x00, 0x5,               // RDLENGTH = 5
258       0x08, 0x08, 0x08, 0x08,  // ADDRESS = 8.8.8.8
259   };
260   TestReadEntryFails<ARecordRdata>(kARecordRdata, sizeof(kARecordRdata));
261 }
262 
TEST(MdnsReaderTest,ReadAAAARecordRdata)263 TEST(MdnsReaderTest, ReadAAAARecordRdata) {
264   // clang-format off
265   constexpr uint8_t kAAAARecordRdata[] = {
266       0x00, 0x10,  // RDLENGTH = 16
267       // ADDRESS = FE80:0000:0000:0000:0202:B3FF:FE1E:8329
268       0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
269       0x02, 0x02, 0xb3, 0xff, 0xfe, 0x1e, 0x83, 0x29,
270   };
271   // clang-format on
272   TestReadEntrySucceeds(kAAAARecordRdata, sizeof(kAAAARecordRdata),
273                         AAAARecordRdata(IPAddress(IPAddress::Version::kV6,
274                                                   kAAAARecordRdata + 2)));
275 }
276 
TEST(MdnsReaderTest,ReadAAAARecordRdata_TooShort)277 TEST(MdnsReaderTest, ReadAAAARecordRdata_TooShort) {
278   // clang-format off
279   constexpr uint8_t kAAAARecordRdata[] = {
280       0x00, 0x10,  // RDLENGTH = 16
281       // ADDRESS missing bytes
282       0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
283   };
284   // clang-format on
285   TestReadEntryFails<AAAARecordRdata>(kAAAARecordRdata,
286                                       sizeof(kAAAARecordRdata));
287 }
288 
TEST(MdnsReaderTest,ReadAAAARecordRdata_WrongLength)289 TEST(MdnsReaderTest, ReadAAAARecordRdata_WrongLength) {
290   // clang-format off
291   constexpr uint8_t kAAAARecordRdata[] = {
292       0x00, 0x11,  // RDLENGTH = 17
293       // ADDRESS = FE80:0000:0000:0000:0202:B3FF:FE1E:8329
294       0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
295       0x02, 0x02, 0xb3, 0xff, 0xfe, 0x1e, 0x83, 0x29,
296   };
297   // clang-format on
298   TestReadEntryFails<AAAARecordRdata>(kAAAARecordRdata,
299                                       sizeof(kAAAARecordRdata));
300 }
301 
TEST(MdnsReaderTest,ReadPtrRecordRdata)302 TEST(MdnsReaderTest, ReadPtrRecordRdata) {
303   // clang-format off
304   constexpr uint8_t kPtrRecordRdata[] = {
305       0x00, 0x18,  // RDLENGTH = 24
306       0x08, 'm', 'y', 'd', 'e', 'v', 'i', 'c', 'e',
307       0x07, 't', 'e', 's', 't', 'i', 'n', 'g',
308       0x05, 'l', 'o', 'c', 'a',  'l',
309       0x00,
310   };
311   // clang-format on
312   TestReadEntrySucceeds(
313       kPtrRecordRdata, sizeof(kPtrRecordRdata),
314       PtrRecordRdata(DomainName{"mydevice", "testing", "local"}));
315 }
316 
TEST(MdnsReaderTest,ReadPtrRecordRdata_TooShort)317 TEST(MdnsReaderTest, ReadPtrRecordRdata_TooShort) {
318   // clang-format off
319   constexpr uint8_t kPtrRecordRdata[] = {
320       0x00, 0x18,  // RDLENGTH = 24
321       0x08, 'm', 'y', 'd', 'e', 'v', 'i', 'c', 'e',
322       0x07, 't', 'e', 's', 't', 'i', 'n', 'g',
323       0x05, 'l', 'o', 'c'
324       // Missing bytes
325   };
326   // clang-format on
327   TestReadEntryFails<PtrRecordRdata>(kPtrRecordRdata, sizeof(kPtrRecordRdata));
328 }
329 
TEST(MdnsReaderTest,ReadPtrRecordRdata_WrongLength)330 TEST(MdnsReaderTest, ReadPtrRecordRdata_WrongLength) {
331   // clang-format off
332   constexpr uint8_t kPtrRecordRdata[] = {
333       0x00, 0x18,  // RDLENGTH = 24
334       0x07, 't', 'e', 's', 't', 'i', 'n', 'g',
335       0x05, 'l', 'o', 'c', 'a',  'l',
336       0x00,
337   };
338   // clang-format on
339   TestReadEntryFails<PtrRecordRdata>(kPtrRecordRdata, sizeof(kPtrRecordRdata));
340 }
341 
TEST(MdnsReaderTest,ReadTxtRecordRdata)342 TEST(MdnsReaderTest, ReadTxtRecordRdata) {
343   // clang-format off
344   constexpr uint8_t kTxtRecordRdata[] = {
345       0x00, 0x0C,  // RDLENGTH = 12
346       0x05, 'f', 'o', 'o', '=', '1',
347       0x05, 'b', 'a', 'r', '=', '2',
348   };
349   // clang-format on
350   TestReadEntrySucceeds(kTxtRecordRdata, sizeof(kTxtRecordRdata),
351                         MakeTxtRecord({"foo=1", "bar=2"}));
352 }
353 
TEST(MdnsReaderTest,ReadTxtRecordRdata_Empty)354 TEST(MdnsReaderTest, ReadTxtRecordRdata_Empty) {
355   constexpr uint8_t kTxtRecordRdata[] = {
356       0x00, 0x01,  // RDLENGTH = 1
357       0x00,        // empty string
358   };
359   TestReadEntrySucceeds(kTxtRecordRdata, sizeof(kTxtRecordRdata),
360                         TxtRecordRdata());
361 }
362 
TEST(MdnsReaderTest,ReadTxtRecordRdata_WithNullInTheMiddle)363 TEST(MdnsReaderTest, ReadTxtRecordRdata_WithNullInTheMiddle) {
364   // clang-format off
365   constexpr uint8_t kTxtRecordRdata[] = {
366       0x00, 0x10,  // RDLENGTH = 16
367       0x09, 'w', 'i', 't', 'h', '\0', 'N', 'U', 'L', 'L',
368       0x05, 'o', 't', 'h', 'e', 'r',
369   };
370   // clang-format on
371   TestReadEntrySucceeds(
372       kTxtRecordRdata, sizeof(kTxtRecordRdata),
373       MakeTxtRecord({absl::string_view("with\0NULL", 9), "other"}));
374 }
375 
TEST(MdnsReaderTest,ReadTxtRecordRdata_EmptyEntries)376 TEST(MdnsReaderTest, ReadTxtRecordRdata_EmptyEntries) {
377   // clang-format off
378   constexpr uint8_t kTxtRecordRdata[] = {
379       0x00, 0x0F,  // RDLENGTH = 12
380       0x05, 'f', 'o', 'o', '=', '1',
381       0x00,
382       0x00,
383       0x05, 'b', 'a', 'r', '=', '2',
384       0x00,
385   };
386   // clang-format on
387   TestReadEntrySucceeds(kTxtRecordRdata, sizeof(kTxtRecordRdata),
388                         MakeTxtRecord({"foo=1", "bar=2"}));
389 }
390 
TEST(MdnsReaderTest,ReadTxtRecordRdata_TooLong)391 TEST(MdnsReaderTest, ReadTxtRecordRdata_TooLong) {
392   // clang-format off
393   constexpr uint8_t kTxtRecordRdata[] = {
394       0x00, 0x0C,  // RDLENGTH = 12
395       0x05, 'f', 'o', 'o', '=', '1',
396       0x05, 'b', 'a', 'r', '=', '2',
397   };
398   // clang-format on
399 
400   Config config;
401   config.maximum_valid_rdata_size = 1;
402   MdnsReader reader(config, kTxtRecordRdata, sizeof(kTxtRecordRdata));
403   TxtRecordRdata entry;
404   EXPECT_FALSE(reader.Read(&entry));
405   // There should be no side effects for failing to read an entry. The
406   // underlying pointer should not have changed.
407   EXPECT_EQ(reader.offset(), UINT64_C(0));
408 }
409 
TEST(MdnsReaderTest,ReadNsecRecordRdata)410 TEST(MdnsReaderTest, ReadNsecRecordRdata) {
411   // clang-format off
412   constexpr uint8_t kExpectedRdata[] = {
413     0x00, 0x20,  // RDLENGTH = 32
414     0x08, 'm', 'y', 'd', 'e', 'v', 'i', 'c', 'e',
415     0x07, 't', 'e', 's', 't', 'i', 'n', 'g',
416     0x05, 'l', 'o', 'c', 'a',  'l',
417     0x00,
418     // It takes 8 bytes to encode the kA and kSRV records because:
419     // - Both record types have value less than 256, so they are both in window
420     //   block 1.
421     // - The bitmap length for this block is always a single byte
422     // - DnsTypes have the following values:
423     //   - kA = 1 (encoded in byte 1)
424     //     kTXT = 16 (encoded in byte 3)
425     //   - kSRV = 33 (encoded in byte 5)
426     //   - kNSEC = 47 (encoded in 6 bytes)
427     // - The largest of these is 47, so 6 bytes are needed to encode this data.
428     // So the full encoded version is:
429     //   00000000 00000110 01000000 00000000 10000000 00000000 0100000  00000001
430     //   |window| | size | | 0-7  | | 8-15 | |16-23 | |24-31 | |32-39 | |40-47 |
431           0x00,    0x06,    0x40,    0x00,    0x80,    0x00,    0x40,    0x01
432   };
433   // clang-format on
434   TestReadEntrySucceeds(
435       kExpectedRdata, sizeof(kExpectedRdata),
436       NsecRecordRdata(DomainName{"mydevice", "testing", "local"}, DnsType::kA,
437                       DnsType::kTXT, DnsType::kSRV, DnsType::kNSEC));
438 }
439 
TEST(MdnsReaderTest,ReadNsecRecordRdata_TooShort)440 TEST(MdnsReaderTest, ReadNsecRecordRdata_TooShort) {
441   // clang-format off
442   constexpr uint8_t kNsecRecordRdata[] = {
443     0x00, 0x20,  // RDLENGTH = 32
444     0x08, 'm', 'y', 'd', 'e', 'v', 'i', 'c', 'e',
445     0x07, 't', 'e', 's', 't', 'i', 'n', 'g',
446     0x05, 'l', 'o', 'c', 'a',  'l',
447     0x00,
448     0x00, 0x06, 0x40, 0x00
449   };
450   // clang-format on
451   TestReadEntryFails<NsecRecordRdata>(kNsecRecordRdata,
452                                       sizeof(kNsecRecordRdata));
453 }
454 
TEST(MdnsReaderTest,ReadNsecRecordRdata_WrongLength)455 TEST(MdnsReaderTest, ReadNsecRecordRdata_WrongLength) {
456   // clang-format off
457   constexpr uint8_t kNsecRecordRdata[] = {
458     0x00, 0x21,  // RDLENGTH = 33
459     0x08, 'm', 'y', 'd', 'e', 'v', 'i', 'c', 'e',
460     0x07, 't', 'e', 's', 't', 'i', 'n', 'g',
461     0x05, 'l', 'o', 'c', 'a',  'l',
462     0x00,
463     0x00, 0x06, 0x40, 0x00, 0x80, 0x00, 0x40, 0x01
464   };
465   // clang-format on
466   TestReadEntryFails<NsecRecordRdata>(kNsecRecordRdata,
467                                       sizeof(kNsecRecordRdata));
468 }
469 
TEST(MdnsReaderTest,ReadMdnsRecord_ARecordRdata)470 TEST(MdnsReaderTest, ReadMdnsRecord_ARecordRdata) {
471   // clang-format off
472   constexpr uint8_t kTestRecord[] = {
473       0x07, 't', 'e', 's', 't', 'i', 'n', 'g',
474       0x05, 'l', 'o', 'c', 'a', 'l',
475       0x00,
476       0x00, 0x01,              // TYPE = A (1)
477       0x80, 0x01,              // CLASS = IN (1) | CACHE_FLUSH_BIT
478       0x00, 0x00, 0x00, 0x78,  // TTL = 120 seconds
479       0x00, 0x04,              // RDLENGTH = 4 bytes
480       0x08, 0x08, 0x08, 0x08,  // RDATA = 8.8.8.8
481   };
482   // clang-format on
483   TestReadEntrySucceeds(kTestRecord, sizeof(kTestRecord),
484                         MdnsRecord(DomainName{"testing", "local"}, DnsType::kA,
485                                    DnsClass::kIN, RecordType::kUnique, kTtl,
486                                    ARecordRdata(IPAddress{8, 8, 8, 8})));
487 }
488 
TEST(MdnsReaderTest,ReadMdnsRecord_UnknownRecordType)489 TEST(MdnsReaderTest, ReadMdnsRecord_UnknownRecordType) {
490   // clang-format off
491   constexpr uint8_t kTestRecord[] = {
492       0x07, 't', 'e', 's', 't', 'i', 'n', 'g',
493       0x05, 'l', 'o', 'c', 'a', 'l',
494       0x00,
495       0x00, 0x05,              // TYPE = CNAME (5)
496       0x80, 0x01,              // CLASS = IN (1) | CACHE_FLUSH_BIT
497       0x00, 0x00, 0x00, 0x78,  // TTL = 120 seconds
498       0x00, 0x08,              // RDLENGTH = 8 bytes
499       0x05, 'c', 'n', 'a', 'm', 'e', 0xc0, 0x00,
500   };
501   constexpr uint8_t kCnameRdata[] = {
502       0x05, 'c', 'n', 'a', 'm', 'e', 0xc0, 0x00,
503   };
504   // clang-format on
505   TestReadEntrySucceeds(
506       kTestRecord, sizeof(kTestRecord),
507       MdnsRecord(DomainName{"testing", "local"},
508                  static_cast<DnsType>(5) /*CNAME class*/, DnsClass::kIN,
509                  RecordType::kUnique, kTtl,
510                  RawRecordRdata(kCnameRdata, sizeof(kCnameRdata))));
511 }
512 
TEST(MdnsReaderTest,ReadMdnsRecord_CompressedNames)513 TEST(MdnsReaderTest, ReadMdnsRecord_CompressedNames) {
514   // clang-format off
515   constexpr uint8_t kTestRecord[] = {
516       // First message
517       0x07, 't', 'e', 's', 't', 'i', 'n', 'g',
518       0x05, 'l', 'o', 'c', 'a', 'l',
519       0x00,
520       0x00, 0x0c,              // TYPE = PTR (12)
521       0x00, 0x01,              // CLASS = IN (1)
522       0x00, 0x00, 0x00, 0x78,  // TTL = 120 seconds
523       0x00, 0x06,              // RDLENGTH = 6 bytes
524       0x03, 'p',  't',  'r',
525       0xc0, 0x00,              // Domain name label pointer to byte 0
526       // Second message
527       0x03, 'o', 'n', 'e',
528       0x03, 't', 'w', 'o',
529       0xc0, 0x00,              // Domain name label pointer to byte 0
530       0x00, 0x01,              // TYPE = A (1)
531       0x80, 0x01,              // CLASS = IN (1) | CACHE_FLUSH_BIT
532       0x00, 0x00, 0x00, 0x78,  // TTL = 120 seconds
533       0x00, 0x04,              // RDLENGTH = 4 bytes
534       0x08, 0x08, 0x08, 0x08,  // RDATA = 8.8.8.8
535   };
536   // clang-format on
537   Config config;
538   MdnsReader reader(config, kTestRecord, sizeof(kTestRecord));
539 
540   MdnsRecord record;
541   EXPECT_TRUE(reader.Read(&record));
542   EXPECT_EQ(record,
543             MdnsRecord(DomainName{"testing", "local"}, DnsType::kPTR,
544                        DnsClass::kIN, RecordType::kShared, kTtl,
545                        PtrRecordRdata(DomainName{"ptr", "testing", "local"})));
546   EXPECT_TRUE(reader.Read(&record));
547   EXPECT_EQ(record, MdnsRecord(DomainName{"one", "two", "testing", "local"},
548                                DnsType::kA, DnsClass::kIN, RecordType::kUnique,
549                                kTtl, ARecordRdata(IPAddress{8, 8, 8, 8})));
550 }
551 
TEST(MdnsReaderTest,ReadMdnsRecord_MissingRdata)552 TEST(MdnsReaderTest, ReadMdnsRecord_MissingRdata) {
553   // clang-format off
554   constexpr uint8_t kTestRecord[] = {
555       0x05, 'l', 'o', 'c', 'a', 'l',
556       0x00,
557       0x00, 0x01,              // TYPE = A (1)
558       0x00, 0x01,              // CLASS = IN (1)
559       0x00, 0x00, 0x00, 0x78,  // TTL = 120 seconds
560       0x00, 0x04,              // RDLENGTH = 4 bytes
561                                // Missing RDATA
562   };
563   // clang-format on
564   TestReadEntryFails<MdnsRecord>(kTestRecord, sizeof(kTestRecord));
565 }
566 
TEST(MdnsReaderTest,ReadMdnsRecord_InvalidHostName)567 TEST(MdnsReaderTest, ReadMdnsRecord_InvalidHostName) {
568   // clang-format off
569   constexpr uint8_t kTestRecord[] = {
570       // Invalid NAME: length byte too short
571       0x03, 'i', 'n', 'v', 'a', 'l', 'i', 'd',
572       0x00,
573       0x00, 0x01,              // TYPE = A (1)
574       0x00, 0x01,              // CLASS = IN (1)
575       0x00, 0x00, 0x00, 0x78,  // TTL = 120 seconds
576       0x00, 0x04,              // RDLENGTH = 4 bytes
577       0x08, 0x08, 0x08, 0x08,  // RDATA = 8.8.8.8
578   };
579   // clang-format on
580   TestReadEntryFails<MdnsRecord>(kTestRecord, sizeof(kTestRecord));
581 }
582 
TEST(MdnsReaderTest,ReadMdnsQuestion)583 TEST(MdnsReaderTest, ReadMdnsQuestion) {
584   // clang-format off
585   constexpr uint8_t kTestQuestion[] = {
586       0x07, 't', 'e', 's', 't', 'i', 'n', 'g',
587       0x05, 'l', 'o', 'c', 'a', 'l',
588       0x00,
589       0x00, 0x01,  // TYPE = A (1)
590       0x80, 0x01,  // CLASS = IN (1) | UNICAST_BIT
591   };
592   // clang-format on
593   TestReadEntrySucceeds(
594       kTestQuestion, sizeof(kTestQuestion),
595       MdnsQuestion(DomainName{"testing", "local"}, DnsType::kA, DnsClass::kIN,
596                    ResponseType::kUnicast));
597 }
598 
TEST(MdnsReaderTest,ReadMdnsQuestion_CompressedNames)599 TEST(MdnsReaderTest, ReadMdnsQuestion_CompressedNames) {
600   // clang-format off
601   constexpr uint8_t kTestQuestions[] = {
602       // First Question
603       0x05, 'f', 'i', 'r', 's', 't',
604       0x05, 'l', 'o', 'c', 'a', 'l',
605       0x00,
606       0x00, 0x01,  // TYPE = A (1)
607       0x80, 0x01,  // CLASS = IN (1) | UNICAST_BIT
608       // Second Question
609       0x06, 's', 'e', 'c', 'o', 'n', 'd',
610       0xc0, 0x06,  // Domain name label pointer
611       0x00, 0x0c,  // TYPE = PTR (12)
612       0x00, 0x01,  // CLASS = IN (1)
613   };
614   // clang-format on
615   Config config;
616   MdnsReader reader(config, kTestQuestions, sizeof(kTestQuestions));
617   MdnsQuestion question;
618   EXPECT_TRUE(reader.Read(&question));
619   EXPECT_EQ(question, MdnsQuestion(DomainName{"first", "local"}, DnsType::kA,
620                                    DnsClass::kIN, ResponseType::kUnicast));
621   EXPECT_TRUE(reader.Read(&question));
622   EXPECT_EQ(question, MdnsQuestion(DomainName{"second", "local"}, DnsType::kPTR,
623                                    DnsClass::kIN, ResponseType::kMulticast));
624   EXPECT_EQ(reader.remaining(), UINT64_C(0));
625 }
626 
TEST(MdnsReaderTest,ReadMdnsQuestion_InvalidHostName)627 TEST(MdnsReaderTest, ReadMdnsQuestion_InvalidHostName) {
628   // clang-format off
629   constexpr uint8_t kTestQuestion[] = {
630       // Invalid NAME: length byte too short
631       0x03, 'i', 'n', 'v', 'a', 'l', 'i', 'd',
632       0x00,
633       0x00, 0x01,  // TYPE = A (1)
634       0x00, 0x01,  // CLASS = IN (1)
635   };
636   // clang-format on
637   TestReadEntryFails<MdnsQuestion>(kTestQuestion, sizeof(kTestQuestion));
638 }
639 
TEST(MdnsReaderTest,ReadMdnsMessage)640 TEST(MdnsReaderTest, ReadMdnsMessage) {
641   // clang-format off
642   constexpr uint8_t kTestMessage[] = {
643       // Header
644       0x00, 0x01,  // ID = 1
645       0x84, 0x00,  // FLAGS = AA | RESPONSE
646       0x00, 0x00,  // Questions = 0
647       0x00, 0x01,  // Answers = 1
648       0x00, 0x00,  // Authority = 0
649       0x00, 0x01,  // Additional = 1
650       // Record 1
651       0x07, 'r', 'e', 'c', 'o', 'r', 'd', '1',
652       0x00,
653       0x00, 0x0c,              // TYPE = PTR (12)
654       0x00, 0x01,              // CLASS = IN (1)
655       0x00, 0x00, 0x00, 0x78,  // TTL = 120 seconds
656       0x00, 0x0f,              // RDLENGTH = 15 bytes
657       0x07, 't', 'e', 's', 't', 'i', 'n', 'g',
658       0x05, 'l', 'o', 'c', 'a', 'l',
659       0x00,
660       // Record 2
661       0x07, 'r', 'e', 'c', 'o', 'r', 'd', '2',
662       0x00,
663       0x00, 0x01,              // TYPE = A (1)
664       0x00, 0x01,              // CLASS = IN (1)
665       0x00, 0x00, 0x00, 0x78,  // TTL = 120 seconds
666       0x00, 0x04,              // RDLENGTH = 4 bytes
667       0xac, 0x00, 0x00, 0x01,  // 172.0.0.1
668   };
669   // clang-format on
670 
671   MdnsRecord record1(DomainName{"record1"}, DnsType::kPTR, DnsClass::kIN,
672                      RecordType::kShared, kTtl,
673                      PtrRecordRdata(DomainName{"testing", "local"}));
674   MdnsRecord record2(DomainName{"record2"}, DnsType::kA, DnsClass::kIN,
675                      RecordType::kShared, kTtl,
676                      ARecordRdata(IPAddress{172, 0, 0, 1}));
677   MdnsMessage message(1, MessageType::Response, std::vector<MdnsQuestion>{},
678                       std::vector<MdnsRecord>{record1},
679                       std::vector<MdnsRecord>{},
680                       std::vector<MdnsRecord>{record2});
681   TestReadEntrySucceeds(kTestMessage, sizeof(kTestMessage), message);
682 }
683 
TEST(MdnsReaderTest,ReadMdnsMessage_MissingAnswerRecord)684 TEST(MdnsReaderTest, ReadMdnsMessage_MissingAnswerRecord) {
685   // clang-format off
686   constexpr uint8_t kInvalidMessage[] = {
687       0x00, 0x00,  // ID = 0
688       0x00, 0x00,  // FLAGS = 0
689       0x00, 0x01,  // Questions = 1
690       0x00, 0x01,  // Answers = 1
691       0x00, 0x00,  // Authority = 0
692       0x00, 0x00,  // Additional = 0
693       0x07, 't', 'e', 's', 't', 'i', 'n', 'g',
694       0x05, 'l', 'o', 'c', 'a', 'l',
695       0x00,
696       0x00, 0x0c,  // TYPE = PTR (12)
697       0x00, 0x01,  // CLASS = IN (1)
698       // NOTE: Missing answer record
699   };
700   // clang-format on
701   TestReadEntryFails<MdnsMessage>(kInvalidMessage, sizeof(kInvalidMessage));
702 }
703 
TEST(MdnsReaderTest,ReadMdnsMessage_MissingAdditionalRecord)704 TEST(MdnsReaderTest, ReadMdnsMessage_MissingAdditionalRecord) {
705   // clang-format off
706   constexpr uint8_t kInvalidMessage[] = {
707       0x00, 0x00,  // ID = 0
708       0x00, 0x00,  // FLAGS = 0
709       0x00, 0x00,  // Questions = 0
710       0x00, 0x00,  // Answers = 0
711       0x00, 0x00,  // Authority = 0
712       0x00, 0x02,  // Additional = 2
713       0x07, 't', 'e', 's', 't', 'i', 'n', 'g',
714       0x05, 'l', 'o', 'c', 'a', 'l',
715       0x00,
716       0x00, 0x0c,              // TYPE = PTR (12)
717       0x00, 0x01,              // CLASS = IN (1)
718       0x00, 0x00, 0x00, 0x78,  // TTL = 120 seconds
719       0x00, 0x00,              // RDLENGTH = 0
720       // NOTE: Only 1 answer record is given.
721   };
722   // clang-format on
723   TestReadEntryFails<MdnsMessage>(kInvalidMessage, sizeof(kInvalidMessage));
724 }
725 
726 }  // namespace discovery
727 }  // namespace openscreen
728