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 /**
30 * @file
31 * This file implements generating and processing of DNS headers and helper functions/methods.
32 */
33
34 #include "dns_types.hpp"
35
36 #include "common/code_utils.hpp"
37 #include "common/debug.hpp"
38 #include "common/num_utils.hpp"
39 #include "common/random.hpp"
40 #include "common/string.hpp"
41 #include "instance/instance.hpp"
42
43 namespace ot {
44 namespace Dns {
45
SetRandomMessageId(void)46 Error Header::SetRandomMessageId(void)
47 {
48 return Random::Crypto::FillBuffer(reinterpret_cast<uint8_t *>(&mMessageId), sizeof(mMessageId));
49 }
50
ResponseCodeToError(Response aResponse)51 Error Header::ResponseCodeToError(Response aResponse)
52 {
53 Error error = kErrorFailed;
54
55 switch (aResponse)
56 {
57 case kResponseSuccess:
58 error = kErrorNone;
59 break;
60
61 case kResponseFormatError: // Server unable to interpret request due to format error.
62 case kResponseBadName: // Bad name.
63 case kResponseBadTruncation: // Bad truncation.
64 case kResponseNotZone: // A name is not in the zone.
65 error = kErrorParse;
66 break;
67
68 case kResponseServerFailure: // Server encountered an internal failure.
69 error = kErrorFailed;
70 break;
71
72 case kResponseNameError: // Name that ought to exist, does not exists.
73 case kResponseRecordNotExists: // Some RRset that ought to exist, does not exist.
74 error = kErrorNotFound;
75 break;
76
77 case kResponseNotImplemented: // Server does not support the query type (OpCode).
78 case kDsoTypeNotImplemented: // DSO TLV type is not implemented.
79 error = kErrorNotImplemented;
80 break;
81
82 case kResponseBadAlg: // Bad algorithm.
83 error = kErrorNotCapable;
84 break;
85
86 case kResponseNameExists: // Some name that ought not to exist, does exist.
87 case kResponseRecordExists: // Some RRset that ought not to exist, does exist.
88 error = kErrorDuplicated;
89 break;
90
91 case kResponseRefused: // Server refused to perform operation for policy or security reasons.
92 case kResponseNotAuth: // Service is not authoritative for zone.
93 error = kErrorSecurity;
94 break;
95
96 default:
97 break;
98 }
99
100 return error;
101 }
102
Matches(const char * aFirstLabel,const char * aLabels,const char * aDomain) const103 bool Name::Matches(const char *aFirstLabel, const char *aLabels, const char *aDomain) const
104 {
105 bool matches = false;
106
107 VerifyOrExit(!IsEmpty());
108
109 if (IsFromCString())
110 {
111 const char *namePtr = mString;
112
113 if (aFirstLabel != nullptr)
114 {
115 matches = CompareAndSkipLabels(namePtr, aFirstLabel, kLabelSeparatorChar);
116 VerifyOrExit(matches);
117 }
118
119 matches = CompareAndSkipLabels(namePtr, aLabels, kLabelSeparatorChar);
120 VerifyOrExit(matches);
121
122 matches = CompareAndSkipLabels(namePtr, aDomain, kNullChar);
123 }
124 else
125 {
126 uint16_t offset = mOffset;
127
128 if (aFirstLabel != nullptr)
129 {
130 SuccessOrExit(CompareLabel(*mMessage, offset, aFirstLabel));
131 }
132
133 SuccessOrExit(CompareMultipleLabels(*mMessage, offset, aLabels));
134 SuccessOrExit(CompareName(*mMessage, offset, aDomain));
135 matches = true;
136 }
137
138 exit:
139 return matches;
140 }
141
CompareAndSkipLabels(const char * & aNamePtr,const char * aLabels,char aExpectedNextChar)142 bool Name::CompareAndSkipLabels(const char *&aNamePtr, const char *aLabels, char aExpectedNextChar)
143 {
144 // Compares `aNamePtr` to the label string `aLabels` followed by
145 // the `aExpectedNextChar`(using case-insensitive match). Upon
146 // successful comparison, `aNamePtr` is advanced to point after
147 // the matched portion.
148
149 bool matches = false;
150 uint16_t len = StringLength(aLabels, kMaxNameSize);
151
152 VerifyOrExit(len < kMaxNameSize);
153
154 VerifyOrExit(StringStartsWith(aNamePtr, aLabels, kStringCaseInsensitiveMatch));
155 aNamePtr += len;
156
157 VerifyOrExit(*aNamePtr == aExpectedNextChar);
158 aNamePtr++;
159
160 matches = true;
161
162 exit:
163 return matches;
164 }
165
AppendTo(Message & aMessage) const166 Error Name::AppendTo(Message &aMessage) const
167 {
168 Error error;
169
170 if (IsEmpty())
171 {
172 error = AppendTerminator(aMessage);
173 }
174 else if (IsFromCString())
175 {
176 error = AppendName(GetAsCString(), aMessage);
177 }
178 else
179 {
180 // Name is from a message. Read labels one by one from
181 // `mMessage` and and append each to the `aMessage`.
182
183 LabelIterator iterator(*mMessage, mOffset);
184
185 while (true)
186 {
187 error = iterator.GetNextLabel();
188
189 switch (error)
190 {
191 case kErrorNone:
192 SuccessOrExit(error = iterator.AppendLabel(aMessage));
193 break;
194
195 case kErrorNotFound:
196 // We reached the end of name successfully.
197 error = AppendTerminator(aMessage);
198
199 OT_FALL_THROUGH;
200
201 default:
202 ExitNow();
203 }
204 }
205 }
206
207 exit:
208 return error;
209 }
210
AppendLabel(const char * aLabel,Message & aMessage)211 Error Name::AppendLabel(const char *aLabel, Message &aMessage)
212 {
213 return AppendLabel(aLabel, static_cast<uint8_t>(StringLength(aLabel, kMaxLabelSize)), aMessage);
214 }
215
AppendLabel(const char * aLabel,uint8_t aLength,Message & aMessage)216 Error Name::AppendLabel(const char *aLabel, uint8_t aLength, Message &aMessage)
217 {
218 Error error = kErrorNone;
219
220 VerifyOrExit((0 < aLength) && (aLength <= kMaxLabelLength), error = kErrorInvalidArgs);
221
222 SuccessOrExit(error = aMessage.Append(aLength));
223 error = aMessage.AppendBytes(aLabel, aLength);
224
225 exit:
226 return error;
227 }
228
AppendMultipleLabels(const char * aLabels,Message & aMessage)229 Error Name::AppendMultipleLabels(const char *aLabels, Message &aMessage)
230 {
231 Error error = kErrorNone;
232 uint16_t index = 0;
233 uint16_t labelStartIndex = 0;
234 char ch;
235
236 VerifyOrExit(aLabels != nullptr);
237
238 do
239 {
240 ch = aLabels[index];
241
242 if ((ch == kNullChar) || (ch == kLabelSeparatorChar))
243 {
244 uint8_t labelLength = static_cast<uint8_t>(index - labelStartIndex);
245
246 if (labelLength == 0)
247 {
248 // Empty label (e.g., consecutive dots) is invalid, but we
249 // allow for two cases: (1) where `aLabels` ends with a dot
250 // (`labelLength` is zero but we are at end of `aLabels` string
251 // and `ch` is null char. (2) if `aLabels` is just "." (we
252 // see a dot at index 0, and index 1 is null char).
253
254 error =
255 ((ch == kNullChar) || ((index == 0) && (aLabels[1] == kNullChar))) ? kErrorNone : kErrorInvalidArgs;
256 ExitNow();
257 }
258
259 VerifyOrExit(index + 1 < kMaxEncodedLength, error = kErrorInvalidArgs);
260 SuccessOrExit(error = AppendLabel(&aLabels[labelStartIndex], labelLength, aMessage));
261
262 labelStartIndex = index + 1;
263 }
264
265 index++;
266
267 } while (ch != kNullChar);
268
269 exit:
270 return error;
271 }
272
AppendTerminator(Message & aMessage)273 Error Name::AppendTerminator(Message &aMessage)
274 {
275 uint8_t terminator = 0;
276
277 return aMessage.Append(terminator);
278 }
279
AppendPointerLabel(uint16_t aOffset,Message & aMessage)280 Error Name::AppendPointerLabel(uint16_t aOffset, Message &aMessage)
281 {
282 Error error;
283 uint16_t value;
284
285 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
286 if (!Instance::IsDnsNameCompressionEnabled())
287 {
288 // If "DNS name compression" mode is disabled, instead of
289 // appending the pointer label, read the name from the message
290 // and append it uncompressed. Note that the `aOffset` parameter
291 // in this method is given relative to the start of DNS header
292 // in `aMessage` (which `aMessage.GetOffset()` specifies).
293
294 error = Name(aMessage, aOffset + aMessage.GetOffset()).AppendTo(aMessage);
295 ExitNow();
296 }
297 #endif
298
299 // A pointer label takes the form of a two byte sequence as a
300 // `uint16_t` value. The first two bits are ones. This allows a
301 // pointer to be distinguished from a text label, since the text
302 // label must begin with two zero bits (note that labels are
303 // restricted to 63 octets or less). The next 14-bits specify
304 // an offset value relative to start of DNS header.
305
306 OT_ASSERT(aOffset < kPointerLabelTypeUint16);
307
308 value = BigEndian::HostSwap16(aOffset | kPointerLabelTypeUint16);
309
310 ExitNow(error = aMessage.Append(value));
311
312 exit:
313 return error;
314 }
315
AppendName(const char * aName,Message & aMessage)316 Error Name::AppendName(const char *aName, Message &aMessage)
317 {
318 Error error;
319
320 SuccessOrExit(error = AppendMultipleLabels(aName, aMessage));
321 error = AppendTerminator(aMessage);
322
323 exit:
324 return error;
325 }
326
ParseName(const Message & aMessage,uint16_t & aOffset)327 Error Name::ParseName(const Message &aMessage, uint16_t &aOffset)
328 {
329 Error error;
330 LabelIterator iterator(aMessage, aOffset);
331
332 while (true)
333 {
334 error = iterator.GetNextLabel();
335
336 switch (error)
337 {
338 case kErrorNone:
339 break;
340
341 case kErrorNotFound:
342 // We reached the end of name successfully.
343 aOffset = iterator.mNameEndOffset;
344 error = kErrorNone;
345
346 OT_FALL_THROUGH;
347
348 default:
349 ExitNow();
350 }
351 }
352
353 exit:
354 return error;
355 }
356
ReadLabel(const Message & aMessage,uint16_t & aOffset,char * aLabelBuffer,uint8_t & aLabelLength)357 Error Name::ReadLabel(const Message &aMessage, uint16_t &aOffset, char *aLabelBuffer, uint8_t &aLabelLength)
358 {
359 Error error;
360 LabelIterator iterator(aMessage, aOffset);
361
362 SuccessOrExit(error = iterator.GetNextLabel());
363 SuccessOrExit(error = iterator.ReadLabel(aLabelBuffer, aLabelLength, /* aAllowDotCharInLabel */ true));
364 aOffset = iterator.mNextLabelOffset;
365
366 exit:
367 return error;
368 }
369
ReadName(const Message & aMessage,uint16_t & aOffset,char * aNameBuffer,uint16_t aNameBufferSize)370 Error Name::ReadName(const Message &aMessage, uint16_t &aOffset, char *aNameBuffer, uint16_t aNameBufferSize)
371 {
372 Error error;
373 LabelIterator iterator(aMessage, aOffset);
374 bool firstLabel = true;
375 uint8_t labelLength;
376
377 while (true)
378 {
379 error = iterator.GetNextLabel();
380
381 switch (error)
382 {
383 case kErrorNone:
384
385 if (!firstLabel)
386 {
387 *aNameBuffer++ = kLabelSeparatorChar;
388 aNameBufferSize--;
389
390 // No need to check if we have reached end of the name buffer
391 // here since `iterator.ReadLabel()` would verify it.
392 }
393
394 labelLength = static_cast<uint8_t>(Min(static_cast<uint16_t>(kMaxLabelSize), aNameBufferSize));
395 SuccessOrExit(error = iterator.ReadLabel(aNameBuffer, labelLength, /* aAllowDotCharInLabel */ firstLabel));
396 aNameBuffer += labelLength;
397 aNameBufferSize -= labelLength;
398 firstLabel = false;
399 break;
400
401 case kErrorNotFound:
402 // We reach the end of name successfully. Always add a terminating dot
403 // at the end.
404 *aNameBuffer++ = kLabelSeparatorChar;
405 aNameBufferSize--;
406 VerifyOrExit(aNameBufferSize >= sizeof(uint8_t), error = kErrorNoBufs);
407 *aNameBuffer = kNullChar;
408 aOffset = iterator.mNameEndOffset;
409 error = kErrorNone;
410
411 OT_FALL_THROUGH;
412
413 default:
414 ExitNow();
415 }
416 }
417
418 exit:
419 return error;
420 }
421
CompareLabel(const Message & aMessage,uint16_t & aOffset,const char * aLabel)422 Error Name::CompareLabel(const Message &aMessage, uint16_t &aOffset, const char *aLabel)
423 {
424 Error error;
425 LabelIterator iterator(aMessage, aOffset);
426
427 SuccessOrExit(error = iterator.GetNextLabel());
428 VerifyOrExit(iterator.CompareLabel(aLabel, kIsSingleLabel), error = kErrorNotFound);
429 aOffset = iterator.mNextLabelOffset;
430
431 exit:
432 return error;
433 }
434
CompareMultipleLabels(const Message & aMessage,uint16_t & aOffset,const char * aLabels)435 Error Name::CompareMultipleLabels(const Message &aMessage, uint16_t &aOffset, const char *aLabels)
436 {
437 Error error;
438 LabelIterator iterator(aMessage, aOffset);
439
440 while (true)
441 {
442 SuccessOrExit(error = iterator.GetNextLabel());
443 VerifyOrExit(iterator.CompareLabel(aLabels, !kIsSingleLabel), error = kErrorNotFound);
444
445 if (*aLabels == kNullChar)
446 {
447 aOffset = iterator.mNextLabelOffset;
448 ExitNow();
449 }
450 }
451
452 exit:
453 return error;
454 }
455
CompareName(const Message & aMessage,uint16_t & aOffset,const char * aName)456 Error Name::CompareName(const Message &aMessage, uint16_t &aOffset, const char *aName)
457 {
458 Error error;
459 LabelIterator iterator(aMessage, aOffset);
460 bool matches = true;
461
462 if (*aName == kLabelSeparatorChar)
463 {
464 aName++;
465 VerifyOrExit(*aName == kNullChar, error = kErrorInvalidArgs);
466 }
467
468 while (true)
469 {
470 error = iterator.GetNextLabel();
471
472 switch (error)
473 {
474 case kErrorNone:
475 if (matches && !iterator.CompareLabel(aName, !kIsSingleLabel))
476 {
477 matches = false;
478 }
479
480 break;
481
482 case kErrorNotFound:
483 // We reached the end of the name in `aMessage`. We check if
484 // all the previous labels matched so far, and we are also
485 // at the end of `aName` string (see null char), then we
486 // return `kErrorNone` indicating a successful comparison
487 // (full match). Otherwise we return `kErrorNotFound` to
488 // indicate failed comparison.
489
490 if (matches && (*aName == kNullChar))
491 {
492 error = kErrorNone;
493 }
494
495 aOffset = iterator.mNameEndOffset;
496
497 OT_FALL_THROUGH;
498
499 default:
500 ExitNow();
501 }
502 }
503
504 exit:
505 return error;
506 }
507
CompareName(const Message & aMessage,uint16_t & aOffset,const Message & aMessage2,uint16_t aOffset2)508 Error Name::CompareName(const Message &aMessage, uint16_t &aOffset, const Message &aMessage2, uint16_t aOffset2)
509 {
510 Error error;
511 LabelIterator iterator(aMessage, aOffset);
512 LabelIterator iterator2(aMessage2, aOffset2);
513 bool matches = true;
514
515 while (true)
516 {
517 error = iterator.GetNextLabel();
518
519 switch (error)
520 {
521 case kErrorNone:
522 // If all the previous labels matched so far, then verify
523 // that we can get the next label on `iterator2` and that it
524 // matches the label from `iterator`.
525 if (matches && (iterator2.GetNextLabel() != kErrorNone || !iterator.CompareLabel(iterator2)))
526 {
527 matches = false;
528 }
529
530 break;
531
532 case kErrorNotFound:
533 // We reached the end of the name in `aMessage`. We check
534 // that `iterator2` is also at its end, and if all previous
535 // labels matched we return `kErrorNone`.
536
537 if (matches && (iterator2.GetNextLabel() == kErrorNotFound))
538 {
539 error = kErrorNone;
540 }
541
542 aOffset = iterator.mNameEndOffset;
543
544 OT_FALL_THROUGH;
545
546 default:
547 ExitNow();
548 }
549 }
550
551 exit:
552 return error;
553 }
554
CompareName(const Message & aMessage,uint16_t & aOffset,const Name & aName)555 Error Name::CompareName(const Message &aMessage, uint16_t &aOffset, const Name &aName)
556 {
557 return aName.IsFromCString()
558 ? CompareName(aMessage, aOffset, aName.mString)
559 : (aName.IsFromMessage() ? CompareName(aMessage, aOffset, *aName.mMessage, aName.mOffset)
560 : ParseName(aMessage, aOffset));
561 }
562
GetNextLabel(void)563 Error Name::LabelIterator::GetNextLabel(void)
564 {
565 Error error;
566
567 while (true)
568 {
569 uint8_t labelLength;
570 uint8_t labelType;
571
572 SuccessOrExit(error = mMessage.Read(mNextLabelOffset, labelLength));
573
574 labelType = labelLength & kLabelTypeMask;
575
576 if (labelType == kTextLabelType)
577 {
578 if (labelLength == 0)
579 {
580 // Zero label length indicates end of a name.
581
582 if (!IsEndOffsetSet())
583 {
584 mNameEndOffset = mNextLabelOffset + sizeof(uint8_t);
585 }
586
587 ExitNow(error = kErrorNotFound);
588 }
589
590 mLabelStartOffset = mNextLabelOffset + sizeof(uint8_t);
591 mLabelLength = labelLength;
592 mNextLabelOffset = mLabelStartOffset + labelLength;
593 ExitNow();
594 }
595 else if (labelType == kPointerLabelType)
596 {
597 // A pointer label takes the form of a two byte sequence as a
598 // `uint16_t` value. The first two bits are ones. The next 14 bits
599 // specify an offset value from the start of the DNS header.
600
601 uint16_t pointerValue;
602 uint16_t nextLabelOffset;
603
604 SuccessOrExit(error = mMessage.Read(mNextLabelOffset, pointerValue));
605
606 if (!IsEndOffsetSet())
607 {
608 mNameEndOffset = mNextLabelOffset + sizeof(uint16_t);
609 }
610
611 // `mMessage.GetOffset()` must point to the start of the
612 // DNS header.
613 nextLabelOffset = mMessage.GetOffset() + (BigEndian::HostSwap16(pointerValue) & kPointerLabelOffsetMask);
614 VerifyOrExit(nextLabelOffset < mNextLabelOffset, error = kErrorParse);
615 mNextLabelOffset = nextLabelOffset;
616
617 // Go back through the `while(true)` loop to get the next label.
618 }
619 else
620 {
621 ExitNow(error = kErrorParse);
622 }
623 }
624
625 exit:
626 return error;
627 }
628
ReadLabel(char * aLabelBuffer,uint8_t & aLabelLength,bool aAllowDotCharInLabel) const629 Error Name::LabelIterator::ReadLabel(char *aLabelBuffer, uint8_t &aLabelLength, bool aAllowDotCharInLabel) const
630 {
631 Error error;
632
633 VerifyOrExit(mLabelLength < aLabelLength, error = kErrorNoBufs);
634
635 SuccessOrExit(error = mMessage.Read(mLabelStartOffset, aLabelBuffer, mLabelLength));
636 aLabelBuffer[mLabelLength] = kNullChar;
637 aLabelLength = mLabelLength;
638
639 if (!aAllowDotCharInLabel)
640 {
641 VerifyOrExit(StringFind(aLabelBuffer, kLabelSeparatorChar) == nullptr, error = kErrorParse);
642 }
643
644 exit:
645 return error;
646 }
647
CaseInsensitiveMatch(uint8_t aFirst,uint8_t aSecond)648 bool Name::LabelIterator::CaseInsensitiveMatch(uint8_t aFirst, uint8_t aSecond)
649 {
650 return ToLowercase(static_cast<char>(aFirst)) == ToLowercase(static_cast<char>(aSecond));
651 }
652
CompareLabel(const char * & aName,bool aIsSingleLabel) const653 bool Name::LabelIterator::CompareLabel(const char *&aName, bool aIsSingleLabel) const
654 {
655 // This method compares the current label in the iterator with the
656 // `aName` string. `aIsSingleLabel` indicates whether `aName` is a
657 // single label, or a sequence of labels separated by dot '.' char.
658 // If the label matches `aName`, then `aName` pointer is moved
659 // forward to the start of the next label (skipping over the `.`
660 // char). This method returns `true` when the labels match, `false`
661 // otherwise.
662
663 bool matches = false;
664
665 VerifyOrExit(StringLength(aName, mLabelLength) == mLabelLength);
666 matches = mMessage.CompareBytes(mLabelStartOffset, aName, mLabelLength, CaseInsensitiveMatch);
667
668 VerifyOrExit(matches);
669
670 aName += mLabelLength;
671
672 // If `aName` is a single label, we should be also at the end of the
673 // `aName` string. Otherwise, we should see either null or dot '.'
674 // character (in case `aName` contains multiple labels).
675
676 matches = (*aName == kNullChar);
677
678 if (!aIsSingleLabel && (*aName == kLabelSeparatorChar))
679 {
680 matches = true;
681 aName++;
682 }
683
684 exit:
685 return matches;
686 }
687
CompareLabel(const LabelIterator & aOtherIterator) const688 bool Name::LabelIterator::CompareLabel(const LabelIterator &aOtherIterator) const
689 {
690 // This method compares the current label in the iterator with the
691 // label from another iterator.
692
693 return (mLabelLength == aOtherIterator.mLabelLength) &&
694 mMessage.CompareBytes(mLabelStartOffset, aOtherIterator.mMessage, aOtherIterator.mLabelStartOffset,
695 mLabelLength, CaseInsensitiveMatch);
696 }
697
AppendLabel(Message & aMessage) const698 Error Name::LabelIterator::AppendLabel(Message &aMessage) const
699 {
700 // This method reads and appends the current label in the iterator
701 // to `aMessage`.
702
703 Error error;
704
705 VerifyOrExit((0 < mLabelLength) && (mLabelLength <= kMaxLabelLength), error = kErrorInvalidArgs);
706 SuccessOrExit(error = aMessage.Append(mLabelLength));
707 error = aMessage.AppendBytesFromMessage(mMessage, mLabelStartOffset, mLabelLength);
708
709 exit:
710 return error;
711 }
712
ExtractLabels(const char * aName,const char * aSuffixName,char * aLabels,uint16_t aLabelsSize)713 Error Name::ExtractLabels(const char *aName, const char *aSuffixName, char *aLabels, uint16_t aLabelsSize)
714 {
715 Error error = kErrorParse;
716 uint16_t nameLength = StringLength(aName, kMaxNameSize);
717 uint16_t suffixLength = StringLength(aSuffixName, kMaxNameSize);
718 const char *suffixStart;
719
720 VerifyOrExit(nameLength < kMaxNameSize);
721 VerifyOrExit(suffixLength < kMaxNameSize);
722
723 VerifyOrExit(nameLength > suffixLength);
724
725 suffixStart = aName + nameLength - suffixLength;
726 VerifyOrExit(StringMatch(suffixStart, aSuffixName, kStringCaseInsensitiveMatch));
727 suffixStart--;
728 VerifyOrExit(*suffixStart == kLabelSeparatorChar);
729
730 // Determine the labels length to copy
731 nameLength -= (suffixLength + 1);
732 VerifyOrExit(nameLength < aLabelsSize, error = kErrorNoBufs);
733
734 memcpy(aLabels, aName, nameLength);
735 aLabels[nameLength] = kNullChar;
736 error = kErrorNone;
737
738 exit:
739 return error;
740 }
741
IsSubDomainOf(const char * aName,const char * aDomain)742 bool Name::IsSubDomainOf(const char *aName, const char *aDomain)
743 {
744 bool match = false;
745 bool nameEndsWithDot = false;
746 bool domainEndsWithDot = false;
747 uint16_t nameLength = StringLength(aName, kMaxNameLength);
748 uint16_t domainLength = StringLength(aDomain, kMaxNameLength);
749
750 if (nameLength > 0 && aName[nameLength - 1] == kLabelSeparatorChar)
751 {
752 nameEndsWithDot = true;
753 --nameLength;
754 }
755
756 if (domainLength > 0 && aDomain[domainLength - 1] == kLabelSeparatorChar)
757 {
758 domainEndsWithDot = true;
759 --domainLength;
760 }
761
762 VerifyOrExit(nameLength >= domainLength);
763
764 aName += nameLength - domainLength;
765
766 if (nameLength > domainLength)
767 {
768 VerifyOrExit(aName[-1] == kLabelSeparatorChar);
769 }
770
771 // This method allows either `aName` or `aDomain` to include or
772 // exclude the last `.` character. If both include it or if both
773 // do not, we do a full comparison using `StringMatch()`.
774 // Otherwise (i.e., when one includes and the other one does not)
775 // we use `StringStartWith()` to allow the extra `.` character.
776
777 if (nameEndsWithDot == domainEndsWithDot)
778 {
779 match = StringMatch(aName, aDomain, kStringCaseInsensitiveMatch);
780 }
781 else if (nameEndsWithDot)
782 {
783 // `aName` ends with dot, but `aDomain` does not.
784 match = StringStartsWith(aName, aDomain, kStringCaseInsensitiveMatch);
785 }
786 else
787 {
788 // `aDomain` ends with dot, but `aName` does not.
789 match = StringStartsWith(aDomain, aName, kStringCaseInsensitiveMatch);
790 }
791
792 exit:
793 return match;
794 }
795
IsSameDomain(const char * aDomain1,const char * aDomain2)796 bool Name::IsSameDomain(const char *aDomain1, const char *aDomain2)
797 {
798 return IsSubDomainOf(aDomain1, aDomain2) && IsSubDomainOf(aDomain2, aDomain1);
799 }
800
ParseRecords(const Message & aMessage,uint16_t & aOffset,uint16_t aNumRecords)801 Error ResourceRecord::ParseRecords(const Message &aMessage, uint16_t &aOffset, uint16_t aNumRecords)
802 {
803 Error error = kErrorNone;
804
805 while (aNumRecords > 0)
806 {
807 ResourceRecord record;
808
809 SuccessOrExit(error = Name::ParseName(aMessage, aOffset));
810 SuccessOrExit(error = record.ReadFrom(aMessage, aOffset));
811 aOffset += static_cast<uint16_t>(record.GetSize());
812 aNumRecords--;
813 }
814
815 exit:
816 return error;
817 }
818
FindRecord(const Message & aMessage,uint16_t & aOffset,uint16_t & aNumRecords,const Name & aName)819 Error ResourceRecord::FindRecord(const Message &aMessage, uint16_t &aOffset, uint16_t &aNumRecords, const Name &aName)
820 {
821 Error error;
822
823 while (aNumRecords > 0)
824 {
825 bool matches = true;
826 ResourceRecord record;
827
828 error = Name::CompareName(aMessage, aOffset, aName);
829
830 switch (error)
831 {
832 case kErrorNone:
833 break;
834 case kErrorNotFound:
835 matches = false;
836 break;
837 default:
838 ExitNow();
839 }
840
841 SuccessOrExit(error = record.ReadFrom(aMessage, aOffset));
842 aNumRecords--;
843 VerifyOrExit(!matches);
844 aOffset += static_cast<uint16_t>(record.GetSize());
845 }
846
847 error = kErrorNotFound;
848
849 exit:
850 return error;
851 }
852
FindRecord(const Message & aMessage,uint16_t & aOffset,uint16_t aNumRecords,uint16_t aIndex,const Name & aName,uint16_t aType,ResourceRecord & aRecord,uint16_t aMinRecordSize)853 Error ResourceRecord::FindRecord(const Message &aMessage,
854 uint16_t &aOffset,
855 uint16_t aNumRecords,
856 uint16_t aIndex,
857 const Name &aName,
858 uint16_t aType,
859 ResourceRecord &aRecord,
860 uint16_t aMinRecordSize)
861 {
862 // This static method searches in `aMessage` starting from `aOffset`
863 // up to maximum of `aNumRecords`, for the `(aIndex+1)`th
864 // occurrence of a resource record of type `aType` with record name
865 // matching `aName`. It also verifies that the record size is larger
866 // than `aMinRecordSize`. If found, `aMinRecordSize` bytes from the
867 // record are read and copied into `aRecord`. In this case `aOffset`
868 // is updated to point to the last record byte read from the message
869 // (so that the caller can read any remaining fields in the record
870 // data).
871
872 Error error;
873 uint16_t offset = aOffset;
874 uint16_t recordOffset;
875
876 while (aNumRecords > 0)
877 {
878 SuccessOrExit(error = FindRecord(aMessage, offset, aNumRecords, aName));
879
880 // Save the offset to start of `ResourceRecord` fields.
881 recordOffset = offset;
882
883 error = ReadRecord(aMessage, offset, aType, aRecord, aMinRecordSize);
884
885 if (error == kErrorNotFound)
886 {
887 // `ReadRecord()` already updates the `offset` to skip
888 // over a non-matching record.
889 continue;
890 }
891
892 SuccessOrExit(error);
893
894 if (aIndex == 0)
895 {
896 aOffset = offset;
897 ExitNow();
898 }
899
900 aIndex--;
901
902 // Skip over the record.
903 offset = static_cast<uint16_t>(recordOffset + aRecord.GetSize());
904 }
905
906 error = kErrorNotFound;
907
908 exit:
909 return error;
910 }
911
ReadRecord(const Message & aMessage,uint16_t & aOffset,uint16_t aType,ResourceRecord & aRecord,uint16_t aMinRecordSize)912 Error ResourceRecord::ReadRecord(const Message &aMessage,
913 uint16_t &aOffset,
914 uint16_t aType,
915 ResourceRecord &aRecord,
916 uint16_t aMinRecordSize)
917 {
918 // This static method tries to read a matching resource record of a
919 // given type and a minimum record size from a message. The `aType`
920 // value of `kTypeAny` matches any type. If the record in the
921 // message does not match, it skips over the record. Please see
922 // `ReadRecord<RecordType>()` for more details.
923
924 Error error;
925 ResourceRecord record;
926
927 SuccessOrExit(error = record.ReadFrom(aMessage, aOffset));
928
929 if (((aType == kTypeAny) || (record.GetType() == aType)) && (record.GetSize() >= aMinRecordSize))
930 {
931 IgnoreError(aMessage.Read(aOffset, &aRecord, aMinRecordSize));
932 aOffset += aMinRecordSize;
933 }
934 else
935 {
936 // Skip over the entire record.
937 aOffset += static_cast<uint16_t>(record.GetSize());
938 error = kErrorNotFound;
939 }
940
941 exit:
942 return error;
943 }
944
ReadName(const Message & aMessage,uint16_t & aOffset,uint16_t aStartOffset,char * aNameBuffer,uint16_t aNameBufferSize,bool aSkipRecord) const945 Error ResourceRecord::ReadName(const Message &aMessage,
946 uint16_t &aOffset,
947 uint16_t aStartOffset,
948 char *aNameBuffer,
949 uint16_t aNameBufferSize,
950 bool aSkipRecord) const
951 {
952 // This protected method parses and reads a name field in a record
953 // from a message. It is intended only for sub-classes of
954 // `ResourceRecord`.
955 //
956 // On input `aOffset` gives the offset in `aMessage` to the start of
957 // name field. `aStartOffset` gives the offset to the start of the
958 // `ResourceRecord`. `aSkipRecord` indicates whether to skip over
959 // the entire resource record or just the read name. On exit, when
960 // successfully read, `aOffset` is updated to either point after the
961 // end of record or after the the name field.
962 //
963 // When read successfully, this method returns `kErrorNone`. On a
964 // parse error (invalid format) returns `kErrorParse`. If the
965 // name does not fit in the given name buffer it returns
966 // `kErrorNoBufs`
967
968 Error error = kErrorNone;
969
970 SuccessOrExit(error = Name::ReadName(aMessage, aOffset, aNameBuffer, aNameBufferSize));
971 VerifyOrExit(aOffset <= aStartOffset + GetSize(), error = kErrorParse);
972
973 VerifyOrExit(aSkipRecord);
974 aOffset = aStartOffset;
975 error = SkipRecord(aMessage, aOffset);
976
977 exit:
978 return error;
979 }
980
SkipRecord(const Message & aMessage,uint16_t & aOffset) const981 Error ResourceRecord::SkipRecord(const Message &aMessage, uint16_t &aOffset) const
982 {
983 // This protected method parses and skips over a resource record
984 // in a message.
985 //
986 // On input `aOffset` gives the offset in `aMessage` to the start of
987 // the `ResourceRecord`. On exit, when successfully parsed, `aOffset`
988 // is updated to point to byte after the entire record.
989
990 Error error;
991
992 SuccessOrExit(error = CheckRecord(aMessage, aOffset));
993 aOffset += static_cast<uint16_t>(GetSize());
994
995 exit:
996 return error;
997 }
998
CheckRecord(const Message & aMessage,uint16_t aOffset) const999 Error ResourceRecord::CheckRecord(const Message &aMessage, uint16_t aOffset) const
1000 {
1001 // This method checks that the entire record (including record data)
1002 // is present in `aMessage` at `aOffset` (pointing to the start of
1003 // the `ResourceRecord` in `aMessage`).
1004
1005 return (aOffset + GetSize() <= aMessage.GetLength()) ? kErrorNone : kErrorParse;
1006 }
1007
ReadFrom(const Message & aMessage,uint16_t aOffset)1008 Error ResourceRecord::ReadFrom(const Message &aMessage, uint16_t aOffset)
1009 {
1010 // This method reads the `ResourceRecord` from `aMessage` at
1011 // `aOffset`. It verifies that the entire record (including record
1012 // data) is present in the message.
1013
1014 Error error;
1015
1016 SuccessOrExit(error = aMessage.Read(aOffset, *this));
1017 error = CheckRecord(aMessage, aOffset);
1018
1019 exit:
1020 return error;
1021 }
1022
Init(const uint8_t * aTxtData,uint16_t aTxtDataLength)1023 void TxtEntry::Iterator::Init(const uint8_t *aTxtData, uint16_t aTxtDataLength)
1024 {
1025 SetTxtData(aTxtData);
1026 SetTxtDataLength(aTxtDataLength);
1027 SetTxtDataPosition(0);
1028 }
1029
GetNextEntry(TxtEntry & aEntry)1030 Error TxtEntry::Iterator::GetNextEntry(TxtEntry &aEntry)
1031 {
1032 Error error = kErrorNone;
1033 uint8_t length;
1034 uint8_t index;
1035 const char *cur;
1036 char *keyBuffer = GetKeyBuffer();
1037
1038 static_assert(sizeof(mChar) >= TxtEntry::kMaxKeyLength + 1, "KeyBuffer cannot fit the max key length");
1039
1040 VerifyOrExit(GetTxtData() != nullptr, error = kErrorParse);
1041
1042 aEntry.mKey = keyBuffer;
1043
1044 while ((cur = GetTxtData() + GetTxtDataPosition()) < GetTxtDataEnd())
1045 {
1046 length = static_cast<uint8_t>(*cur);
1047
1048 cur++;
1049 VerifyOrExit(cur + length <= GetTxtDataEnd(), error = kErrorParse);
1050 IncreaseTxtDataPosition(sizeof(uint8_t) + length);
1051
1052 // Silently skip over an empty string or if the string starts with
1053 // a `=` character (i.e., missing key) - RFC 6763 - section 6.4.
1054
1055 if ((length == 0) || (cur[0] == kKeyValueSeparator))
1056 {
1057 continue;
1058 }
1059
1060 for (index = 0; index < length; index++)
1061 {
1062 if (cur[index] == kKeyValueSeparator)
1063 {
1064 keyBuffer[index++] = kNullChar; // Increment index to skip over `=`.
1065 aEntry.mValue = reinterpret_cast<const uint8_t *>(&cur[index]);
1066 aEntry.mValueLength = length - index;
1067 ExitNow();
1068 }
1069
1070 if (index >= sizeof(mChar) - 1)
1071 {
1072 // The key is larger than supported key string length.
1073 // In this case, we return the full encoded string in
1074 // `mValue` and `mValueLength` and set `mKey` to
1075 // `nullptr`.
1076
1077 aEntry.mKey = nullptr;
1078 aEntry.mValue = reinterpret_cast<const uint8_t *>(cur);
1079 aEntry.mValueLength = length;
1080 ExitNow();
1081 }
1082
1083 keyBuffer[index] = cur[index];
1084 }
1085
1086 // If we reach the end of the string without finding `=` then
1087 // it is a boolean key attribute (encoded as "key").
1088
1089 keyBuffer[index] = kNullChar;
1090 aEntry.mValue = nullptr;
1091 aEntry.mValueLength = 0;
1092 ExitNow();
1093 }
1094
1095 error = kErrorNotFound;
1096
1097 exit:
1098 return error;
1099 }
1100
AppendTo(Message & aMessage) const1101 Error TxtEntry::AppendTo(Message &aMessage) const
1102 {
1103 Appender appender(aMessage);
1104
1105 return AppendTo(appender);
1106 }
1107
AppendTo(Appender & aAppender) const1108 Error TxtEntry::AppendTo(Appender &aAppender) const
1109 {
1110 Error error = kErrorNone;
1111 uint16_t keyLength;
1112 char separator = kKeyValueSeparator;
1113
1114 if (mKey == nullptr)
1115 {
1116 VerifyOrExit((mValue != nullptr) && (mValueLength != 0));
1117 error = aAppender.AppendBytes(mValue, mValueLength);
1118 ExitNow();
1119 }
1120
1121 keyLength = StringLength(mKey, static_cast<uint16_t>(kMaxKeyValueEncodedSize) + 1);
1122
1123 VerifyOrExit(kMinKeyLength <= keyLength, error = kErrorInvalidArgs);
1124
1125 if (mValue == nullptr)
1126 {
1127 // Treat as a boolean attribute and encoded as "key" (with no `=`).
1128 SuccessOrExit(error = aAppender.Append<uint8_t>(static_cast<uint8_t>(keyLength)));
1129 error = aAppender.AppendBytes(mKey, keyLength);
1130 ExitNow();
1131 }
1132
1133 // Treat as key/value and encode as "key=value", value may be empty.
1134
1135 VerifyOrExit(mValueLength + keyLength + sizeof(char) <= kMaxKeyValueEncodedSize, error = kErrorInvalidArgs);
1136
1137 SuccessOrExit(error = aAppender.Append<uint8_t>(static_cast<uint8_t>(keyLength + mValueLength + sizeof(char))));
1138 SuccessOrExit(error = aAppender.AppendBytes(mKey, keyLength));
1139 SuccessOrExit(error = aAppender.Append(separator));
1140 error = aAppender.AppendBytes(mValue, mValueLength);
1141
1142 exit:
1143 return error;
1144 }
1145
AppendEntries(const TxtEntry * aEntries,uint16_t aNumEntries,Message & aMessage)1146 Error TxtEntry::AppendEntries(const TxtEntry *aEntries, uint16_t aNumEntries, Message &aMessage)
1147 {
1148 Appender appender(aMessage);
1149
1150 return AppendEntries(aEntries, aNumEntries, appender);
1151 }
1152
AppendEntries(const TxtEntry * aEntries,uint16_t aNumEntries,MutableData<kWithUint16Length> & aData)1153 Error TxtEntry::AppendEntries(const TxtEntry *aEntries, uint16_t aNumEntries, MutableData<kWithUint16Length> &aData)
1154 {
1155 Error error;
1156 Appender appender(aData.GetBytes(), aData.GetLength());
1157
1158 SuccessOrExit(error = AppendEntries(aEntries, aNumEntries, appender));
1159 appender.GetAsData(aData);
1160
1161 exit:
1162 return error;
1163 }
1164
AppendEntries(const TxtEntry * aEntries,uint16_t aNumEntries,Appender & aAppender)1165 Error TxtEntry::AppendEntries(const TxtEntry *aEntries, uint16_t aNumEntries, Appender &aAppender)
1166 {
1167 Error error = kErrorNone;
1168
1169 for (uint16_t index = 0; index < aNumEntries; index++)
1170 {
1171 SuccessOrExit(error = aEntries[index].AppendTo(aAppender));
1172 }
1173
1174 if (aAppender.GetAppendedLength() == 0)
1175 {
1176 error = aAppender.Append<uint8_t>(0);
1177 }
1178
1179 exit:
1180 return error;
1181 }
1182
IsValid(void) const1183 bool AaaaRecord::IsValid(void) const
1184 {
1185 return GetType() == Dns::ResourceRecord::kTypeAaaa && GetSize() == sizeof(*this);
1186 }
1187
IsValid(void) const1188 bool KeyRecord::IsValid(void) const { return GetType() == Dns::ResourceRecord::kTypeKey; }
1189
1190 #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
Init(void)1191 void Ecdsa256KeyRecord::Init(void)
1192 {
1193 KeyRecord::Init();
1194 SetAlgorithm(kAlgorithmEcdsaP256Sha256);
1195 }
1196
IsValid(void) const1197 bool Ecdsa256KeyRecord::IsValid(void) const
1198 {
1199 return KeyRecord::IsValid() && GetLength() == sizeof(*this) - sizeof(ResourceRecord) &&
1200 GetAlgorithm() == kAlgorithmEcdsaP256Sha256;
1201 }
1202 #endif
1203
IsValid(void) const1204 bool SigRecord::IsValid(void) const
1205 {
1206 return GetType() == Dns::ResourceRecord::kTypeSig && GetLength() >= sizeof(*this) - sizeof(ResourceRecord);
1207 }
1208
InitAsShortVariant(uint32_t aLeaseInterval)1209 void LeaseOption::InitAsShortVariant(uint32_t aLeaseInterval)
1210 {
1211 SetOptionCode(kUpdateLease);
1212 SetOptionLength(kShortLength);
1213 SetLeaseInterval(aLeaseInterval);
1214 }
1215
InitAsLongVariant(uint32_t aLeaseInterval,uint32_t aKeyLeaseInterval)1216 void LeaseOption::InitAsLongVariant(uint32_t aLeaseInterval, uint32_t aKeyLeaseInterval)
1217 {
1218 SetOptionCode(kUpdateLease);
1219 SetOptionLength(kLongLength);
1220 SetLeaseInterval(aLeaseInterval);
1221 SetKeyLeaseInterval(aKeyLeaseInterval);
1222 }
1223
IsValid(void) const1224 bool LeaseOption::IsValid(void) const
1225 {
1226 bool isValid = false;
1227
1228 VerifyOrExit((GetOptionLength() == kShortLength) || (GetOptionLength() >= kLongLength));
1229 isValid = (GetLeaseInterval() <= GetKeyLeaseInterval());
1230
1231 exit:
1232 return isValid;
1233 }
1234
ReadFrom(const Message & aMessage,uint16_t aOffset,uint16_t aLength)1235 Error LeaseOption::ReadFrom(const Message &aMessage, uint16_t aOffset, uint16_t aLength)
1236 {
1237 Error error = kErrorNone;
1238 uint16_t endOffset;
1239
1240 VerifyOrExit(static_cast<uint32_t>(aOffset) + aLength <= aMessage.GetLength(), error = kErrorParse);
1241
1242 endOffset = aOffset + aLength;
1243
1244 while (aOffset < endOffset)
1245 {
1246 uint16_t size;
1247
1248 SuccessOrExit(error = aMessage.Read(aOffset, this, sizeof(Option)));
1249
1250 VerifyOrExit(aOffset + GetSize() <= endOffset, error = kErrorParse);
1251
1252 size = static_cast<uint16_t>(GetSize());
1253
1254 if (GetOptionCode() == kUpdateLease)
1255 {
1256 VerifyOrExit(GetOptionLength() >= kShortLength, error = kErrorParse);
1257
1258 IgnoreError(aMessage.Read(aOffset, this, Min(size, static_cast<uint16_t>(sizeof(LeaseOption)))));
1259 VerifyOrExit(IsValid(), error = kErrorParse);
1260
1261 ExitNow();
1262 }
1263
1264 aOffset += size;
1265 }
1266
1267 error = kErrorNotFound;
1268
1269 exit:
1270 return error;
1271 }
1272
ReadPtrName(const Message & aMessage,uint16_t & aOffset,char * aLabelBuffer,uint8_t aLabelBufferSize,char * aNameBuffer,uint16_t aNameBufferSize) const1273 Error PtrRecord::ReadPtrName(const Message &aMessage,
1274 uint16_t &aOffset,
1275 char *aLabelBuffer,
1276 uint8_t aLabelBufferSize,
1277 char *aNameBuffer,
1278 uint16_t aNameBufferSize) const
1279 {
1280 Error error = kErrorNone;
1281 uint16_t startOffset = aOffset - sizeof(PtrRecord); // start of `PtrRecord`.
1282
1283 // Verify that the name is within the record data length.
1284 SuccessOrExit(error = Name::ParseName(aMessage, aOffset));
1285 VerifyOrExit(aOffset <= startOffset + GetSize(), error = kErrorParse);
1286
1287 aOffset = startOffset + sizeof(PtrRecord);
1288 SuccessOrExit(error = Name::ReadLabel(aMessage, aOffset, aLabelBuffer, aLabelBufferSize));
1289
1290 if (aNameBuffer != nullptr)
1291 {
1292 SuccessOrExit(error = Name::ReadName(aMessage, aOffset, aNameBuffer, aNameBufferSize));
1293 }
1294
1295 aOffset = startOffset;
1296 error = SkipRecord(aMessage, aOffset);
1297
1298 exit:
1299 return error;
1300 }
1301
ReadTxtData(const Message & aMessage,uint16_t & aOffset,uint8_t * aTxtBuffer,uint16_t & aTxtBufferSize) const1302 Error TxtRecord::ReadTxtData(const Message &aMessage,
1303 uint16_t &aOffset,
1304 uint8_t *aTxtBuffer,
1305 uint16_t &aTxtBufferSize) const
1306 {
1307 Error error = kErrorNone;
1308
1309 SuccessOrExit(error = aMessage.Read(aOffset, aTxtBuffer, Min(GetLength(), aTxtBufferSize)));
1310 aOffset += GetLength();
1311
1312 VerifyOrExit(GetLength() <= aTxtBufferSize, error = kErrorNoBufs);
1313 aTxtBufferSize = GetLength();
1314 VerifyOrExit(VerifyTxtData(aTxtBuffer, aTxtBufferSize, /* aAllowEmpty */ true), error = kErrorParse);
1315
1316 exit:
1317 return error;
1318 }
1319
VerifyTxtData(const uint8_t * aTxtData,uint16_t aTxtLength,bool aAllowEmpty)1320 bool TxtRecord::VerifyTxtData(const uint8_t *aTxtData, uint16_t aTxtLength, bool aAllowEmpty)
1321 {
1322 bool valid = false;
1323 uint8_t curEntryLength = 0;
1324
1325 // Per RFC 1035, TXT-DATA MUST have one or more <character-string>s.
1326 VerifyOrExit(aAllowEmpty || aTxtLength > 0);
1327
1328 for (uint16_t i = 0; i < aTxtLength; ++i)
1329 {
1330 if (curEntryLength == 0)
1331 {
1332 curEntryLength = aTxtData[i];
1333 }
1334 else
1335 {
1336 --curEntryLength;
1337 }
1338 }
1339
1340 valid = (curEntryLength == 0);
1341
1342 exit:
1343 return valid;
1344 }
1345
1346 } // namespace Dns
1347 } // namespace ot
1348