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