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