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