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