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