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