• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright (c) 2020, The OpenThread Authors.
3  *  All rights reserved.
4  *
5  *  Redistribution and use in source and binary forms, with or without
6  *  modification, are permitted provided that the following conditions are met:
7  *  1. Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  *  2. Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *  3. Neither the name of the copyright holder nor the
13  *     names of its contributors may be used to endorse or promote products
14  *     derived from this software without specific prior written permission.
15  *
16  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  *  POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <string.h>
30 
31 #include <openthread/config.h>
32 
33 #include "test_platform.h"
34 #include "test_util.hpp"
35 
36 #include "common/array.hpp"
37 #include "common/instance.hpp"
38 #include "net/dns_types.hpp"
39 
40 namespace ot {
41 
TestDnsName(void)42 void TestDnsName(void)
43 {
44     enum
45     {
46         kMaxSize       = 300,
47         kMaxNameLength = Dns::Name::kMaxNameSize - 1,
48     };
49 
50     struct TestName
51     {
52         const char *   mName;
53         uint16_t       mEncodedLength;
54         const uint8_t *mEncodedData;
55         const char **  mLabels;
56         const char *   mExpectedReadName;
57     };
58 
59     Instance *   instance;
60     MessagePool *messagePool;
61     Message *    message;
62     uint8_t      buffer[kMaxSize];
63     uint16_t     len;
64     uint16_t     offset;
65     char         label[Dns::Name::kMaxLabelSize];
66     uint8_t      labelLength;
67     char         name[Dns::Name::kMaxNameSize];
68     const char * subDomain;
69     const char * domain;
70 
71     static const uint8_t kEncodedName1[] = {7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 3, 'c', 'o', 'm', 0};
72     static const uint8_t kEncodedName2[] = {3, 'f', 'o', 'o', 1, 'a', 2, 'b', 'b', 3, 'e', 'd', 'u', 0};
73     static const uint8_t kEncodedName3[] = {10, 'f', 'o', 'u', 'n', 'd', 'a', 't', 'i', 'o', 'n', 0};
74     static const uint8_t kEncodedName4[] = {0};
75 
76     static const char *kLabels1[] = {"example", "com", nullptr};
77     static const char *kLabels2[] = {"foo", "a", "bb", "edu", nullptr};
78     static const char *kLabels3[] = {"foundation", nullptr};
79     static const char *kLabels4[] = {nullptr};
80 
81     static const TestName kTestNames[] = {
82         {"example.com", sizeof(kEncodedName1), kEncodedName1, kLabels1, "example.com."},
83         {"example.com.", sizeof(kEncodedName1), kEncodedName1, kLabels1, "example.com."},
84         {"foo.a.bb.edu", sizeof(kEncodedName2), kEncodedName2, kLabels2, "foo.a.bb.edu."},
85         {"foo.a.bb.edu.", sizeof(kEncodedName2), kEncodedName2, kLabels2, "foo.a.bb.edu."},
86         {"foundation", sizeof(kEncodedName3), kEncodedName3, kLabels3, "foundation."},
87         {"foundation.", sizeof(kEncodedName3), kEncodedName3, kLabels3, "foundation."},
88         {"", sizeof(kEncodedName4), kEncodedName4, kLabels4, "."},
89         {".", sizeof(kEncodedName4), kEncodedName4, kLabels4, "."},
90         {nullptr, sizeof(kEncodedName4), kEncodedName4, kLabels4, "."},
91     };
92 
93     static const char *kMaxLengthNames[] = {
94         "HereIsSomeoneHidden.MyHoldFromMeTaken.FromSelfHasMeDriven.MyLeadFromMeTaken."
95         "HereIsSomeoneHidden.AsLifeSweeterThanLife.TakesMeToGardenOfSoul.MyFortFromMeTaken."
96         "HereIsSomeoneHidden.LikeSugarInSugarCane.ASweetSugarTrader.MyShopFromMeTaken."
97         "SorcererAndMagicia.",
98 
99         "HereIsSomeoneHidden.MyHoldFromMeTaken.FromSelfHasMeDriven.MyLeadFromMeTaken."
100         "HereIsSomeoneHidden.AsLifeSweeterThanLife.TakesMeToGardenOfSoul.MyFortFromMeTaken."
101         "HereIsSomeoneHidden.LikeSugarInSugarCane.ASweetSugarTrader.MyShopFromMeTaken."
102         "SorcererAndMagicia",
103     };
104 
105     static const char *kInvalidNames[] = {
106         "foo..bar",
107         "..",
108         "a..",
109         "..b",
110 
111         // Long label
112         "a.an-invalid-very-long-label-string-with-more-than-sixty-four-characters.com",
113 
114         // Long name (more than 255 characters)
115         "HereIsSomeoneHidden.MyHoldFromMeTaken.FromSelfHasMeDriven.MyLeadFromMeTaken."
116         "HereIsSomeoneHidden.AsLifeSweeterThanLife.TakesMeToGardenOfSoul.MyFortFromMeTaken."
117         "HereIsSomeoneHidden.LikeSugarInSugarCane.ASweetSugarTrader.MyShopFromMeTaken."
118         "SorcererAndMagician.NoEyesCanEverSee.AnArtfulConjurer.MySenseFromMeTaken."
119         "MyEyesWillNeverSee.BeautiesOfTheWholeWorld.BeholdWhoseVisionFine.MySightFromMeTaken"
120         "PoemByRumiMolana",
121 
122         // Long name of 255 characters which ends with a dot
123         "HereIsSomeoneHidden.MyHoldFromMeTaken.FromSelfHasMeDriven.MyLeadFromMeTaken."
124         "HereIsSomeoneHidden.AsLifeSweeterThanLife.TakesMeToGardenOfSoul.MyFortFromMeTaken."
125         "HereIsSomeoneHidden.LikeSugarInSugarCane.ASweetSugarTrader.MyShopFromMeTaken."
126         "SorcererAndMagician.",
127 
128         // Long name of 254 characters which does not end with a dot
129         "HereIsSomeoneHidden.MyHoldFromMeTaken.FromSelfHasMeDriven.MyLeadFromMeTaken."
130         "HereIsSomeoneHidden.AsLifeSweeterThanLife.TakesMeToGardenOfSoul.MyFortFromMeTaken."
131         "HereIsSomeoneHidden.LikeSugarInSugarCane.ASweetSugarTrader.MyShopFromMeTaken."
132         "SorcererAndMagician",
133 
134     };
135 
136     static const char kBadLabel[] = "badlabel";
137     static const char kBadName[]  = "bad.name";
138 
139     printf("================================================================\n");
140     printf("TestDnsName()\n");
141 
142     instance = static_cast<Instance *>(testInitInstance());
143     VerifyOrQuit(instance != nullptr, "Null OpenThread instance");
144 
145     messagePool = &instance->Get<MessagePool>();
146     VerifyOrQuit((message = messagePool->Allocate(Message::kTypeIp6)) != nullptr);
147 
148     message->SetOffset(0);
149 
150     printf("----------------------------------------------------------------\n");
151     printf("Verify domain name match:\n");
152 
153     subDomain = "my-service._ipps._tcp.local.";
154     domain    = "local.";
155     VerifyOrQuit(Dns::Name::IsSubDomainOf(subDomain, domain));
156 
157     subDomain = "my-service._ipps._tcp.local";
158     domain    = "local.";
159     VerifyOrQuit(Dns::Name::IsSubDomainOf(subDomain, domain));
160 
161     subDomain = "my-service._ipps._tcp.local.";
162     domain    = "local";
163     VerifyOrQuit(Dns::Name::IsSubDomainOf(subDomain, domain));
164 
165     subDomain = "my-service._ipps._tcp.local";
166     domain    = "local";
167     VerifyOrQuit(Dns::Name::IsSubDomainOf(subDomain, domain));
168 
169     subDomain = "my-service._ipps._tcp.default.service.arpa.";
170     domain    = "default.service.arpa.";
171     VerifyOrQuit(Dns::Name::IsSubDomainOf(subDomain, domain));
172 
173     subDomain = "my-service._ipps._tcp.default.service.arpa.";
174     domain    = "service.arpa.";
175     VerifyOrQuit(Dns::Name::IsSubDomainOf(subDomain, domain));
176 
177     // Verify it doesn't match a portion of a label.
178     subDomain = "my-service._ipps._tcp.default.service.arpa.";
179     domain    = "vice.arpa.";
180     VerifyOrQuit(!Dns::Name::IsSubDomainOf(subDomain, domain));
181 
182     // Validate case does not matter
183 
184     subDomain = "my-service._ipps._tcp.local.";
185     domain    = "LOCAL.";
186     VerifyOrQuit(Dns::Name::IsSubDomainOf(subDomain, domain));
187 
188     subDomain = "my-service._ipps._tcp.local";
189     domain    = "LOCAL.";
190     VerifyOrQuit(Dns::Name::IsSubDomainOf(subDomain, domain));
191 
192     subDomain = "my-service._ipps._tcp.local.";
193     domain    = "LOCAL";
194     VerifyOrQuit(Dns::Name::IsSubDomainOf(subDomain, domain));
195 
196     subDomain = "my-service._ipps._tcp.local";
197     domain    = "LOCAL";
198     VerifyOrQuit(Dns::Name::IsSubDomainOf(subDomain, domain));
199 
200     subDomain = "my-service._ipps._tcp.Default.Service.ARPA.";
201     domain    = "dEFAULT.Service.arpa.";
202     VerifyOrQuit(Dns::Name::IsSubDomainOf(subDomain, domain));
203 
204     subDomain = "my-service._ipps._tcp.default.service.ARpa.";
205     domain    = "SeRvIcE.arPA.";
206     VerifyOrQuit(Dns::Name::IsSubDomainOf(subDomain, domain));
207 
208     // Verify it doesn't match a portion of a label.
209     subDomain = "my-service._ipps._tcp.default.service.arpa.";
210     domain    = "Vice.arpa.";
211     VerifyOrQuit(!Dns::Name::IsSubDomainOf(subDomain, domain));
212 
213     printf("----------------------------------------------------------------\n");
214     printf("Append names, check encoded bytes, parse name and read labels:\n");
215 
216     for (const TestName &test : kTestNames)
217     {
218         IgnoreError(message->SetLength(0));
219 
220         SuccessOrQuit(Dns::Name::AppendName(test.mName, *message));
221 
222         len = message->GetLength();
223         SuccessOrQuit(message->Read(0, buffer, len));
224 
225         DumpBuffer(test.mName, buffer, len);
226 
227         VerifyOrQuit(len == test.mEncodedLength, "Encoded length does not match expected value");
228         VerifyOrQuit(memcmp(buffer, test.mEncodedData, len) == 0, "Encoded name data does not match expected data");
229 
230         // Parse and skip over the name
231         offset = 0;
232         SuccessOrQuit(Dns::Name::ParseName(*message, offset));
233         VerifyOrQuit(offset == len, "Name::ParseName() returned incorrect offset");
234 
235         // Read labels one by one.
236         offset = 0;
237 
238         for (uint8_t index = 0; test.mLabels[index] != nullptr; index++)
239         {
240             labelLength = sizeof(label);
241             SuccessOrQuit(Dns::Name::ReadLabel(*message, offset, label, labelLength));
242 
243             printf("Label[%d] = \"%s\"\n", index, label);
244 
245             VerifyOrQuit(strcmp(label, test.mLabels[index]) == 0, "Name::ReadLabel() did not get expected label");
246             VerifyOrQuit(labelLength == strlen(label), "Name::ReadLabel() returned incorrect label length");
247         }
248 
249         labelLength = sizeof(label);
250         VerifyOrQuit(Dns::Name::ReadLabel(*message, offset, label, labelLength) == kErrorNotFound,
251                      "Name::ReadLabel() failed at end of the name");
252 
253         // Read entire name
254         offset = 0;
255         SuccessOrQuit(Dns::Name::ReadName(*message, offset, name, sizeof(name)));
256 
257         printf("Read name =\"%s\"\n", name);
258 
259         VerifyOrQuit(strcmp(name, test.mExpectedReadName) == 0, "Name::ReadName() did not get expected name");
260         VerifyOrQuit(offset == len, "Name::ReadName() returned incorrect offset");
261 
262         // Read entire name with different name buffer sizes (just right and one byte off the expected size)
263         offset = 0;
264         SuccessOrQuit(
265             Dns::Name::ReadName(*message, offset, name, static_cast<uint16_t>(strlen(test.mExpectedReadName) + 1)),
266             "Name::ReadName() failed with exact name buffer size");
267         offset = 0;
268         VerifyOrQuit(Dns::Name::ReadName(*message, offset, name,
269                                          static_cast<uint16_t>(strlen(test.mExpectedReadName))) == kErrorNoBufs,
270                      "Name::ReadName() did not fail with too small name buffer size");
271 
272         // Compare labels one by one.
273         offset = 0;
274 
275         for (uint8_t index = 0; test.mLabels[index] != nullptr; index++)
276         {
277             uint16_t startOffset = offset;
278 
279             strcpy(label, test.mLabels[index]);
280 
281             SuccessOrQuit(Dns::Name::CompareLabel(*message, offset, label));
282             VerifyOrQuit(offset != startOffset, "Name::CompareLabel() did not change offset");
283 
284             offset = startOffset;
285             VerifyOrQuit(Dns::Name::CompareLabel(*message, offset, kBadLabel) == kErrorNotFound,
286                          "Name::CompareLabel() did not fail with incorrect label");
287 
288             StringConvertToUppercase(label);
289 
290             offset = startOffset;
291             SuccessOrQuit(Dns::Name::CompareLabel(*message, offset, label));
292         }
293 
294         // Compare the whole name.
295         strcpy(name, test.mExpectedReadName);
296 
297         offset = 0;
298         SuccessOrQuit(Dns::Name::CompareName(*message, offset, name));
299         VerifyOrQuit(offset == len, "Name::CompareName() returned incorrect offset");
300 
301         StringConvertToUppercase(name);
302 
303         offset = 0;
304         SuccessOrQuit(Dns::Name::CompareName(*message, offset, name));
305 
306         offset = 0;
307         VerifyOrQuit(Dns::Name::CompareName(*message, offset, kBadName) == kErrorNotFound,
308                      "Name::CompareName() did not fail with incorrect name");
309         VerifyOrQuit(offset == len, "Name::CompareName() returned incorrect offset");
310 
311         // Remove the terminating '.' in expected name and verify
312         // that it can still be used by `CompareName()`.
313         offset = 0;
314         strcpy(name, test.mExpectedReadName);
315         name[strlen(name) - 1] = '\0';
316         SuccessOrQuit(Dns::Name::CompareName(*message, offset, name));
317         VerifyOrQuit(offset == len, "Name::CompareName() returned incorrect offset");
318 
319         if (strlen(name) >= 1)
320         {
321             name[strlen(name) - 1] = '\0';
322             offset                 = 0;
323             VerifyOrQuit(Dns::Name::CompareName(*message, offset, name) == kErrorNotFound,
324                          "Name::CompareName() did not fail with invalid name");
325             VerifyOrQuit(offset == len, "Name::CompareName() returned incorrect offset");
326         }
327 
328         // Compare the name with itself read from message.
329         offset = 0;
330         SuccessOrQuit(Dns::Name::CompareName(*message, offset, *message, offset));
331         VerifyOrQuit(offset == len, "Name::CompareName() returned incorrect offset");
332     }
333 
334     printf("----------------------------------------------------------------\n");
335     printf("Max length names:\n");
336 
337     for (const char *&maxLengthName : kMaxLengthNames)
338     {
339         if (maxLengthName[strlen(maxLengthName) - 1] == '.')
340         {
341             VerifyOrQuit(strlen(maxLengthName) == kMaxNameLength);
342         }
343         else
344         {
345             VerifyOrQuit(strlen(maxLengthName) == kMaxNameLength - 1);
346         }
347 
348         IgnoreError(message->SetLength(0));
349 
350         printf("\"%s\"\n", maxLengthName);
351 
352         SuccessOrQuit(Dns::Name::AppendName(maxLengthName, *message));
353     }
354 
355     printf("----------------------------------------------------------------\n");
356     printf("Invalid names:\n");
357 
358     for (const char *&invalidName : kInvalidNames)
359     {
360         IgnoreError(message->SetLength(0));
361 
362         printf("\"%s\"\n", invalidName);
363 
364         VerifyOrQuit(Dns::Name::AppendName(invalidName, *message) == kErrorInvalidArgs);
365     }
366 
367     printf("----------------------------------------------------------------\n");
368     printf("Append as multiple labels and terminator instead of full name:\n");
369 
370     for (const TestName &test : kTestNames)
371     {
372         IgnoreError(message->SetLength(0));
373 
374         SuccessOrQuit(Dns::Name::AppendMultipleLabels(test.mName, *message));
375         SuccessOrQuit(Dns::Name::AppendTerminator(*message));
376 
377         len = message->GetLength();
378         SuccessOrQuit(message->Read(0, buffer, len));
379 
380         DumpBuffer(test.mName, buffer, len);
381 
382         VerifyOrQuit(len == test.mEncodedLength, "Encoded length does not match expected value");
383         VerifyOrQuit(memcmp(buffer, test.mEncodedData, len) == 0, "Encoded name data does not match expected data");
384     }
385 
386     printf("----------------------------------------------------------------\n");
387     printf("Append labels one by one:\n");
388 
389     for (const TestName &test : kTestNames)
390     {
391         IgnoreError(message->SetLength(0));
392 
393         for (uint8_t index = 0; test.mLabels[index] != nullptr; index++)
394         {
395             SuccessOrQuit(Dns::Name::AppendLabel(test.mLabels[index], *message));
396         }
397 
398         SuccessOrQuit(Dns::Name::AppendTerminator(*message));
399 
400         len = message->GetLength();
401         SuccessOrQuit(message->Read(0, buffer, len));
402 
403         DumpBuffer(test.mName, buffer, len);
404 
405         VerifyOrQuit(len == test.mEncodedLength, "Encoded length does not match expected value");
406         VerifyOrQuit(memcmp(buffer, test.mEncodedData, len) == 0, "Encoded name data does not match expected data");
407     }
408 
409     message->Free();
410     testFreeInstance(instance);
411 }
412 
TestDnsCompressedName(void)413 void TestDnsCompressedName(void)
414 {
415     enum
416     {
417         kHeaderOffset   = 10,
418         kGuardBlockSize = 20,
419         kMaxBufferSize  = 100,
420         kLabelSize      = 64,
421         kNameSize       = 256,
422 
423         kName2EncodedSize = 4 + 2,  // encoded "FOO" + pointer label (2 bytes)
424         kName3EncodedSize = 2,      // pointer label (2 bytes)
425         kName4EncodedSize = 15 + 2, // encoded "Human.Readable" + pointer label (2 bytes).
426 
427     };
428 
429     const char kName[]          = "F.ISI.ARPA";
430     const char kLabel1[]        = "FOO";
431     const char kInstanceLabel[] = "Human.Readable";
432 
433     static const uint8_t kEncodedName[]    = {1, 'F', 3, 'I', 'S', 'I', 4, 'A', 'R', 'P', 'A', 0};
434     static const uint8_t kIsiRelativeIndex = 2; // Index in kEncodedName to the start of "ISI.ARPA" portion.
435 
436     static const char *kName1Labels[] = {"F", "ISI", "ARPA"};
437     static const char *kName2Labels[] = {"FOO", "F", "ISI", "ARPA"};
438     static const char *kName3Labels[] = {"ISI", "ARPA"};
439     static const char *kName4Labels[] = {"Human.Readable", "F", "ISI", "ARPA"};
440 
441     static const char kExpectedReadName1[] = "F.ISI.ARPA.";
442     static const char kExpectedReadName2[] = "FOO.F.ISI.ARPA.";
443     static const char kExpectedReadName3[] = "ISI.ARPA.";
444 
445     static const char kBadName[] = "bad.name";
446 
447     Instance *   instance;
448     MessagePool *messagePool;
449     Message *    message;
450     Message *    message2;
451     uint16_t     offset;
452     uint16_t     name1Offset;
453     uint16_t     name2Offset;
454     uint16_t     name3Offset;
455     uint16_t     name4Offset;
456     uint8_t      buffer[kMaxBufferSize];
457     char         label[kLabelSize];
458     uint8_t      labelLength;
459     char         name[kNameSize];
460     Dns::Name    dnsName1;
461     Dns::Name    dnsName2;
462     Dns::Name    dnsName3;
463     Dns::Name    dnsName4;
464 
465     printf("================================================================\n");
466     printf("TestDnsCompressedName()\n");
467 
468     instance = static_cast<Instance *>(testInitInstance());
469     VerifyOrQuit(instance != nullptr, "Null OpenThread instance");
470 
471     messagePool = &instance->Get<MessagePool>();
472     VerifyOrQuit((message = messagePool->Allocate(Message::kTypeIp6)) != nullptr);
473 
474     // Append name1 "F.ISI.ARPA"
475 
476     for (uint8_t index = 0; index < kHeaderOffset + kGuardBlockSize; index++)
477     {
478         SuccessOrQuit(message->Append(index));
479     }
480 
481     message->SetOffset(kHeaderOffset);
482 
483     name1Offset = message->GetLength();
484     SuccessOrQuit(Dns::Name::AppendName(kName, *message));
485 
486     // Append name2 "FOO.F.ISI.ARPA" as a compressed name after some guard/extra bytes
487 
488     for (uint8_t index = 0; index < kGuardBlockSize; index++)
489     {
490         uint8_t value = 0xff;
491         SuccessOrQuit(message->Append(value));
492     }
493 
494     name2Offset = message->GetLength();
495 
496     SuccessOrQuit(Dns::Name::AppendLabel(kLabel1, *message));
497     SuccessOrQuit(Dns::Name::AppendPointerLabel(name1Offset - kHeaderOffset, *message));
498 
499     // Append name3 "ISI.ARPA" as a compressed name after some guard/extra bytes
500 
501     for (uint8_t index = 0; index < kGuardBlockSize; index++)
502     {
503         uint8_t value = 0xaa;
504         SuccessOrQuit(message->Append(value));
505     }
506 
507     name3Offset = message->GetLength();
508     SuccessOrQuit(Dns::Name::AppendPointerLabel(name1Offset + kIsiRelativeIndex - kHeaderOffset, *message));
509 
510     name4Offset = message->GetLength();
511     SuccessOrQuit(Dns::Name::AppendLabel(kInstanceLabel, *message));
512     SuccessOrQuit(Dns::Name::AppendPointerLabel(name1Offset - kHeaderOffset, *message));
513 
514     printf("----------------------------------------------------------------\n");
515     printf("Read and parse the uncompressed name-1 \"F.ISI.ARPA\"\n");
516 
517     SuccessOrQuit(message->Read(name1Offset, buffer, sizeof(kEncodedName)));
518 
519     DumpBuffer(kName, buffer, sizeof(kEncodedName));
520     VerifyOrQuit(memcmp(buffer, kEncodedName, sizeof(kEncodedName)) == 0,
521                  "Encoded name data does not match expected data");
522 
523     offset = name1Offset;
524     SuccessOrQuit(Dns::Name::ParseName(*message, offset));
525 
526     VerifyOrQuit(offset == name1Offset + sizeof(kEncodedName), "Name::ParseName() returned incorrect offset");
527 
528     offset = name1Offset;
529 
530     for (const char *nameLabel : kName1Labels)
531     {
532         labelLength = sizeof(label);
533         SuccessOrQuit(Dns::Name::ReadLabel(*message, offset, label, labelLength));
534 
535         printf("label: \"%s\"\n", label);
536         VerifyOrQuit(strcmp(label, nameLabel) == 0, "Name::ReadLabel() did not get expected label");
537         VerifyOrQuit(labelLength == strlen(label), "Name::ReadLabel() returned incorrect label length");
538     }
539 
540     labelLength = sizeof(label);
541     VerifyOrQuit(Dns::Name::ReadLabel(*message, offset, label, labelLength) == kErrorNotFound,
542                  "Name::ReadLabel() failed at end of the name");
543 
544     offset = name1Offset;
545     SuccessOrQuit(Dns::Name::ReadName(*message, offset, name, sizeof(name)));
546     printf("Read name =\"%s\"\n", name);
547     VerifyOrQuit(strcmp(name, kExpectedReadName1) == 0, "Name::ReadName() did not return expected name");
548     VerifyOrQuit(offset == name1Offset + sizeof(kEncodedName), "Name::ReadName() returned incorrect offset");
549 
550     offset = name1Offset;
551 
552     for (const char *nameLabel : kName1Labels)
553     {
554         SuccessOrQuit(Dns::Name::CompareLabel(*message, offset, nameLabel));
555     }
556 
557     offset = name1Offset;
558     SuccessOrQuit(Dns::Name::CompareName(*message, offset, kExpectedReadName1));
559     VerifyOrQuit(offset == name1Offset + sizeof(kEncodedName), "Name::CompareName() returned incorrect offset");
560 
561     offset = name1Offset;
562     VerifyOrQuit(Dns::Name::CompareName(*message, offset, kBadName) == kErrorNotFound,
563                  "Name::CompareName() did not fail with incorrect name");
564     VerifyOrQuit(offset == name1Offset + sizeof(kEncodedName), "Name::CompareName() returned incorrect offset");
565 
566     offset = name1Offset;
567     SuccessOrQuit(Dns::Name::CompareName(*message, offset, *message, offset));
568     VerifyOrQuit(offset == name1Offset + sizeof(kEncodedName), "Name::CompareName() returned incorrect offset");
569 
570     offset = name1Offset;
571     VerifyOrQuit(Dns::Name::CompareName(*message, offset, *message, name2Offset) == kErrorNotFound,
572                  "Name::CompareName() did not fail with mismatching name");
573     VerifyOrQuit(offset == name1Offset + sizeof(kEncodedName), "Name::CompareName() returned incorrect offset");
574 
575     printf("----------------------------------------------------------------\n");
576     printf("Read and parse compressed name-2 \"FOO.F.ISI.ARPA\"\n");
577 
578     SuccessOrQuit(message->Read(name2Offset, buffer, kName2EncodedSize));
579     DumpBuffer("name2(compressed)", buffer, kName2EncodedSize);
580 
581     offset = name2Offset;
582     SuccessOrQuit(Dns::Name::ParseName(*message, offset));
583     VerifyOrQuit(offset == name2Offset + kName2EncodedSize, "Name::ParseName() returned incorrect offset");
584 
585     offset = name2Offset;
586 
587     for (const char *nameLabel : kName2Labels)
588     {
589         labelLength = sizeof(label);
590         SuccessOrQuit(Dns::Name::ReadLabel(*message, offset, label, labelLength));
591 
592         printf("label: \"%s\"\n", label);
593         VerifyOrQuit(strcmp(label, nameLabel) == 0, "Name::ReadLabel() did not get expected label");
594         VerifyOrQuit(labelLength == strlen(label), "Name::ReadLabel() returned incorrect label length");
595     }
596 
597     labelLength = sizeof(label);
598     VerifyOrQuit(Dns::Name::ReadLabel(*message, offset, label, labelLength) == kErrorNotFound,
599                  "Name::ReadLabel() failed at end of the name");
600 
601     offset = name2Offset;
602     SuccessOrQuit(Dns::Name::ReadName(*message, offset, name, sizeof(name)));
603     printf("Read name =\"%s\"\n", name);
604     VerifyOrQuit(strcmp(name, kExpectedReadName2) == 0, "Name::ReadName() did not return expected name");
605     VerifyOrQuit(offset == name2Offset + kName2EncodedSize, "Name::ReadName() returned incorrect offset");
606 
607     offset = name2Offset;
608 
609     for (const char *nameLabel : kName2Labels)
610     {
611         SuccessOrQuit(Dns::Name::CompareLabel(*message, offset, nameLabel));
612     }
613 
614     offset = name2Offset;
615     SuccessOrQuit(Dns::Name::CompareName(*message, offset, kExpectedReadName2));
616     VerifyOrQuit(offset == name2Offset + kName2EncodedSize, "Name::CompareName() returned incorrect offset");
617 
618     offset = name2Offset;
619     VerifyOrQuit(Dns::Name::CompareName(*message, offset, kBadName) == kErrorNotFound,
620                  "Name::CompareName() did not fail with incorrect name");
621     VerifyOrQuit(offset == name2Offset + kName2EncodedSize, "Name::CompareName() returned incorrect offset");
622 
623     offset = name2Offset;
624     SuccessOrQuit(Dns::Name::CompareName(*message, offset, *message, offset), "Name::CompareName() with itself failed");
625     VerifyOrQuit(offset == name2Offset + kName2EncodedSize, "Name::CompareName() returned incorrect offset");
626 
627     offset = name2Offset;
628     VerifyOrQuit(Dns::Name::CompareName(*message, offset, *message, name3Offset) == kErrorNotFound,
629                  "Name::CompareName() did not fail with mismatching name");
630     VerifyOrQuit(offset == name2Offset + kName2EncodedSize, "Name::CompareName() returned incorrect offset");
631 
632     printf("----------------------------------------------------------------\n");
633     printf("Read and parse compressed name-3 \"ISI.ARPA\"\n");
634 
635     SuccessOrQuit(message->Read(name3Offset, buffer, kName3EncodedSize));
636     DumpBuffer("name2(compressed)", buffer, kName3EncodedSize);
637 
638     offset = name3Offset;
639     SuccessOrQuit(Dns::Name::ParseName(*message, offset));
640     VerifyOrQuit(offset == name3Offset + kName3EncodedSize, "Name::ParseName() returned incorrect offset");
641 
642     offset = name3Offset;
643 
644     for (const char *nameLabel : kName3Labels)
645     {
646         labelLength = sizeof(label);
647         SuccessOrQuit(Dns::Name::ReadLabel(*message, offset, label, labelLength));
648 
649         printf("label: \"%s\"\n", label);
650         VerifyOrQuit(strcmp(label, nameLabel) == 0, "Name::ReadLabel() did not get expected label");
651         VerifyOrQuit(labelLength == strlen(label), "Name::ReadLabel() returned incorrect label length");
652     }
653 
654     labelLength = sizeof(label);
655     VerifyOrQuit(Dns::Name::ReadLabel(*message, offset, label, labelLength) == kErrorNotFound,
656                  "Name::ReadLabel() failed at end of the name");
657 
658     offset = name3Offset;
659     SuccessOrQuit(Dns::Name::ReadName(*message, offset, name, sizeof(name)));
660     printf("Read name =\"%s\"\n", name);
661     VerifyOrQuit(strcmp(name, kExpectedReadName3) == 0, "Name::ReadName() did not return expected name");
662     VerifyOrQuit(offset == name3Offset + kName3EncodedSize, "Name::ReadName() returned incorrect offset");
663 
664     offset = name3Offset;
665 
666     for (const char *nameLabel : kName3Labels)
667     {
668         SuccessOrQuit(Dns::Name::CompareLabel(*message, offset, nameLabel));
669     }
670 
671     offset = name3Offset;
672     SuccessOrQuit(Dns::Name::CompareName(*message, offset, kExpectedReadName3));
673     VerifyOrQuit(offset == name3Offset + kName3EncodedSize, "Name::CompareName() returned incorrect offset");
674 
675     offset = name3Offset;
676     VerifyOrQuit(Dns::Name::CompareName(*message, offset, kBadName) == kErrorNotFound,
677                  "Name::CompareName() did not fail with incorrect name");
678     VerifyOrQuit(offset == name3Offset + kName3EncodedSize, "Name::CompareName() returned incorrect offset");
679 
680     offset = name3Offset;
681     SuccessOrQuit(Dns::Name::CompareName(*message, offset, *message, offset), "Name::CompareName() with itself failed");
682     VerifyOrQuit(offset == name3Offset + kName3EncodedSize, "Name::CompareName() returned incorrect offset");
683 
684     offset = name3Offset;
685     VerifyOrQuit(Dns::Name::CompareName(*message, offset, *message, name4Offset) == kErrorNotFound,
686                  "Name::CompareName() did not fail with mismatching name");
687     VerifyOrQuit(offset == name3Offset + kName3EncodedSize, "Name::CompareName() returned incorrect offset");
688 
689     printf("----------------------------------------------------------------\n");
690     printf("Read and parse the uncompressed name-4 \"Human\\.Readable.F.ISI.ARPA\"\n");
691 
692     SuccessOrQuit(message->Read(name4Offset, buffer, kName4EncodedSize));
693     DumpBuffer("name4(compressed)", buffer, kName4EncodedSize);
694 
695     offset = name4Offset;
696     SuccessOrQuit(Dns::Name::ParseName(*message, offset));
697     VerifyOrQuit(offset == name4Offset + kName4EncodedSize, "Name::ParseName() returned incorrect offset");
698 
699     offset = name4Offset;
700 
701     for (const char *nameLabel : kName4Labels)
702     {
703         labelLength = sizeof(label);
704         SuccessOrQuit(Dns::Name::ReadLabel(*message, offset, label, labelLength));
705 
706         printf("label: \"%s\"\n", label);
707         VerifyOrQuit(strcmp(label, nameLabel) == 0, "Name::ReadLabel() did not get expected label");
708         VerifyOrQuit(labelLength == strlen(label), "Name::ReadLabel() returned incorrect label length");
709     }
710 
711     // `ReadName()` for name-4 should fails due to first label containing dot char.
712     offset = name4Offset;
713     VerifyOrQuit(Dns::Name::ReadName(*message, offset, name, sizeof(name)) == kErrorParse,
714                  "Name::ReadName() did not fail with invalid label");
715 
716     offset = name4Offset;
717 
718     for (const char *nameLabel : kName4Labels)
719     {
720         SuccessOrQuit(Dns::Name::CompareLabel(*message, offset, nameLabel));
721     }
722 
723     offset = name4Offset;
724     SuccessOrQuit(Dns::Name::CompareName(*message, offset, *message, offset), "Name::CompareName() with itself failed");
725 
726     offset = name4Offset;
727     VerifyOrQuit(Dns::Name::CompareName(*message, offset, *message, name1Offset) == kErrorNotFound,
728                  "Name::CompareName() did not fail with mismatching name");
729 
730     printf("----------------------------------------------------------------\n");
731     printf("Append names from one message to another\n");
732 
733     VerifyOrQuit((message2 = messagePool->Allocate(Message::kTypeIp6)) != nullptr);
734 
735     dnsName1.SetFromMessage(*message, name1Offset);
736     dnsName2.SetFromMessage(*message, name2Offset);
737     dnsName3.SetFromMessage(*message, name3Offset);
738     dnsName4.SetFromMessage(*message, name4Offset);
739 
740     offset = 0;
741     SuccessOrQuit(dnsName1.AppendTo(*message2));
742     SuccessOrQuit(dnsName2.AppendTo(*message2));
743     SuccessOrQuit(dnsName3.AppendTo(*message2));
744     SuccessOrQuit(dnsName4.AppendTo(*message2));
745 
746     SuccessOrQuit(message2->Read(0, buffer, message2->GetLength()));
747     DumpBuffer("message2", buffer, message2->GetLength());
748 
749     // Now compare the names one by one in `message2`. Note that
750     // `CompareName()` will update `offset` on success.
751 
752     SuccessOrQuit(Dns::Name::CompareName(*message2, offset, dnsName1));
753     SuccessOrQuit(Dns::Name::CompareName(*message2, offset, dnsName2));
754     SuccessOrQuit(Dns::Name::CompareName(*message2, offset, dnsName3));
755     SuccessOrQuit(Dns::Name::CompareName(*message2, offset, dnsName4));
756 
757     offset = 0;
758     SuccessOrQuit(Dns::Name::ReadName(*message2, offset, name, sizeof(name)));
759     printf("- Name1 after `AppendTo()`: \"%s\"\n", name);
760     SuccessOrQuit(Dns::Name::ReadName(*message2, offset, name, sizeof(name)));
761     printf("- Name2 after `AppendTo()`: \"%s\"\n", name);
762     SuccessOrQuit(Dns::Name::ReadName(*message2, offset, name, sizeof(name)));
763     printf("- Name3 after `AppendTo()`: \"%s\"\n", name);
764     // `ReadName()` for name-4 will fail due to first label containing dot char.
765 
766     message->Free();
767     message2->Free();
768     testFreeInstance(instance);
769 }
770 
TestHeaderAndResourceRecords(void)771 void TestHeaderAndResourceRecords(void)
772 {
773     enum
774     {
775         kHeaderOffset    = 0,
776         kQuestionCount   = 1,
777         kAnswerCount     = 2,
778         kAdditionalCount = 5,
779         kTtl             = 7200,
780         kTxtTtl          = 7300,
781         kSrvPort         = 1234,
782         kSrvPriority     = 1,
783         kSrvWeight       = 2,
784         kMaxSize         = 600,
785     };
786 
787     const char    kMessageString[]  = "DnsMessage";
788     const char    kDomainName[]     = "example.com.";
789     const char    kServiceLabels[]  = "_service._udp";
790     const char    kServiceName[]    = "_service._udp.example.com.";
791     const char    kInstance1Label[] = "inst1";
792     const char    kInstance2Label[] = "instance2";
793     const char    kInstance1Name[]  = "inst1._service._udp.example.com.";
794     const char    kInstance2Name[]  = "instance2._service._udp.example.com.";
795     const char    kHostName[]       = "host.example.com.";
796     const uint8_t kTxtData[]        = {9, 'k', 'e', 'y', '=', 'v', 'a', 'l', 'u', 'e', 0};
797     const char    kHostAddress[]    = "fd00::abcd:";
798 
799     const char *kInstanceLabels[] = {kInstance1Label, kInstance2Label};
800     const char *kInstanceNames[]  = {kInstance1Name, kInstance2Name};
801 
802     Instance *          instance;
803     MessagePool *       messagePool;
804     Message *           message;
805     Dns::Header         header;
806     uint16_t            messageId;
807     uint16_t            headerOffset;
808     uint16_t            offset;
809     uint16_t            numRecords;
810     uint16_t            len;
811     uint16_t            serviceNameOffset;
812     uint16_t            hostNameOffset;
813     uint16_t            answerSectionOffset;
814     uint16_t            additionalSectionOffset;
815     uint16_t            index;
816     Dns::PtrRecord      ptrRecord;
817     Dns::SrvRecord      srvRecord;
818     Dns::TxtRecord      txtRecord;
819     Dns::AaaaRecord     aaaaRecord;
820     Dns::ResourceRecord record;
821     Ip6::Address        hostAddress;
822 
823     char    label[Dns::Name::kMaxLabelSize];
824     char    name[Dns::Name::kMaxNameSize];
825     uint8_t buffer[kMaxSize];
826 
827     printf("================================================================\n");
828     printf("TestHeaderAndResourceRecords()\n");
829 
830     instance = static_cast<Instance *>(testInitInstance());
831     VerifyOrQuit(instance != nullptr, "Null OpenThread instance");
832 
833     messagePool = &instance->Get<MessagePool>();
834     VerifyOrQuit((message = messagePool->Allocate(Message::kTypeIp6)) != nullptr);
835 
836     printf("----------------------------------------------------------------\n");
837     printf("Preparing the message\n");
838 
839     SuccessOrQuit(message->Append(kMessageString));
840 
841     // Header
842 
843     headerOffset = message->GetLength();
844     SuccessOrQuit(header.SetRandomMessageId());
845     messageId = header.GetMessageId();
846     header.SetType(Dns::Header::kTypeResponse);
847     header.SetQuestionCount(kQuestionCount);
848     header.SetAnswerCount(kAnswerCount);
849     header.SetAdditionalRecordCount(kAdditionalCount);
850     SuccessOrQuit(message->Append(header));
851     message->SetOffset(headerOffset);
852 
853     // Question section
854 
855     serviceNameOffset = message->GetLength() - headerOffset;
856     SuccessOrQuit(Dns::Name::AppendMultipleLabels(kServiceLabels, *message));
857     SuccessOrQuit(Dns::Name::AppendName(kDomainName, *message));
858     SuccessOrQuit(message->Append(Dns::Question(Dns::ResourceRecord::kTypePtr)));
859 
860     // Answer section
861 
862     answerSectionOffset = message->GetLength();
863 
864     for (const char *instanceLabel : kInstanceLabels)
865     {
866         SuccessOrQuit(Dns::Name::AppendPointerLabel(serviceNameOffset, *message));
867         ptrRecord.Init();
868         ptrRecord.SetTtl(kTtl);
869         offset = message->GetLength();
870         SuccessOrQuit(message->Append(ptrRecord));
871         SuccessOrQuit(Dns::Name::AppendLabel(instanceLabel, *message));
872         SuccessOrQuit(Dns::Name::AppendPointerLabel(serviceNameOffset, *message));
873         ptrRecord.SetLength(message->GetLength() - offset - sizeof(Dns::ResourceRecord));
874         message->Write(offset, ptrRecord);
875     }
876 
877     // Additional section
878 
879     additionalSectionOffset = message->GetLength();
880 
881     for (const char *instanceName : kInstanceNames)
882     {
883         uint16_t instanceNameOffset = message->GetLength() - headerOffset;
884 
885         // SRV record
886         SuccessOrQuit(Dns::Name::AppendName(instanceName, *message));
887         srvRecord.Init();
888         srvRecord.SetTtl(kTtl);
889         srvRecord.SetPort(kSrvPort);
890         srvRecord.SetWeight(kSrvWeight);
891         srvRecord.SetPriority(kSrvPriority);
892         offset = message->GetLength();
893         SuccessOrQuit(message->Append(srvRecord));
894         hostNameOffset = message->GetLength() - headerOffset;
895         SuccessOrQuit(Dns::Name::AppendName(kHostName, *message));
896         srvRecord.SetLength(message->GetLength() - offset - sizeof(Dns::ResourceRecord));
897         message->Write(offset, srvRecord);
898 
899         // TXT record
900         SuccessOrQuit(Dns::Name::AppendPointerLabel(instanceNameOffset, *message));
901         txtRecord.Init();
902         txtRecord.SetTtl(kTxtTtl);
903         txtRecord.SetLength(sizeof(kTxtData));
904         SuccessOrQuit(message->Append(txtRecord));
905         SuccessOrQuit(message->Append(kTxtData));
906     }
907 
908     SuccessOrQuit(hostAddress.FromString(kHostAddress));
909     SuccessOrQuit(Dns::Name::AppendPointerLabel(hostNameOffset, *message));
910     aaaaRecord.Init();
911     aaaaRecord.SetTtl(kTtl);
912     aaaaRecord.SetAddress(hostAddress);
913     SuccessOrQuit(message->Append(aaaaRecord));
914 
915     // Dump the entire message
916 
917     VerifyOrQuit(message->GetLength() < kMaxSize, "Message is too long");
918     SuccessOrQuit(message->Read(0, buffer, message->GetLength()));
919     DumpBuffer("message", buffer, message->GetLength());
920 
921     printf("----------------------------------------------------------------\n");
922     printf("Parse and verify the message\n");
923 
924     offset = 0;
925     VerifyOrQuit(message->Compare(offset, kMessageString), "Message header does not match");
926     offset += sizeof(kMessageString);
927 
928     // Header
929 
930     VerifyOrQuit(offset == headerOffset, "headerOffset is incorrect");
931     SuccessOrQuit(message->Read(offset, header));
932     offset += sizeof(header);
933 
934     VerifyOrQuit(header.GetMessageId() == messageId);
935     VerifyOrQuit(header.GetType() == Dns::Header::kTypeResponse);
936     VerifyOrQuit(header.GetQuestionCount() == kQuestionCount);
937     VerifyOrQuit(header.GetAnswerCount() == kAnswerCount);
938     VerifyOrQuit(header.GetAdditionalRecordCount() == kAdditionalCount);
939 
940     printf("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \n");
941     printf("Question Section\n");
942 
943     SuccessOrQuit(Dns::Name::CompareName(*message, offset, kServiceName), "Question name does not match");
944     VerifyOrQuit(message->Compare(offset, Dns::Question(Dns::ResourceRecord::kTypePtr)));
945     offset += sizeof(Dns::Question);
946 
947     printf("PTR for \"%s\"\n", kServiceName);
948 
949     printf("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \n");
950     printf("Answer Section\n");
951 
952     VerifyOrQuit(offset == answerSectionOffset, "answer section offset is incorrect");
953 
954     for (const char *instanceName : kInstanceNames)
955     {
956         SuccessOrQuit(Dns::Name::CompareName(*message, offset, kServiceName));
957         SuccessOrQuit(Dns::ResourceRecord::ReadRecord(*message, offset, ptrRecord));
958         VerifyOrQuit(ptrRecord.GetTtl() == kTtl, "Read PTR is incorrect");
959 
960         SuccessOrQuit(ptrRecord.ReadPtrName(*message, offset, name, sizeof(name)));
961         VerifyOrQuit(strcmp(name, instanceName) == 0, "Inst1 name is incorrect");
962 
963         printf("    \"%s\" PTR %u %d \"%s\"\n", kServiceName, ptrRecord.GetTtl(), ptrRecord.GetLength(), name);
964     }
965 
966     VerifyOrQuit(offset == additionalSectionOffset, "offset is incorrect after answer section parse");
967 
968     offset = answerSectionOffset;
969     SuccessOrQuit(Dns::ResourceRecord::ParseRecords(*message, offset, kAnswerCount));
970     VerifyOrQuit(offset == additionalSectionOffset, "offset is incorrect after answer section parse");
971 
972     printf("Use FindRecord() to find and iterate through all the records:\n");
973 
974     offset     = answerSectionOffset;
975     numRecords = kAnswerCount;
976 
977     while (numRecords > 0)
978     {
979         uint16_t prevNumRecords = numRecords;
980 
981         SuccessOrQuit(Dns::ResourceRecord::FindRecord(*message, offset, numRecords, Dns::Name(kServiceName)));
982         VerifyOrQuit(numRecords == prevNumRecords - 1, "Incorrect num records");
983         SuccessOrQuit(Dns::ResourceRecord::ReadRecord(*message, offset, ptrRecord));
984         VerifyOrQuit(ptrRecord.GetTtl() == kTtl, "Read PTR is incorrect");
985         SuccessOrQuit(ptrRecord.ReadPtrName(*message, offset, label, sizeof(label), name, sizeof(name)));
986         printf("    \"%s\" PTR %u %d inst:\"%s\" at \"%s\"\n", kServiceName, ptrRecord.GetTtl(), ptrRecord.GetLength(),
987                label, name);
988     }
989 
990     VerifyOrQuit(offset == additionalSectionOffset, "offset is incorrect after answer section parse");
991     VerifyOrQuit(Dns::ResourceRecord::FindRecord(*message, offset, numRecords, Dns::Name(kServiceName)) ==
992                      kErrorNotFound,
993                  "FindRecord did not fail with no records");
994 
995     // Use `ReadRecord()` with a non-matching record type. Verify that it correct skips over the record.
996 
997     offset     = answerSectionOffset;
998     numRecords = kAnswerCount;
999 
1000     while (numRecords > 0)
1001     {
1002         SuccessOrQuit(Dns::ResourceRecord::FindRecord(*message, offset, numRecords, Dns::Name(kServiceName)));
1003         VerifyOrQuit(Dns::ResourceRecord::ReadRecord(*message, offset, srvRecord) == kErrorNotFound,
1004                      "ReadRecord() did not fail with non-matching type");
1005     }
1006 
1007     VerifyOrQuit(offset == additionalSectionOffset, "offset is incorrect after answer section parse");
1008 
1009     // Use `FindRecord` with a non-matching name. Verify that it correctly skips over all records.
1010 
1011     offset     = answerSectionOffset;
1012     numRecords = kAnswerCount;
1013     VerifyOrQuit(Dns::ResourceRecord::FindRecord(*message, offset, numRecords, Dns::Name(kInstance1Name)) ==
1014                      kErrorNotFound,
1015                  "FindRecord did not fail with non-matching name");
1016     VerifyOrQuit(numRecords == 0, "Incorrect num records");
1017     VerifyOrQuit(offset == additionalSectionOffset, "offset is incorrect after answer section parse");
1018 
1019     printf("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \n");
1020     printf("Additional Section\n");
1021 
1022     for (const char *instanceName : kInstanceNames)
1023     {
1024         // SRV record
1025         SuccessOrQuit(Dns::Name::CompareName(*message, offset, instanceName));
1026         SuccessOrQuit(Dns::ResourceRecord::ReadRecord(*message, offset, srvRecord));
1027         VerifyOrQuit(srvRecord.GetTtl() == kTtl);
1028         VerifyOrQuit(srvRecord.GetPort() == kSrvPort);
1029         VerifyOrQuit(srvRecord.GetWeight() == kSrvWeight);
1030         VerifyOrQuit(srvRecord.GetPriority() == kSrvPriority);
1031         SuccessOrQuit(srvRecord.ReadTargetHostName(*message, offset, name, sizeof(name)));
1032         VerifyOrQuit(strcmp(name, kHostName) == 0);
1033         printf("    \"%s\" SRV %u %d %d %d %d \"%s\"\n", instanceName, srvRecord.GetTtl(), srvRecord.GetLength(),
1034                srvRecord.GetPort(), srvRecord.GetWeight(), srvRecord.GetPriority(), name);
1035 
1036         // TXT record
1037         SuccessOrQuit(Dns::Name::CompareName(*message, offset, instanceName));
1038         SuccessOrQuit(Dns::ResourceRecord::ReadRecord(*message, offset, txtRecord));
1039         VerifyOrQuit(txtRecord.GetTtl() == kTxtTtl);
1040         len = sizeof(buffer);
1041         SuccessOrQuit(txtRecord.ReadTxtData(*message, offset, buffer, len));
1042         VerifyOrQuit(len == sizeof(kTxtData));
1043         VerifyOrQuit(memcmp(buffer, kTxtData, len) == 0);
1044         printf("    \"%s\" TXT %u %d \"%s\"\n", instanceName, txtRecord.GetTtl(), txtRecord.GetLength(),
1045                reinterpret_cast<const char *>(buffer));
1046     }
1047 
1048     SuccessOrQuit(Dns::Name::CompareName(*message, offset, kHostName));
1049     SuccessOrQuit(Dns::ResourceRecord::ReadRecord(*message, offset, aaaaRecord));
1050     VerifyOrQuit(aaaaRecord.GetTtl() == kTtl);
1051     VerifyOrQuit(aaaaRecord.GetAddress() == hostAddress);
1052     printf("    \"%s\" AAAA %u %d \"%s\"\n", kHostName, aaaaRecord.GetTtl(), aaaaRecord.GetLength(),
1053            aaaaRecord.GetAddress().ToString().AsCString());
1054 
1055     VerifyOrQuit(offset == message->GetLength(), "offset is incorrect after additional section parse");
1056 
1057     // Use `ParseRecords()` to parse all records
1058     offset = additionalSectionOffset;
1059     SuccessOrQuit(Dns::ResourceRecord::ParseRecords(*message, offset, kAdditionalCount));
1060     VerifyOrQuit(offset == message->GetLength(), "offset is incorrect after additional section parse");
1061 
1062     printf("Use FindRecord() to search for specific name:\n");
1063 
1064     for (const char *instanceName : kInstanceNames)
1065     {
1066         offset     = additionalSectionOffset;
1067         numRecords = kAdditionalCount;
1068 
1069         SuccessOrQuit(Dns::ResourceRecord::FindRecord(*message, offset, numRecords, Dns::Name(instanceName)));
1070         SuccessOrQuit(Dns::ResourceRecord::ReadRecord(*message, offset, srvRecord));
1071         SuccessOrQuit(Dns::Name::ParseName(*message, offset));
1072         printf("    \"%s\" SRV %u %d %d %d %d\n", instanceName, srvRecord.GetTtl(), srvRecord.GetLength(),
1073                srvRecord.GetPort(), srvRecord.GetWeight(), srvRecord.GetPriority());
1074 
1075         SuccessOrQuit(Dns::ResourceRecord::FindRecord(*message, offset, numRecords, Dns::Name(instanceName)));
1076         SuccessOrQuit(Dns::ResourceRecord::ReadRecord(*message, offset, txtRecord));
1077         offset += txtRecord.GetLength();
1078         printf("    \"%s\" TXT %u %d\n", instanceName, txtRecord.GetTtl(), txtRecord.GetLength());
1079 
1080         VerifyOrQuit(Dns::ResourceRecord::FindRecord(*message, offset, numRecords, Dns::Name(instanceName)) ==
1081                          kErrorNotFound,
1082                      "FindRecord() did not fail with no more records");
1083 
1084         VerifyOrQuit(offset == message->GetLength(), "offset is incorrect after additional section parse");
1085     }
1086 
1087     offset     = additionalSectionOffset;
1088     numRecords = kAdditionalCount;
1089     SuccessOrQuit(Dns::ResourceRecord::FindRecord(*message, offset, numRecords, Dns::Name(kHostName)));
1090 
1091     SuccessOrQuit(Dns::ResourceRecord::ReadRecord(*message, offset, record));
1092     VerifyOrQuit(record.GetType() == Dns::ResourceRecord::kTypeAaaa);
1093     offset += record.GetLength();
1094     VerifyOrQuit(offset == message->GetLength(), "offset is incorrect after additional section parse");
1095 
1096     printf("Use FindRecord() to search for specific records:\n");
1097     printf(" Answer Section\n");
1098 
1099     for (index = 0; index < GetArrayLength(kInstanceNames); index++)
1100     {
1101         offset = answerSectionOffset;
1102         SuccessOrQuit(
1103             Dns::ResourceRecord::FindRecord(*message, offset, kAnswerCount, index, Dns::Name(kServiceName), ptrRecord));
1104 
1105         printf("   index:%d -> \"%s\" PTR %u %d\n", index, kServiceName, ptrRecord.GetTtl(), ptrRecord.GetLength());
1106     }
1107 
1108     // Check `FindRecord()` failure with non-matching name, record type, or bad index.
1109 
1110     offset = answerSectionOffset;
1111     VerifyOrQuit(Dns::ResourceRecord::FindRecord(*message, offset, kAnswerCount, index, Dns::Name(kServiceName),
1112                                                  ptrRecord) == kErrorNotFound,
1113                  "FindRecord() did not fail with bad index");
1114     VerifyOrQuit(offset == answerSectionOffset, "FindRecord() changed offset on failure");
1115 
1116     offset = answerSectionOffset;
1117     VerifyOrQuit(Dns::ResourceRecord::FindRecord(*message, offset, kAnswerCount, index, Dns::Name(kInstance1Name),
1118                                                  ptrRecord) == kErrorNotFound,
1119                  "FindRecord() did not fail with bad index");
1120     VerifyOrQuit(offset == answerSectionOffset, "FindRecord() changed offset on failure");
1121 
1122     offset = answerSectionOffset;
1123     VerifyOrQuit(Dns::ResourceRecord::FindRecord(*message, offset, kAnswerCount, index, Dns::Name(kServiceName),
1124                                                  txtRecord) == kErrorNotFound,
1125                  "FindRecord() did not fail with bad index");
1126     VerifyOrQuit(offset == answerSectionOffset, "FindRecord() changed offset on failure");
1127 
1128     printf(" Additional Section\n");
1129 
1130     for (const char *instanceName : kInstanceNames)
1131     {
1132         // There is a single SRV and TXT entry for each instance
1133         offset = additionalSectionOffset;
1134         SuccessOrQuit(Dns::ResourceRecord::FindRecord(*message, offset, kAdditionalCount, /* aIndex */ 0,
1135                                                       Dns::Name(instanceName), srvRecord));
1136         printf("    \"%s\" SRV %u %d %d %d %d \n", instanceName, srvRecord.GetTtl(), srvRecord.GetLength(),
1137                srvRecord.GetPort(), srvRecord.GetWeight(), srvRecord.GetPriority());
1138 
1139         offset = additionalSectionOffset;
1140         SuccessOrQuit(Dns::ResourceRecord::FindRecord(*message, offset, kAdditionalCount, /* aIndex */ 0,
1141                                                       Dns::Name(instanceName), txtRecord));
1142         printf("    \"%s\" TXT %u %d\n", instanceName, txtRecord.GetTtl(), txtRecord.GetLength());
1143 
1144         offset = additionalSectionOffset;
1145         VerifyOrQuit(Dns::ResourceRecord::FindRecord(*message, offset, kAdditionalCount, /* aIndex */ 1,
1146                                                      Dns::Name(instanceName), srvRecord) == kErrorNotFound);
1147 
1148         offset = additionalSectionOffset;
1149         VerifyOrQuit(Dns::ResourceRecord::FindRecord(*message, offset, kAdditionalCount, /* aIndex */ 1,
1150                                                      Dns::Name(instanceName), txtRecord) == kErrorNotFound);
1151     }
1152 
1153     for (index = 0; index < kAdditionalCount; index++)
1154     {
1155         offset = additionalSectionOffset;
1156         // Find record with empty name (matching any) and any type.
1157         SuccessOrQuit(Dns::ResourceRecord::FindRecord(*message, offset, kAdditionalCount, index, Dns::Name(), record));
1158     }
1159 
1160     offset = additionalSectionOffset;
1161     VerifyOrQuit(Dns::ResourceRecord::FindRecord(*message, offset, kAdditionalCount, index, Dns::Name(), record) ==
1162                  kErrorNotFound);
1163 
1164     message->Free();
1165     testFreeInstance(instance);
1166 }
1167 
TestDnsTxtEntry(void)1168 void TestDnsTxtEntry(void)
1169 {
1170     enum
1171     {
1172         kMaxTxtDataSize = 255,
1173     };
1174 
1175     struct EncodedTxtData
1176     {
1177         const uint8_t *mData;
1178         uint8_t        mLength;
1179     };
1180 
1181     const char    kKey1[]   = "key";
1182     const uint8_t kValue1[] = {'v', 'a', 'l', 'u', 'e'};
1183 
1184     const char    kKey2[]   = "E";
1185     const uint8_t kValue2[] = {'m', 'c', '^', '2'};
1186 
1187     const char    kKey3[]   = "space key";
1188     const uint8_t kValue3[] = {'=', 0, '='};
1189 
1190     const char    kKey4[]   = "123456789"; // Max recommended length key
1191     const uint8_t kValue4[] = {0};
1192 
1193     const char    kKey5[]   = "1234567890"; // Longer than recommended key
1194     const uint8_t kValue5[] = {'a'};
1195 
1196     const char kKey6[] = "boolKey";  // Should be encoded as "boolKey" (without `=`).
1197     const char kKey7[] = "emptyKey"; // Should be encoded as "emptyKey=".
1198 
1199     // Invalid key
1200     const char kShortKey[] = "";
1201 
1202     const uint8_t kEncodedTxt1[] = {9, 'k', 'e', 'y', '=', 'v', 'a', 'l', 'u', 'e'};
1203     const uint8_t kEncodedTxt2[] = {6, 'E', '=', 'm', 'c', '^', '2'};
1204     const uint8_t kEncodedTxt3[] = {13, 's', 'p', 'a', 'c', 'e', ' ', 'k', 'e', 'y', '=', '=', 0, '='};
1205     const uint8_t kEncodedTxt4[] = {11, '1', '2', '3', '4', '5', '6', '7', '8', '9', '=', 0};
1206     const uint8_t kEncodedTxt5[] = {12, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '=', 'a'};
1207     const uint8_t kEncodedTxt6[] = {7, 'b', 'o', 'o', 'l', 'K', 'e', 'y'};
1208     const uint8_t kEncodedTxt7[] = {9, 'e', 'm', 'p', 't', 'y', 'K', 'e', 'y', '='};
1209 
1210     const uint8_t kInvalidEncodedTxt1[] = {4, 'a', '=', 'b'}; // Incorrect length
1211 
1212     // Special encoded txt data with zero strings and string starting
1213     // with '=' (missing key) whcih should be skipped over silently.
1214     const uint8_t kSpecialEncodedTxt[] = {0, 0, 3, 'A', '=', 'B', 2, '=', 'C', 3, 'D', '=', 'E', 3, '=', '1', '2'};
1215 
1216     const Dns::TxtEntry kTxtEntries[] = {
1217         Dns::TxtEntry(kKey1, kValue1, sizeof(kValue1)),
1218         Dns::TxtEntry(kKey2, kValue2, sizeof(kValue2)),
1219         Dns::TxtEntry(kKey3, kValue3, sizeof(kValue3)),
1220         Dns::TxtEntry(kKey4, kValue4, sizeof(kValue4)),
1221         Dns::TxtEntry(kKey5, kValue5, sizeof(kValue5)),
1222         Dns::TxtEntry(kKey6, nullptr, 0),
1223         Dns::TxtEntry(kKey7, kValue1, 0),
1224     };
1225 
1226     const EncodedTxtData kEncodedTxtData[] = {
1227         {kEncodedTxt1, sizeof(kEncodedTxt1)}, {kEncodedTxt2, sizeof(kEncodedTxt2)},
1228         {kEncodedTxt3, sizeof(kEncodedTxt3)}, {kEncodedTxt4, sizeof(kEncodedTxt4)},
1229         {kEncodedTxt5, sizeof(kEncodedTxt5)}, {kEncodedTxt6, sizeof(kEncodedTxt6)},
1230         {kEncodedTxt7, sizeof(kEncodedTxt7)}};
1231 
1232     Instance *                     instance;
1233     MessagePool *                  messagePool;
1234     Message *                      message;
1235     uint8_t                        txtData[kMaxTxtDataSize];
1236     uint16_t                       txtDataLength;
1237     uint8_t                        index;
1238     Dns::TxtEntry                  txtEntry;
1239     Dns::TxtEntry::Iterator        iterator;
1240     MutableData<kWithUint16Length> data;
1241 
1242     printf("================================================================\n");
1243     printf("TestDnsTxtEntry()\n");
1244 
1245     instance = static_cast<Instance *>(testInitInstance());
1246     VerifyOrQuit(instance != nullptr);
1247 
1248     messagePool = &instance->Get<MessagePool>();
1249     VerifyOrQuit((message = messagePool->Allocate(Message::kTypeIp6)) != nullptr);
1250 
1251     data.Init(txtData, sizeof(txtData));
1252     SuccessOrQuit(Dns::TxtEntry::AppendEntries(kTxtEntries, GetArrayLength(kTxtEntries), data));
1253     VerifyOrQuit(data.GetBytes() == txtData);
1254     txtDataLength = data.GetLength();
1255     VerifyOrQuit(txtDataLength < kMaxTxtDataSize, "TXT data is too long");
1256     DumpBuffer("txt data", txtData, txtDataLength);
1257 
1258     SuccessOrQuit(Dns::TxtEntry::AppendEntries(kTxtEntries, GetArrayLength(kTxtEntries), *message));
1259     VerifyOrQuit(txtDataLength == message->GetLength());
1260     VerifyOrQuit(message->CompareBytes(0, txtData, txtDataLength));
1261 
1262     index = 0;
1263     for (const EncodedTxtData &encodedData : kEncodedTxtData)
1264     {
1265         VerifyOrQuit(memcmp(&txtData[index], encodedData.mData, encodedData.mLength) == 0);
1266         index += encodedData.mLength;
1267     }
1268 
1269     iterator.Init(txtData, txtDataLength);
1270 
1271     for (const Dns::TxtEntry &expectedTxtEntry : kTxtEntries)
1272     {
1273         uint8_t expectedKeyLength = static_cast<uint8_t>(strlen(expectedTxtEntry.mKey));
1274 
1275         SuccessOrQuit(iterator.GetNextEntry(txtEntry), "TxtEntry::GetNextEntry() failed");
1276         printf("key:\"%s\" valueLen:%d\n", txtEntry.mKey != nullptr ? txtEntry.mKey : "(null)", txtEntry.mValueLength);
1277 
1278         if (expectedKeyLength > Dns::TxtEntry::kMaxKeyLength)
1279         {
1280             // When the key is longer than recommended max key length,
1281             // the full encoded string is returned in `mValue` and
1282             // `mValueLength` and `mKey` should be set to  `nullptr`.
1283 
1284             VerifyOrQuit(txtEntry.mKey == nullptr, "TxtEntry key does not match expected value for long key");
1285             VerifyOrQuit(txtEntry.mValueLength == expectedKeyLength + expectedTxtEntry.mValueLength + sizeof(char),
1286                          "TxtEntry value length is incorrect for long key");
1287             VerifyOrQuit(memcmp(txtEntry.mValue, expectedTxtEntry.mKey, expectedKeyLength) == 0);
1288             VerifyOrQuit(txtEntry.mValue[expectedKeyLength] == static_cast<uint8_t>('='));
1289             VerifyOrQuit(memcmp(&txtEntry.mValue[expectedKeyLength + sizeof(uint8_t)], expectedTxtEntry.mValue,
1290                                 expectedTxtEntry.mValueLength) == 0);
1291             continue;
1292         }
1293 
1294         VerifyOrQuit(strcmp(txtEntry.mKey, expectedTxtEntry.mKey) == 0);
1295         VerifyOrQuit(txtEntry.mValueLength == expectedTxtEntry.mValueLength);
1296 
1297         if (txtEntry.mValueLength != 0)
1298         {
1299             VerifyOrQuit(memcmp(txtEntry.mValue, expectedTxtEntry.mValue, txtEntry.mValueLength) == 0);
1300         }
1301         else
1302         {
1303             // Ensure both `txtEntry.mKey` and `expectedTxtEntry.mKey` are
1304             // null or both are non-null (for boolean or empty keys).
1305             VerifyOrQuit((txtEntry.mKey == nullptr) == (expectedTxtEntry.mKey == nullptr),
1306                          "TxtEntry value does not match expected value for bool or empty key");
1307         }
1308     }
1309 
1310     VerifyOrQuit(iterator.GetNextEntry(txtEntry) == kErrorNotFound, "GetNextEntry() returned unexpected entry");
1311     VerifyOrQuit(iterator.GetNextEntry(txtEntry) == kErrorNotFound, "GetNextEntry() succeeded after done");
1312 
1313     // Verify `AppendEntries()` correctly rejecting invalid key
1314     txtEntry.mValue       = kValue1;
1315     txtEntry.mValueLength = sizeof(kValue1);
1316     txtEntry.mKey         = kShortKey;
1317     VerifyOrQuit(Dns::TxtEntry::AppendEntries(&txtEntry, 1, *message) == kErrorInvalidArgs,
1318                  "AppendEntries() did not fail with invalid key");
1319 
1320     // Verify appending empty txt data
1321 
1322     SuccessOrQuit(message->SetLength(0));
1323 
1324     data.Init(txtData, sizeof(txtData));
1325     SuccessOrQuit(Dns::TxtEntry::AppendEntries(nullptr, 0, data), "AppendEntries() failed with empty array");
1326     txtDataLength = data.GetLength();
1327     VerifyOrQuit(txtDataLength == sizeof(uint8_t), "Data length is incorrect with empty array");
1328     VerifyOrQuit(txtData[0] == 0, "Data is invalid with empty array");
1329 
1330     SuccessOrQuit(Dns::TxtEntry::AppendEntries(nullptr, 0, *message), "AppendEntries() failed with empty array");
1331     VerifyOrQuit(message->GetLength() == txtDataLength);
1332     VerifyOrQuit(message->CompareBytes(0, txtData, txtDataLength));
1333 
1334     SuccessOrQuit(message->SetLength(0));
1335     txtEntry.mKey         = nullptr;
1336     txtEntry.mValue       = nullptr;
1337     txtEntry.mValueLength = 0;
1338     SuccessOrQuit(Dns::TxtEntry::AppendEntries(&txtEntry, 1, *message), "AppendEntries() failed with empty entry");
1339     txtDataLength = message->GetLength();
1340     VerifyOrQuit(txtDataLength == sizeof(uint8_t), "Data length is incorrect with empty entry");
1341     SuccessOrQuit(message->Read(0, txtData, txtDataLength), "Failed to read txt data from message");
1342     VerifyOrQuit(txtData[0] == 0, "Data is invalid with empty entry");
1343 
1344     // Verify `Iterator` behavior with invalid txt data.
1345 
1346     iterator.Init(kInvalidEncodedTxt1, sizeof(kInvalidEncodedTxt1));
1347     VerifyOrQuit(iterator.GetNextEntry(txtEntry) == kErrorParse, "GetNextEntry() did not fail with invalid data");
1348 
1349     // Verify `GetNextEntry()` correctly skipping over empty strings and
1350     // strings starting with '=' (missing key) in encoded txt.
1351     //
1352     // kSpecialEncodedTxt:
1353     // { 0, 3, 'A', '=', 'B', 2, '=', 'C', 3, 'D', '=', 'E', 3, '=', '1', '2' }
1354 
1355     iterator.Init(kSpecialEncodedTxt, sizeof(kSpecialEncodedTxt));
1356 
1357     // We should get "A=B` (or key="A", and value="B")
1358     SuccessOrQuit(iterator.GetNextEntry(txtEntry), "GetNextEntry() failed");
1359     VerifyOrQuit((txtEntry.mKey[0] == 'A') && (txtEntry.mKey[1] == '\0'), "GetNextEntry() got incorrect key");
1360     VerifyOrQuit((txtEntry.mValueLength == 1) && (txtEntry.mValue[0] == 'B'), "GetNextEntry() got incorrect value");
1361 
1362     // We should get "D=E` (or key="D", and value="E")
1363     SuccessOrQuit(iterator.GetNextEntry(txtEntry), "GetNextEntry() failed");
1364     VerifyOrQuit((txtEntry.mKey[0] == 'D') && (txtEntry.mKey[1] == '\0'), "GetNextEntry() got incorrect key");
1365     VerifyOrQuit((txtEntry.mValueLength == 1) && (txtEntry.mValue[0] == 'E'), "GetNextEntry() got incorrect value");
1366 
1367     VerifyOrQuit(iterator.GetNextEntry(txtEntry) == kErrorNotFound, "GetNextEntry() returned extra entry");
1368 
1369     message->Free();
1370     testFreeInstance(instance);
1371 }
1372 
1373 } // namespace ot
1374 
main(void)1375 int main(void)
1376 {
1377     ot::TestDnsName();
1378     ot::TestDnsCompressedName();
1379     ot::TestHeaderAndResourceRecords();
1380     ot::TestDnsTxtEntry();
1381 
1382     printf("All tests passed\n");
1383     return 0;
1384 }
1385