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