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