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