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