1 /*
2  *  Copyright (c) 2016, 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 the CoAP message generation and parsing.
32  */
33 
34 #include "coap_message.hpp"
35 
36 #include "coap/coap.hpp"
37 #include "common/array.hpp"
38 #include "common/code_utils.hpp"
39 #include "common/debug.hpp"
40 #include "common/encoding.hpp"
41 #include "common/random.hpp"
42 #include "common/string.hpp"
43 #include "instance/instance.hpp"
44 
45 namespace ot {
46 namespace Coap {
47 
Init(void)48 void Message::Init(void)
49 {
50     GetHelpData().Clear();
51     SetVersion(kVersion1);
52     SetOffset(0);
53     GetHelpData().mHeaderLength = kMinHeaderLength;
54 
55     IgnoreError(SetLength(GetHelpData().mHeaderLength));
56 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
57     SetBlockWiseBlockNumber(0);
58     SetMoreBlocksFlag(false);
59     SetBlockWiseBlockSize(OT_COAP_OPTION_BLOCK_SZX_16);
60 #endif
61 }
62 
Init(Type aType,Code aCode)63 void Message::Init(Type aType, Code aCode)
64 {
65     Init();
66     SetType(aType);
67     SetCode(aCode);
68 }
69 
Init(Type aType,Code aCode,Uri aUri)70 Error Message::Init(Type aType, Code aCode, Uri aUri)
71 {
72     Error error;
73 
74     Init(aType, aCode);
75     SuccessOrExit(error = GenerateRandomToken(kDefaultTokenLength));
76     SuccessOrExit(error = AppendUriPathOptions(PathForUri(aUri)));
77 
78 exit:
79     return error;
80 }
81 
InitAsPost(const Ip6::Address & aDestination,Uri aUri)82 Error Message::InitAsPost(const Ip6::Address &aDestination, Uri aUri)
83 {
84     return Init(aDestination.IsMulticast() ? kTypeNonConfirmable : kTypeConfirmable, kCodePost, aUri);
85 }
86 
IsConfirmablePostRequest(void) const87 bool Message::IsConfirmablePostRequest(void) const { return IsConfirmable() && IsPostRequest(); }
88 
IsNonConfirmablePostRequest(void) const89 bool Message::IsNonConfirmablePostRequest(void) const { return IsNonConfirmable() && IsPostRequest(); }
90 
Finish(void)91 void Message::Finish(void)
92 {
93     // If the payload marker is set but the message contains no
94     // payload, we remove the payload marker from the message. Note
95     // that the presence of a marker followed by a zero-length payload
96     // will be processed as a message format error on the receiver.
97 
98     if (GetHelpData().mPayloadMarkerSet && (GetHelpData().mHeaderLength == GetLength()))
99     {
100         IgnoreError(SetLength(GetLength() - 1));
101     }
102 
103     WriteBytes(0, &GetHelpData().mHeader, GetOptionStart());
104 }
105 
WriteExtendedOptionField(uint16_t aValue,uint8_t * & aBuffer)106 uint8_t Message::WriteExtendedOptionField(uint16_t aValue, uint8_t *&aBuffer)
107 {
108     /*
109      * Encodes a CoAP Option header field (Option Delta/Length) per
110      * RFC 7252. The returned value is a 4-bit unsigned integer. Extended fields
111      * (if needed) are written into the given buffer `aBuffer` and the pointer
112      * would also be updated.
113      *
114      * If `aValue < 13 (kOption1ByteExtensionOffset)`, it is returned as is
115      * (no extension).
116      *
117      * If `13 <= aValue < 269 (kOption2ByteExtensionOffset)`, one-byte
118      * extension is used, and the value minus 13 is written in `aBuffer` as an
119      * 8-bit unsigned integer, and `13 (kOption1ByteExtension)` is returned.
120      *
121      * If `269 <= aValue`, two-byte extension is used and the value minis 269
122      * is written as a 16-bit unsigned integer and `14 (kOption2ByteExtension)`
123      * is returned.
124      *
125      */
126 
127     uint8_t rval;
128 
129     if (aValue < kOption1ByteExtensionOffset)
130     {
131         rval = static_cast<uint8_t>(aValue);
132     }
133     else if (aValue < kOption2ByteExtensionOffset)
134     {
135         rval     = kOption1ByteExtension;
136         *aBuffer = static_cast<uint8_t>(aValue - kOption1ByteExtensionOffset);
137         aBuffer += sizeof(uint8_t);
138     }
139     else
140     {
141         rval = kOption2ByteExtension;
142         BigEndian::WriteUint16(aValue - kOption2ByteExtensionOffset, aBuffer);
143         aBuffer += sizeof(uint16_t);
144     }
145 
146     return rval;
147 }
148 
AppendOption(uint16_t aNumber,uint16_t aLength,const void * aValue)149 Error Message::AppendOption(uint16_t aNumber, uint16_t aLength, const void *aValue)
150 {
151     Error    error = kErrorNone;
152     uint16_t delta;
153     uint8_t  header[kMaxOptionHeaderSize];
154     uint16_t headerLength;
155     uint8_t *cur;
156 
157     VerifyOrExit(aNumber >= GetHelpData().mOptionLast, error = kErrorInvalidArgs);
158     delta = aNumber - GetHelpData().mOptionLast;
159 
160     cur = &header[1];
161 
162     header[0] = static_cast<uint8_t>(WriteExtendedOptionField(delta, cur) << kOptionDeltaOffset);
163     header[0] |= static_cast<uint8_t>(WriteExtendedOptionField(aLength, cur) << kOptionLengthOffset);
164 
165     headerLength = static_cast<uint16_t>(cur - header);
166 
167     VerifyOrExit(static_cast<uint32_t>(GetLength()) + headerLength + aLength < kMaxHeaderLength, error = kErrorNoBufs);
168 
169     SuccessOrExit(error = AppendBytes(header, headerLength));
170     SuccessOrExit(error = AppendBytes(aValue, aLength));
171 
172     GetHelpData().mOptionLast = aNumber;
173 
174     GetHelpData().mHeaderLength = GetLength();
175 
176 exit:
177     return error;
178 }
179 
AppendUintOption(uint16_t aNumber,uint32_t aValue)180 Error Message::AppendUintOption(uint16_t aNumber, uint32_t aValue)
181 {
182     uint8_t        buffer[sizeof(uint32_t)];
183     const uint8_t *value  = &buffer[0];
184     uint16_t       length = sizeof(uint32_t);
185 
186     BigEndian::WriteUint32(aValue, buffer);
187 
188     while ((length > 0) && (value[0] == 0))
189     {
190         value++;
191         length--;
192     }
193 
194     return AppendOption(aNumber, length, value);
195 }
196 
AppendStringOption(uint16_t aNumber,const char * aValue)197 Error Message::AppendStringOption(uint16_t aNumber, const char *aValue)
198 {
199     return AppendOption(aNumber, static_cast<uint16_t>(strlen(aValue)), aValue);
200 }
201 
AppendUriPathOptions(const char * aUriPath)202 Error Message::AppendUriPathOptions(const char *aUriPath)
203 {
204     Error       error = kErrorNone;
205     const char *cur   = aUriPath;
206     const char *end;
207 
208     while ((end = StringFind(cur, '/')) != nullptr)
209     {
210         SuccessOrExit(error = AppendOption(kOptionUriPath, static_cast<uint16_t>(end - cur), cur));
211         cur = end + 1;
212     }
213 
214     SuccessOrExit(error = AppendStringOption(kOptionUriPath, cur));
215 
216 exit:
217     return error;
218 }
219 
ReadUriPathOptions(char (& aUriPath)[kMaxReceivedUriPath+1]) const220 Error Message::ReadUriPathOptions(char (&aUriPath)[kMaxReceivedUriPath + 1]) const
221 {
222     char            *curUriPath = aUriPath;
223     Error            error      = kErrorNone;
224     Option::Iterator iterator;
225 
226     SuccessOrExit(error = iterator.Init(*this, kOptionUriPath));
227 
228     while (!iterator.IsDone())
229     {
230         uint16_t optionLength = iterator.GetOption()->GetLength();
231 
232         if (curUriPath != aUriPath)
233         {
234             *curUriPath++ = '/';
235         }
236 
237         VerifyOrExit(curUriPath + optionLength < GetArrayEnd(aUriPath), error = kErrorParse);
238 
239         IgnoreError(iterator.ReadOptionValue(curUriPath));
240         curUriPath += optionLength;
241 
242         SuccessOrExit(error = iterator.Advance(kOptionUriPath));
243     }
244 
245     *curUriPath = '\0';
246 
247 exit:
248     return error;
249 }
250 
AppendBlockOption(Message::BlockType aType,uint32_t aNum,bool aMore,otCoapBlockSzx aSize)251 Error Message::AppendBlockOption(Message::BlockType aType, uint32_t aNum, bool aMore, otCoapBlockSzx aSize)
252 {
253     Error    error   = kErrorNone;
254     uint32_t encoded = aSize;
255 
256     VerifyOrExit(aType == kBlockType1 || aType == kBlockType2, error = kErrorInvalidArgs);
257     VerifyOrExit(aSize <= OT_COAP_OPTION_BLOCK_SZX_1024, error = kErrorInvalidArgs);
258     VerifyOrExit(aNum < kBlockNumMax, error = kErrorInvalidArgs);
259 
260     encoded |= static_cast<uint32_t>(aMore << kBlockMOffset);
261     encoded |= aNum << kBlockNumOffset;
262 
263     error = AppendUintOption((aType == kBlockType1) ? kOptionBlock1 : kOptionBlock2, encoded);
264 
265 exit:
266     return error;
267 }
268 
269 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
ReadBlockOptionValues(uint16_t aBlockType)270 Error Message::ReadBlockOptionValues(uint16_t aBlockType)
271 {
272     Error            error                     = kErrorNone;
273     uint8_t          buf[kMaxOptionHeaderSize] = {0};
274     Option::Iterator iterator;
275 
276     VerifyOrExit((aBlockType == kOptionBlock1) || (aBlockType == kOptionBlock2), error = kErrorInvalidArgs);
277 
278     SuccessOrExit(error = iterator.Init(*this, aBlockType));
279     SuccessOrExit(error = iterator.ReadOptionValue(buf));
280 
281     SetBlockWiseBlockNumber(0);
282     SetMoreBlocksFlag(false);
283 
284     switch (iterator.GetOption()->GetLength())
285     {
286     case 0:
287     case 1:
288         SetBlockWiseBlockNumber(static_cast<uint32_t>((buf[0] & 0xf0) >> 4));
289         SetMoreBlocksFlag(static_cast<bool>((buf[0] & 0x08) >> 3 == 1));
290         SetBlockWiseBlockSize(static_cast<otCoapBlockSzx>(buf[0] & 0x07));
291         break;
292     case 2:
293         SetBlockWiseBlockNumber(static_cast<uint32_t>((buf[0] << 4) + ((buf[1] & 0xf0) >> 4)));
294         SetMoreBlocksFlag(static_cast<bool>((buf[1] & 0x08) >> 3 == 1));
295         SetBlockWiseBlockSize(static_cast<otCoapBlockSzx>(buf[1] & 0x07));
296         break;
297     case 3:
298         SetBlockWiseBlockNumber(static_cast<uint32_t>((buf[0] << 12) + (buf[1] << 4) + ((buf[2] & 0xf0) >> 4)));
299         SetMoreBlocksFlag(static_cast<bool>((buf[2] & 0x08) >> 3 == 1));
300         SetBlockWiseBlockSize(static_cast<otCoapBlockSzx>(buf[2] & 0x07));
301         break;
302     default:
303         error = kErrorInvalidArgs;
304         break;
305     }
306 
307 exit:
308     return error;
309 }
310 #endif // OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
311 
SetPayloadMarker(void)312 Error Message::SetPayloadMarker(void)
313 {
314     Error   error  = kErrorNone;
315     uint8_t marker = kPayloadMarker;
316 
317     VerifyOrExit(GetLength() < kMaxHeaderLength, error = kErrorNoBufs);
318     SuccessOrExit(error = Append(marker));
319     GetHelpData().mPayloadMarkerSet = true;
320     GetHelpData().mHeaderLength     = GetLength();
321 
322     // Set offset to the start of payload.
323     SetOffset(GetHelpData().mHeaderLength);
324 
325 exit:
326     return error;
327 }
328 
ParseHeader(void)329 Error Message::ParseHeader(void)
330 {
331     Error            error = kErrorNone;
332     Option::Iterator iterator;
333 
334     OT_ASSERT(GetReserved() >=
335               sizeof(HelpData) + static_cast<size_t>((reinterpret_cast<uint8_t *>(&GetHelpData()) - GetFirstData())));
336 
337     GetHelpData().Clear();
338 
339     GetHelpData().mHeaderOffset = GetOffset();
340     IgnoreError(Read(GetHelpData().mHeaderOffset, GetHelpData().mHeader));
341 
342     VerifyOrExit(GetTokenLength() <= kMaxTokenLength, error = kErrorParse);
343 
344     SuccessOrExit(error = iterator.Init(*this));
345 
346     while (!iterator.IsDone())
347     {
348         SuccessOrExit(error = iterator.Advance());
349     }
350 
351     GetHelpData().mHeaderLength = iterator.GetPayloadMessageOffset() - GetHelpData().mHeaderOffset;
352     MoveOffset(GetHelpData().mHeaderLength);
353 
354 exit:
355     return error;
356 }
357 
SetToken(const uint8_t * aToken,uint8_t aTokenLength)358 Error Message::SetToken(const uint8_t *aToken, uint8_t aTokenLength)
359 {
360     OT_ASSERT(aTokenLength <= kMaxTokenLength);
361 
362     SetTokenLength(aTokenLength);
363     memcpy(GetToken(), aToken, aTokenLength);
364     GetHelpData().mHeaderLength += aTokenLength;
365 
366     return SetLength(GetHelpData().mHeaderLength);
367 }
368 
GenerateRandomToken(uint8_t aTokenLength)369 Error Message::GenerateRandomToken(uint8_t aTokenLength)
370 {
371     uint8_t token[kMaxTokenLength];
372 
373     OT_ASSERT(aTokenLength <= sizeof(token));
374 
375     IgnoreError(Random::Crypto::FillBuffer(token, aTokenLength));
376 
377     return SetToken(token, aTokenLength);
378 }
379 
SetTokenFromMessage(const Message & aMessage)380 Error Message::SetTokenFromMessage(const Message &aMessage)
381 {
382     return SetToken(aMessage.GetToken(), aMessage.GetTokenLength());
383 }
384 
IsTokenEqual(const Message & aMessage) const385 bool Message::IsTokenEqual(const Message &aMessage) const
386 {
387     uint8_t tokenLength = GetTokenLength();
388 
389     return ((tokenLength == aMessage.GetTokenLength()) && (memcmp(GetToken(), aMessage.GetToken(), tokenLength) == 0));
390 }
391 
SetDefaultResponseHeader(const Message & aRequest)392 Error Message::SetDefaultResponseHeader(const Message &aRequest)
393 {
394     Init(kTypeAck, kCodeChanged);
395 
396     SetMessageId(aRequest.GetMessageId());
397 
398     return SetTokenFromMessage(aRequest);
399 }
400 
Clone(uint16_t aLength) const401 Message *Message::Clone(uint16_t aLength) const
402 {
403     Message *message = static_cast<Message *>(ot::Message::Clone(aLength));
404 
405     VerifyOrExit(message != nullptr);
406 
407     message->GetHelpData() = GetHelpData();
408 
409 exit:
410     return message;
411 }
412 
413 #if OPENTHREAD_CONFIG_COAP_API_ENABLE
CodeToString(void) const414 const char *Message::CodeToString(void) const
415 {
416     static constexpr Stringify::Entry kCodeTable[] = {
417         {kCodeEmpty, "Empty"},
418         {kCodeGet, "Get"},
419         {kCodePost, "Post"},
420         {kCodePut, "Put"},
421         {kCodeDelete, "Delete"},
422         {kCodeCreated, "Created"},
423         {kCodeDeleted, "Deleted"},
424         {kCodeValid, "Valid"},
425         {kCodeChanged, "Changed"},
426         {kCodeContent, "Content"},
427         {kCodeContinue, "Continue"},
428         {kCodeBadRequest, "BadRequest"},
429         {kCodeUnauthorized, "Unauthorized"},
430         {kCodeBadOption, "BadOption"},
431         {kCodeForbidden, "Forbidden"},
432         {kCodeNotFound, "NotFound"},
433         {kCodeMethodNotAllowed, "MethodNotAllowed"},
434         {kCodeNotAcceptable, "NotAcceptable"},
435         {kCodeRequestIncomplete, "RequestIncomplete"},
436         {kCodePreconditionFailed, "PreconditionFailed"},
437         {kCodeRequestTooLarge, "RequestTooLarge"},
438         {kCodeUnsupportedFormat, "UnsupportedFormat"},
439         {kCodeInternalError, "InternalError"},
440         {kCodeNotImplemented, "NotImplemented"},
441         {kCodeBadGateway, "BadGateway"},
442         {kCodeServiceUnavailable, "ServiceUnavailable"},
443         {kCodeGatewayTimeout, "GatewayTimeout"},
444         {kCodeProxyNotSupported, "ProxyNotSupported"},
445     };
446 
447     static_assert(Stringify::IsSorted(kCodeTable), "kCodeTable is not sorted");
448 
449     return Stringify::Lookup(GetCode(), kCodeTable, "Unknown");
450 }
451 #endif // OPENTHREAD_CONFIG_COAP_API_ENABLE
452 
begin(void)453 Message::Iterator MessageQueue::begin(void) { return Message::Iterator(GetHead()); }
454 
begin(void) const455 Message::ConstIterator MessageQueue::begin(void) const { return Message::ConstIterator(GetHead()); }
456 
Init(const Message & aMessage)457 Error Option::Iterator::Init(const Message &aMessage)
458 {
459     Error    error  = kErrorParse;
460     uint32_t offset = static_cast<uint32_t>(aMessage.GetHelpData().mHeaderOffset) + aMessage.GetOptionStart();
461 
462     // Note that the case where `offset == aMessage.GetLength())` is
463     // valid and indicates an empty payload (no CoAP Option and no
464     // Payload Marker).
465 
466     VerifyOrExit(offset <= aMessage.GetLength(), MarkAsParseErrored());
467 
468     mOption.mNumber   = 0;
469     mOption.mLength   = 0;
470     mMessage          = &aMessage;
471     mNextOptionOffset = static_cast<uint16_t>(offset);
472 
473     error = Advance();
474 
475 exit:
476     return error;
477 }
478 
Advance(void)479 Error Option::Iterator::Advance(void)
480 {
481     Error    error = kErrorNone;
482     uint8_t  headerByte;
483     uint16_t optionDelta;
484     uint16_t optionLength;
485 
486     VerifyOrExit(!IsDone());
487 
488     error = Read(sizeof(uint8_t), &headerByte);
489 
490     if ((error != kErrorNone) || (headerByte == Message::kPayloadMarker))
491     {
492         // Payload Marker indicates end of options and start of payload.
493         // Absence of a Payload Marker indicates a zero-length payload.
494 
495         MarkAsDone();
496 
497         if (error == kErrorNone)
498         {
499             // The presence of a marker followed by a zero-length payload
500             // MUST be processed as a message format error.
501 
502             VerifyOrExit(mNextOptionOffset < GetMessage().GetLength(), error = kErrorParse);
503         }
504 
505         ExitNow(error = kErrorNone);
506     }
507 
508     optionDelta = (headerByte & Message::kOptionDeltaMask) >> Message::kOptionDeltaOffset;
509     SuccessOrExit(error = ReadExtendedOptionField(optionDelta));
510 
511     optionLength = (headerByte & Message::kOptionLengthMask) >> Message::kOptionLengthOffset;
512     SuccessOrExit(error = ReadExtendedOptionField(optionLength));
513 
514     VerifyOrExit(optionLength <= GetMessage().GetLength() - mNextOptionOffset, error = kErrorParse);
515     mNextOptionOffset += optionLength;
516 
517     mOption.mNumber += optionDelta;
518     mOption.mLength = optionLength;
519 
520 exit:
521     if (error != kErrorNone)
522     {
523         MarkAsParseErrored();
524     }
525 
526     return error;
527 }
528 
ReadOptionValue(void * aValue) const529 Error Option::Iterator::ReadOptionValue(void *aValue) const
530 {
531     Error error = kErrorNone;
532 
533     VerifyOrExit(!IsDone(), error = kErrorNotFound);
534     GetMessage().ReadBytes(mNextOptionOffset - mOption.mLength, aValue, mOption.mLength);
535 
536 exit:
537     return error;
538 }
539 
ReadOptionValue(uint64_t & aUintValue) const540 Error Option::Iterator::ReadOptionValue(uint64_t &aUintValue) const
541 {
542     Error   error = kErrorNone;
543     uint8_t buffer[sizeof(uint64_t)];
544 
545     VerifyOrExit(!IsDone(), error = kErrorNotFound);
546 
547     VerifyOrExit(mOption.mLength <= sizeof(uint64_t), error = kErrorNoBufs);
548     IgnoreError(ReadOptionValue(buffer));
549 
550     aUintValue = 0;
551 
552     for (uint16_t pos = 0; pos < mOption.mLength; pos++)
553     {
554         aUintValue <<= kBitsPerByte;
555         aUintValue |= buffer[pos];
556     }
557 
558 exit:
559     return error;
560 }
561 
Read(uint16_t aLength,void * aBuffer)562 Error Option::Iterator::Read(uint16_t aLength, void *aBuffer)
563 {
564     // Reads `aLength` bytes from the message into `aBuffer` at
565     // `mNextOptionOffset` and updates the `mNextOptionOffset` on a
566     // successful read (i.e., when entire `aLength` bytes can be read).
567 
568     Error error = kErrorNone;
569 
570     SuccessOrExit(error = GetMessage().Read(mNextOptionOffset, aBuffer, aLength));
571     mNextOptionOffset += aLength;
572 
573 exit:
574     return error;
575 }
576 
ReadExtendedOptionField(uint16_t & aValue)577 Error Option::Iterator::ReadExtendedOptionField(uint16_t &aValue)
578 {
579     Error error = kErrorNone;
580 
581     VerifyOrExit(aValue >= Message::kOption1ByteExtension);
582 
583     if (aValue == Message::kOption1ByteExtension)
584     {
585         uint8_t value8;
586 
587         SuccessOrExit(error = Read(sizeof(uint8_t), &value8));
588         aValue = static_cast<uint16_t>(value8) + Message::kOption1ByteExtensionOffset;
589     }
590     else if (aValue == Message::kOption2ByteExtension)
591     {
592         uint16_t value16;
593 
594         SuccessOrExit(error = Read(sizeof(uint16_t), &value16));
595         value16 = BigEndian::HostSwap16(value16);
596         aValue  = value16 + Message::kOption2ByteExtensionOffset;
597     }
598     else
599     {
600         error = kErrorParse;
601     }
602 
603 exit:
604     return error;
605 }
606 
InitOrAdvance(const Message * aMessage,uint16_t aNumber)607 Error Option::Iterator::InitOrAdvance(const Message *aMessage, uint16_t aNumber)
608 {
609     Error error = (aMessage != nullptr) ? Init(*aMessage) : Advance();
610 
611     while ((error == kErrorNone) && !IsDone() && (GetOption()->GetNumber() != aNumber))
612     {
613         error = Advance();
614     }
615 
616     return error;
617 }
618 
619 } // namespace Coap
620 } // namespace ot
621