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,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 * This method 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 Encoding::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 Encoding::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 <<= CHAR_BIT;
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 = Encoding::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