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