[coap] update/simplify CoAP Options generation/processing (#5503)

This commit simplifies the generation/processing of CoAP Options in a
CoAP message. It adds helper methods `ReadExtendedOptionField()`,
`WriteExtendedOptionField` to decode/encode extended Option Header
fields (Option Number Delta or Option Length). It simplifies
`Option::Iterator` (using `Init()`/`Advance()` to parse and iterate
through all CoAP Options in a message). Note that the public OT
`otCoap` APIs and their behavior remain as before and unchanged.
This commit is contained in:
Abtin Keshavarzian
2020-09-11 14:31:12 -07:00
committed by GitHub
parent 5892a52b89
commit 11faac0aad
4 changed files with 423 additions and 350 deletions
+19 -7
View File
@@ -169,37 +169,49 @@ const uint8_t *otCoapMessageGetToken(const otMessage *aMessage)
otError otCoapOptionIteratorInit(otCoapOptionIterator *aIterator, const otMessage *aMessage)
{
return static_cast<Coap::OptionIterator *>(aIterator)->Init(static_cast<const Coap::Message *>(aMessage));
return static_cast<Coap::Option::Iterator *>(aIterator)->Init(*static_cast<const Coap::Message *>(aMessage));
}
const otCoapOption *otCoapOptionIteratorGetFirstOptionMatching(otCoapOptionIterator *aIterator, uint16_t aOption)
{
return static_cast<Coap::OptionIterator *>(aIterator)->GetFirstOptionMatching(aOption);
Coap::Option::Iterator &iterator = *static_cast<Coap::Option::Iterator *>(aIterator);
IgnoreError(iterator.Init(iterator.GetMessage(), aOption));
return iterator.GetOption();
}
const otCoapOption *otCoapOptionIteratorGetFirstOption(otCoapOptionIterator *aIterator)
{
return static_cast<Coap::OptionIterator *>(aIterator)->GetFirstOption();
Coap::Option::Iterator &iterator = *static_cast<Coap::Option::Iterator *>(aIterator);
IgnoreError(iterator.Init(iterator.GetMessage()));
return iterator.GetOption();
}
const otCoapOption *otCoapOptionIteratorGetNextOptionMatching(otCoapOptionIterator *aIterator, uint16_t aOption)
{
return static_cast<Coap::OptionIterator *>(aIterator)->GetNextOptionMatching(aOption);
Coap::Option::Iterator &iterator = *static_cast<Coap::Option::Iterator *>(aIterator);
IgnoreError(iterator.Advance(aOption));
return iterator.GetOption();
}
const otCoapOption *otCoapOptionIteratorGetNextOption(otCoapOptionIterator *aIterator)
{
return static_cast<Coap::OptionIterator *>(aIterator)->GetNextOption();
Coap::Option::Iterator &iterator = *static_cast<Coap::Option::Iterator *>(aIterator);
IgnoreError(iterator.Advance());
return iterator.GetOption();
}
otError otCoapOptionIteratorGetOptionUintValue(otCoapOptionIterator *aIterator, uint64_t *const aValue)
{
return static_cast<Coap::OptionIterator *>(aIterator)->GetOptionValue(*aValue);
return static_cast<Coap::Option::Iterator *>(aIterator)->ReadOptionValue(*aValue);
}
otError otCoapOptionIteratorGetOptionValue(otCoapOptionIterator *aIterator, void *aValue)
{
return static_cast<Coap::OptionIterator *>(aIterator)->GetOptionValue(aValue);
return static_cast<Coap::Option::Iterator *>(aIterator)->ReadOptionValue(aValue);
}
otError otCoapSendRequestWithParameters(otInstance * aInstance,
+28 -31
View File
@@ -171,18 +171,18 @@ otError CoapBase::SendMessage(Message & aMessage,
#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
// Whether or not to turn on special "Observe" handling.
OptionIterator iterator;
bool observe;
Option::Iterator iterator;
bool observe;
SuccessOrExit(error = iterator.Init(&aMessage));
observe = (iterator.GetFirstOptionMatching(kOptionObserve) != nullptr);
SuccessOrExit(error = iterator.Init(aMessage, kOptionObserve));
observe = !iterator.IsDone();
// Special case, if we're sending a GET with Observe=1, that is a cancellation.
if (observe && aMessage.IsGetRequest())
{
uint64_t observeVal = 0;
SuccessOrExit(error = iterator.GetOptionValue(observeVal));
SuccessOrExit(error = iterator.ReadOptionValue(observeVal));
if (observeVal == 1)
{
@@ -570,10 +570,10 @@ void CoapBase::ProcessReceivedResponse(Message &aMessage, const Ip6::MessageInfo
if (metadata.mObserve && request->IsRequest())
{
// We sent Observe in our request, see if we received Observe in the response too.
OptionIterator iterator;
Option::Iterator iterator;
SuccessOrExit(error = iterator.Init(&aMessage));
responseObserve = (iterator.GetFirstOptionMatching(kOptionObserve) != nullptr);
SuccessOrExit(error = iterator.Init(aMessage, kOptionObserve));
responseObserve = !iterator.IsDone();
}
#endif
@@ -687,11 +687,11 @@ exit:
void CoapBase::ProcessReceivedRequest(Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
{
char uriPath[Resource::kMaxReceivedUriPath];
char * curUriPath = uriPath;
Message * cachedResponse = nullptr;
otError error = OT_ERROR_NOT_FOUND;
OptionIterator iterator;
char uriPath[Resource::kMaxReceivedUriPath];
char * curUriPath = uriPath;
Message * cachedResponse = nullptr;
otError error = OT_ERROR_NOT_FOUND;
Option::Iterator iterator;
if (mInterceptor != nullptr)
{
@@ -714,29 +714,26 @@ void CoapBase::ProcessReceivedRequest(Message &aMessage, const Ip6::MessageInfo
break;
}
SuccessOrExit(error = iterator.Init(&aMessage));
for (const otCoapOption *option = iterator.GetFirstOption(); option != nullptr; option = iterator.GetNextOption())
SuccessOrExit(error = iterator.Init(aMessage, kOptionUriPath));
while (!iterator.IsDone())
{
switch (option->mNumber)
uint16_t optionLength = iterator.GetOption()->GetLength();
if (curUriPath != uriPath)
{
case kOptionUriPath:
if (curUriPath != uriPath)
{
*curUriPath++ = '/';
}
VerifyOrExit(option->mLength < sizeof(uriPath) - static_cast<size_t>(curUriPath + 1 - uriPath), OT_NOOP);
IgnoreError(iterator.GetOptionValue(curUriPath));
curUriPath += option->mLength;
break;
default:
break;
*curUriPath++ = '/';
}
VerifyOrExit(curUriPath + optionLength < OT_ARRAY_END(uriPath), OT_NOOP);
IgnoreError(iterator.ReadOptionValue(curUriPath));
curUriPath += optionLength;
SuccessOrExit(error = iterator.Advance(kOptionUriPath));
}
curUriPath[0] = '\0';
*curUriPath = '\0';
for (const Resource *resource = mResources.GetHead(); resource; resource = resource->GetNext())
{
+193 -227
View File
@@ -112,60 +112,71 @@ void Message::Finish(void)
Write(0, GetOptionStart(), &GetHelpData().mHeader);
}
uint8_t Message::WriteExtendedOptionField(uint16_t aValue, uint8_t *&aBuffer)
{
/*
* This method encodes a CoAP Option header field (Option Delta/Length) per
* RFC 7252. The returned value is a 4-bit unsigned integer. Extended fields
* (if needed) are written into the given buffer `aBuffer` and the pointer
* would also be updated.
*
* If `aValue < 13 (kOption1ByteExtensionOffset)`, it is returned as is
* (no extension).
*
* If `13 <= aValue < 269 (kOption2ByteExtensionOffset)`, one-byte
* extension is used, and the value minus 13 is written in `aBuffer` as an
* 8-bit unsigned integer, and `13 (kOption1ByteExtension)` is returned.
*
* If `269 <= aValue`, two-byte extension is used and the value minis 269
* is written as a 16-bit unsigned integer and `14 (kOption2ByteExtension)`
* is returned.
*
*/
uint8_t rval;
if (aValue < kOption1ByteExtensionOffset)
{
rval = static_cast<uint8_t>(aValue);
}
else if (aValue < kOption2ByteExtensionOffset)
{
rval = kOption1ByteExtension;
*aBuffer = static_cast<uint8_t>(aValue - kOption1ByteExtensionOffset);
aBuffer += sizeof(uint8_t);
}
else
{
rval = kOption2ByteExtension;
Encoding::BigEndian::WriteUint16(aValue - kOption2ByteExtensionOffset, aBuffer);
aBuffer += sizeof(uint16_t);
}
return rval;
}
otError Message::AppendOption(uint16_t aNumber, uint16_t aLength, const void *aValue)
{
otError error = OT_ERROR_NONE;
uint16_t optionDelta = aNumber - GetHelpData().mOptionLast;
uint16_t optionLength;
uint8_t buf[kMaxOptionHeaderSize] = {0};
uint8_t *cur = &buf[1];
otError error = OT_ERROR_NONE;
uint16_t delta;
uint8_t header[kMaxOptionHeaderSize];
uint16_t headerLength;
uint8_t *cur;
// Assure that no option is inserted out of order.
VerifyOrExit(aNumber >= GetHelpData().mOptionLast, error = OT_ERROR_INVALID_ARGS);
delta = aNumber - GetHelpData().mOptionLast;
// Calculate the total option size and check the buffers.
optionLength = 1 + aLength;
optionLength += optionDelta < kOption1ByteExtensionOffset ? 0 : (optionDelta < kOption2ByteExtensionOffset ? 1 : 2);
optionLength += aLength < kOption1ByteExtensionOffset ? 0 : (aLength < kOption2ByteExtensionOffset ? 1 : 2);
VerifyOrExit(GetLength() + optionLength < kMaxHeaderLength, error = OT_ERROR_NO_BUFS);
cur = &header[1];
// Insert option delta.
if (optionDelta < kOption1ByteExtensionOffset)
{
*buf = (optionDelta << kOptionDeltaOffset) & kOptionDeltaMask;
}
else if (optionDelta < kOption2ByteExtensionOffset)
{
*buf |= kOption1ByteExtension << kOptionDeltaOffset;
*cur++ = (optionDelta - kOption1ByteExtensionOffset) & 0xff;
}
else
{
*buf |= kOption2ByteExtension << kOptionDeltaOffset;
optionDelta -= kOption2ByteExtensionOffset;
*cur++ = optionDelta >> 8;
*cur++ = optionDelta & 0xff;
}
header[0] = static_cast<uint8_t>(WriteExtendedOptionField(delta, cur) << kOptionDeltaOffset);
header[0] |= static_cast<uint8_t>(WriteExtendedOptionField(aLength, cur) << kOptionLengthOffset);
// Insert option length.
if (aLength < kOption1ByteExtensionOffset)
{
*buf |= aLength;
}
else if (aLength < kOption2ByteExtensionOffset)
{
*buf |= kOption1ByteExtension;
*cur++ = (aLength - kOption1ByteExtensionOffset) & 0xff;
}
else
{
*buf |= kOption2ByteExtension;
optionLength = aLength - kOption2ByteExtensionOffset;
*cur++ = optionLength >> 8;
*cur++ = optionLength & 0xff;
}
headerLength = static_cast<uint16_t>(cur - header);
SuccessOrExit(error = Append(buf, static_cast<uint16_t>(cur - buf)));
VerifyOrExit(static_cast<uint32_t>(GetLength()) + headerLength + aLength < kMaxHeaderLength,
error = OT_ERROR_NO_BUFS);
SuccessOrExit(error = Append(header, headerLength));
SuccessOrExit(error = Append(aValue, aLength));
GetHelpData().mOptionLast = aNumber;
@@ -178,13 +189,12 @@ exit:
otError Message::AppendUintOption(uint16_t aNumber, uint32_t aValue)
{
uint16_t length = sizeof(aValue);
uint8_t *value;
uint8_t buffer[sizeof(uint32_t)];
const uint8_t *value = &buffer[0];
uint16_t length = sizeof(uint32_t);
aValue = Encoding::BigEndian::HostSwap32(aValue);
value = reinterpret_cast<uint8_t *>(&aValue);
Encoding::BigEndian::WriteUint32(aValue, buffer);
// skip preceding zeros
while (value[0] == 0 && length > 0)
{
value++;
@@ -199,11 +209,6 @@ otError Message::AppendStringOption(uint16_t aNumber, const char *aValue)
return AppendOption(aNumber, static_cast<uint16_t>(strlen(aValue)), aValue);
}
otError Message::AppendObserveOption(uint32_t aObserve)
{
return AppendUintOption(kOptionObserve, aObserve & 0xFFFFFF);
}
otError Message::AppendUriPathOptions(const char *aUriPath)
{
otError error = OT_ERROR_NONE;
@@ -240,30 +245,10 @@ exit:
return error;
}
otError Message::AppendProxyUriOption(const char *aProxyUri)
{
return AppendStringOption(kOptionProxyUri, aProxyUri);
}
otError Message::AppendContentFormatOption(otCoapOptionContentFormat aContentFormat)
{
return AppendUintOption(kOptionContentFormat, static_cast<uint32_t>(aContentFormat));
}
otError Message::AppendMaxAgeOption(uint32_t aMaxAge)
{
return AppendUintOption(kOptionMaxAge, aMaxAge);
}
otError Message::AppendUriQueryOption(const char *aUriQuery)
{
return AppendStringOption(kOptionUriQuery, aUriQuery);
}
otError Message::SetPayloadMarker(void)
{
otError error = OT_ERROR_NONE;
uint8_t marker = 0xff;
uint8_t marker = kPayloadMarker;
VerifyOrExit(GetLength() < kMaxHeaderLength, error = OT_ERROR_NO_BUFS);
SuccessOrExit(error = Append(&marker, sizeof(marker)));
@@ -278,8 +263,8 @@ exit:
otError Message::ParseHeader(void)
{
otError error = OT_ERROR_NONE;
OptionIterator iterator;
otError error = OT_ERROR_NONE;
Option::Iterator iterator;
OT_ASSERT(mBuffer.mHead.mMetadata.mReserved >=
sizeof(GetHelpData()) +
@@ -292,13 +277,14 @@ otError Message::ParseHeader(void)
VerifyOrExit(GetTokenLength() <= kMaxTokenLength, error = OT_ERROR_PARSE);
SuccessOrExit(error = iterator.Init(this));
for (const otCoapOption *option = iterator.GetFirstOption(); option != nullptr; option = iterator.GetNextOption())
SuccessOrExit(error = iterator.Init(*this));
while (!iterator.IsDone())
{
SuccessOrExit(error = iterator.Advance());
}
VerifyOrExit(iterator.mNextOptionOffset > 0, error = OT_ERROR_PARSE);
GetHelpData().mHeaderLength = iterator.mNextOptionOffset - GetHelpData().mHeaderOffset;
GetHelpData().mHeaderLength = iterator.GetPayloadMessageOffset() - GetHelpData().mHeaderOffset;
MoveOffset(GetHelpData().mHeaderLength);
exit:
@@ -327,6 +313,13 @@ otError Message::SetToken(uint8_t aTokenLength)
return SetToken(token, aTokenLength);
}
bool Message::IsTokenEqual(const Message &aMessage) const
{
uint8_t tokenLength = GetTokenLength();
return ((tokenLength == aMessage.GetTokenLength()) && (memcmp(GetToken(), aMessage.GetToken(), tokenLength) == 0));
}
otError Message::SetDefaultResponseHeader(const Message &aRequest)
{
Init(kTypeAck, kCodeChanged);
@@ -448,194 +441,167 @@ const char *Message::CodeToString(void) const
}
#endif // OPENTHREAD_CONFIG_COAP_API_ENABLE
otError OptionIterator::Init(const Message *aMessage)
otError Option::Iterator::Init(const Message &aMessage)
{
otError err = OT_ERROR_NONE;
otError error = OT_ERROR_PARSE;
uint32_t offset = static_cast<uint32_t>(aMessage.GetHelpData().mHeaderOffset) + aMessage.GetOptionStart();
/*
* Check that:
* Length - Offset: the length of the payload
* is greater than:
* Start position of options
*
* → Check options start before the message ends, or bail ::Init with
* OT_ERROR_PARSE as the reason.
*/
VerifyOrExit(aMessage->GetLength() - aMessage->GetHelpData().mHeaderOffset >= aMessage->GetOptionStart(),
err = OT_ERROR_PARSE);
// Note that the case where `offset == aMessage.GetLength())` is
// valid and indicates an empty payload (no CoAP Option and no
// Payload Marker).
mMessage = aMessage;
GetFirstOption();
VerifyOrExit(offset <= aMessage.GetLength(), MarkAsParseErrored());
mOption.mNumber = 0;
mOption.mLength = 0;
mMessage = &aMessage;
mNextOptionOffset = static_cast<uint16_t>(offset);
error = Advance();
exit:
return err;
return error;
}
const otCoapOption *OptionIterator::GetFirstOptionMatching(uint16_t aOption)
otError Option::Iterator::Advance(void)
{
const otCoapOption *rval = nullptr;
otError error = OT_ERROR_NONE;
uint8_t headerByte;
uint16_t optionDelta;
uint16_t optionLength;
for (const otCoapOption *option = GetFirstOption(); option != nullptr; option = GetNextOption())
VerifyOrExit(!IsDone(), OT_NOOP);
error = Read(sizeof(uint8_t), &headerByte);
if ((error != OT_ERROR_NONE) || (headerByte == Message::kPayloadMarker))
{
if (option->mNumber == aOption)
// Payload Marker indicates end of options and start of payload.
// Absence of a Payload Marker indicates a zero-length payload.
MarkAsDone();
if (error == OT_ERROR_NONE)
{
// Found, stop searching
rval = option;
break;
// The presence of a marker followed by a zero-length payload
// MUST be processed as a message format error.
VerifyOrExit(mNextOptionOffset < GetMessage().GetLength(), error = OT_ERROR_PARSE);
}
ExitNow(error = OT_ERROR_NONE);
}
return rval;
}
optionDelta = (headerByte & Message::kOptionDeltaMask) >> Message::kOptionDeltaOffset;
SuccessOrExit(error = ReadExtendedOptionField(optionDelta));
const otCoapOption *OptionIterator::GetFirstOption(void)
{
const otCoapOption *option = nullptr;
const Message & message = GetMessage();
ClearOption();
mNextOptionOffset = message.GetHelpData().mHeaderOffset + message.GetOptionStart();
if (mNextOptionOffset < message.GetLength())
{
option = GetNextOption();
}
return option;
}
const otCoapOption *OptionIterator::GetNextOptionMatching(uint16_t aOption)
{
const otCoapOption *rval = nullptr;
for (const otCoapOption *option = GetNextOption(); option != nullptr; option = GetNextOption())
{
if (option->mNumber == aOption)
{
// Found, stop searching
rval = option;
break;
}
}
return rval;
}
const otCoapOption *OptionIterator::GetNextOption(void)
{
otError error = OT_ERROR_NONE;
uint16_t optionDelta;
uint16_t optionLength;
uint8_t buf[Message::kMaxOptionHeaderSize];
uint8_t * cur = buf + 1;
otCoapOption * rval = nullptr;
const Message &message = GetMessage();
VerifyOrExit(mNextOptionOffset < message.GetLength(), error = OT_ERROR_NOT_FOUND);
message.Read(mNextOptionOffset, sizeof(buf), buf);
optionDelta = buf[0] >> 4;
optionLength = buf[0] & 0xf;
mNextOptionOffset += sizeof(uint8_t);
if (optionDelta < Message::kOption1ByteExtension)
{
// do nothing
}
else if (optionDelta == Message::kOption1ByteExtension)
{
optionDelta = Message::kOption1ByteExtensionOffset + cur[0];
mNextOptionOffset += sizeof(uint8_t);
cur++;
}
else if (optionDelta == Message::kOption2ByteExtension)
{
optionDelta = Message::kOption2ByteExtensionOffset + static_cast<uint16_t>((cur[0] << 8) | cur[1]);
mNextOptionOffset += sizeof(uint16_t);
cur += 2;
}
else
{
// RFC7252 (Section 3):
// Reserved for payload marker.
VerifyOrExit(optionLength == 0xf, error = OT_ERROR_PARSE);
// The presence of a marker followed by a zero-length payload MUST be processed
// as a message format error.
VerifyOrExit(mNextOptionOffset < message.GetLength(), error = OT_ERROR_PARSE);
ExitNow(error = OT_ERROR_NOT_FOUND);
}
if (optionLength < Message::kOption1ByteExtension)
{
// do nothing
}
else if (optionLength == Message::kOption1ByteExtension)
{
optionLength = Message::kOption1ByteExtensionOffset + cur[0];
mNextOptionOffset += sizeof(uint8_t);
}
else if (optionLength == Message::kOption2ByteExtension)
{
optionLength = Message::kOption2ByteExtensionOffset + static_cast<uint16_t>((cur[0] << 8) | cur[1]);
mNextOptionOffset += sizeof(uint16_t);
}
else
{
ExitNow(error = OT_ERROR_PARSE);
}
VerifyOrExit(optionLength <= message.GetLength() - mNextOptionOffset, error = OT_ERROR_PARSE);
rval = &mOption;
rval->mNumber += optionDelta;
rval->mLength = optionLength;
optionLength = (headerByte & Message::kOptionLengthMask) >> Message::kOptionLengthOffset;
SuccessOrExit(error = ReadExtendedOptionField(optionLength));
VerifyOrExit(optionLength <= GetMessage().GetLength() - mNextOptionOffset, error = OT_ERROR_PARSE);
mNextOptionOffset += optionLength;
mOption.mNumber += optionDelta;
mOption.mLength = optionLength;
exit:
if (error == OT_ERROR_PARSE)
if (error != OT_ERROR_NONE)
{
mNextOptionOffset = 0;
MarkAsParseErrored();
}
return rval;
return error;
}
otError OptionIterator::GetOptionValue(uint64_t &aValue) const
otError Option::Iterator::ReadOptionValue(void *aValue) const
{
otError error = OT_ERROR_NONE;
uint8_t value[sizeof(aValue)];
VerifyOrExit(mOption.mLength <= sizeof(aValue), error = OT_ERROR_NO_BUFS);
SuccessOrExit(error = GetOptionValue(value));
VerifyOrExit(!IsDone(), error = OT_ERROR_NOT_FOUND);
GetMessage().Read(mNextOptionOffset - mOption.mLength, mOption.mLength, aValue);
exit:
return error;
}
otError Option::Iterator::ReadOptionValue(uint64_t &aUintValue) const
{
otError error = OT_ERROR_NONE;
uint8_t buffer[sizeof(uint64_t)];
VerifyOrExit(!IsDone(), error = OT_ERROR_NOT_FOUND);
VerifyOrExit(mOption.mLength <= sizeof(uint64_t), error = OT_ERROR_NO_BUFS);
IgnoreError(ReadOptionValue(buffer));
aUintValue = 0;
aValue = 0;
for (uint16_t pos = 0; pos < mOption.mLength; pos++)
{
aValue <<= 8;
aValue |= value[pos];
aUintValue <<= CHAR_BIT;
aUintValue |= buffer[pos];
}
exit:
return error;
}
otError OptionIterator::GetOptionValue(void *aValue) const
otError Option::Iterator::Read(uint16_t aLength, void *aBuffer)
{
// Reads `aLength` bytes from the message into `aBuffer` at
// `mNextOptionOffset` and updates the `mNextOptionOffset` on a
// successful read (i.e., when entire `aLength` bytes can be read).
otError error = OT_ERROR_NONE;
VerifyOrExit(GetMessage().Read(mNextOptionOffset, aLength, aBuffer) == aLength, error = OT_ERROR_PARSE);
mNextOptionOffset += aLength;
exit:
return error;
}
otError Option::Iterator::ReadExtendedOptionField(uint16_t &aValue)
{
otError error = OT_ERROR_NONE;
VerifyOrExit(mNextOptionOffset > 0, error = OT_ERROR_NOT_FOUND);
VerifyOrExit(aValue >= Message::kOption1ByteExtension, OT_NOOP);
VerifyOrExit(GetMessage().Read(mNextOptionOffset - mOption.mLength, mOption.mLength, aValue) == mOption.mLength,
error = OT_ERROR_PARSE);
if (aValue == Message::kOption1ByteExtension)
{
uint8_t value8;
SuccessOrExit(error = Read(sizeof(uint8_t), &value8));
aValue = static_cast<uint16_t>(value8) + Message::kOption1ByteExtensionOffset;
}
else if (aValue == Message::kOption2ByteExtension)
{
uint16_t value16;
SuccessOrExit(error = Read(sizeof(uint16_t), &value16));
value16 = Encoding::BigEndian::HostSwap16(value16);
aValue = value16 + Message::kOption2ByteExtensionOffset;
}
else
{
error = OT_ERROR_PARSE;
}
exit:
return error;
}
otError Option::Iterator::InitOrAdvance(const Message *aMessage, uint16_t aNumber)
{
otError error = (aMessage != nullptr) ? Init(*aMessage) : Advance();
while ((error == OT_ERROR_NONE) && !IsDone() && (GetOption()->GetNumber() != aNumber))
{
error = Advance();
}
return error;
}
} // namespace Coap
} // namespace ot
+183 -85
View File
@@ -66,7 +66,7 @@ using ot::Encoding::BigEndian::HostSwap16;
*
*/
class OptionIterator;
class Option;
/**
* CoAP Type values.
@@ -159,7 +159,7 @@ enum : uint16_t
*/
class Message : public ot::Message
{
friend class OptionIterator;
friend class Option;
public:
enum : uint8_t
@@ -404,16 +404,14 @@ public:
* @retval FALSE If Tokens differ in length or value.
*
*/
bool IsTokenEqual(const Message &aMessage) const
{
return ((GetTokenLength() == aMessage.GetTokenLength()) &&
(memcmp(GetToken(), aMessage.GetToken(), GetTokenLength()) == 0));
}
bool IsTokenEqual(const Message &aMessage) const;
/**
* This method appends a CoAP option.
*
* @param[in] aOption The CoAP Option.
* @param[in] aNumber The CoAP Option number.
* @param[in] aLength The CoAP Option length.
* @param[in] aValue A pointer to the CoAP Option value (@p aLength bytes are used as Option value).
*
* @retval OT_ERROR_NONE Successfully appended the option.
* @retval OT_ERROR_INVALID_ARGS The option type is not equal or greater than the last option type.
@@ -423,8 +421,7 @@ public:
otError AppendOption(uint16_t aNumber, uint16_t aLength, const void *aValue);
/**
* This method appends an unsigned integer CoAP option as specified in
* https://tools.ietf.org/html/rfc7252#section-3.2
* This method appends an unsigned integer CoAP option as specified in RFC-7252 section-3.2
*
* @param[in] aNumber The CoAP Option number.
* @param[in] aValue The CoAP Option unsigned integer value.
@@ -458,7 +455,7 @@ public:
* @retval OT_ERROR_INVALID_ARGS The option type is not equal or greater than the last option type.
* @retval OT_ERROR_NO_BUFS The option length exceeds the buffer size.
*/
otError AppendObserveOption(uint32_t aObserve);
otError AppendObserveOption(uint32_t aObserve) { return AppendUintOption(kOptionObserve, aObserve & kObserveMask); }
/**
* This method appends a Uri-Path option.
@@ -497,7 +494,7 @@ public:
* @retval OT_ERROR_NO_BUFS The option length exceeds the buffer size.
*
*/
otError AppendProxyUriOption(const char *aProxyUri);
otError AppendProxyUriOption(const char *aProxyUri) { return AppendStringOption(kOptionProxyUri, aProxyUri); }
/**
* This method appends a Content-Format option.
@@ -509,7 +506,10 @@ public:
* @retval OT_ERROR_NO_BUFS The option length exceeds the buffer size.
*
*/
otError AppendContentFormatOption(otCoapOptionContentFormat aContentFormat);
otError AppendContentFormatOption(otCoapOptionContentFormat aContentFormat)
{
return AppendUintOption(kOptionContentFormat, static_cast<uint32_t>(aContentFormat));
}
/**
* This method appends a Max-Age option.
@@ -520,7 +520,7 @@ public:
* @retval OT_ERROR_INVALID_ARGS The option type is not equal or greater than the last option type.
* @retval OT_ERROR_NO_BUFS The option length exceeds the buffer size.
*/
otError AppendMaxAgeOption(uint32_t aMaxAge);
otError AppendMaxAgeOption(uint32_t aMaxAge) { return AppendUintOption(kOptionMaxAge, aMaxAge); }
/**
* This method appends a single Uri-Query option.
@@ -531,7 +531,7 @@ public:
* @retval OT_ERROR_INVALID_ARGS The option type is not equal or greater than the last option type.
* @retval OT_ERROR_NO_BUFS The option length exceeds the buffer size.
*/
otError AppendUriQueryOption(const char *aUriQuery);
otError AppendUriQueryOption(const char *aUriQuery) { return AppendStringOption(kOptionUriQuery, aUriQuery); }
/**
* This method adds Payload Marker indicating beginning of the payload to the CoAP header.
@@ -788,9 +788,12 @@ private:
kOptionLengthOffset = 0,
kOptionLengthMask = 0xf << kOptionLengthOffset,
kMaxOptionHeaderSize = 5,
kOption1ByteExtension = 13, // Indicates a 1 byte extension (RFC 7252).
kOption2ByteExtension = 14, // Indicates a 2 byte extension (RFC 7252).
kMaxOptionHeaderSize = 5,
kOption1ByteExtension = 13, // Indicates a one-byte extension.
kOption2ByteExtension = 14, // Indicates a two-byte extension.
kPayloadMarker = 0xff,
kHelpDataAlignment = sizeof(uint16_t), ///< Alignment of help data.
};
@@ -811,9 +814,10 @@ private:
kBlockNumOffset = 4,
};
enum
enum : uint32_t
{
kBlockNumMax = 0xFFFFF,
kObserveMask = 0xffffff,
kBlockNumMax = 0xffff,
};
/**
@@ -858,6 +862,8 @@ private:
GetHelpData().mHeader.mVersionTypeToken &= ~kTokenLengthMask;
GetHelpData().mHeader.mVersionTypeToken |= ((aTokenLength << kTokenLengthOffset) & kTokenLengthMask);
}
uint8_t WriteExtendedOptionField(uint16_t aValue, uint8_t *&aBuffer);
};
/**
@@ -911,88 +917,180 @@ public:
};
/**
* This class acts as an iterator for CoAP options.
* This class represents a CoAP option.
*
*/
class OptionIterator : public ::otCoapOptionIterator
class Option : public otCoapOption
{
public:
/**
* Initialize the state of the iterator to iterate over the given message.
*
* @retval OT_ERROR_NONE Successfully initialized
* @retval OT_ERROR_PARSE Message state is inconsistent
* This class represents an iterator for CoAP options.
*
*/
otError Init(const Message *aMessage);
class Iterator : public otCoapOptionIterator
{
public:
/**
* This method initializes the iterator to iterate over CoAP Options in a CoAP message.
*
* The iterator MUST be initialized before any other methods are used, otherwise its behavior is undefined.
*
* After initialization, the iterator is either updated to point to the first option, or it is marked as done
* (i.e., `IsDone()` returns `true`) when there is no option or if there is a parse error.
*
* @param[in] aMessage The CoAP message.
*
* @retval OT_ERROR_NONE Successfully initialized. Iterator is either at the first option or done.
* @retval OT_ERROR_PARSE CoAP Option header in @p aMessage is not well-formed.
*
*/
otError Init(const Message &aMessage);
/**
* This method initializes the iterator to iterate over CoAP Options in a CoAP message matching a given Option
* Number value.
*
* The iterator MUST be initialized before any other methods are used, otherwise its behavior is undefined.
*
* After initialization, the iterator is either updated to point to the first option matching the given Option
* Number value, or it is marked as done (i.e., `IsDone()` returns `true`) when there is no matching option or
* if there is a parse error.
*
* @param[in] aMessage The CoAP message.
* @param[in] aNumber The CoAP Option Number.
*
* @retval OT_ERROR_NONE Successfully initialized. Iterator is either at the first matching option or done.
* @retval OT_ERROR_PARSE CoAP Option header in @p aMessage is not well-formed.
*
*/
otError Init(const Message &aMessage, uint16_t aNumber) { return InitOrAdvance(&aMessage, aNumber); }
/**
* This method indicates whether or not the iterator is done (i.e., has reached the end of CoAP Option Header).
*
* @retval TRUE Iterator is done (reached end of Option header).
* @retval FALSE Iterator is not done and currently pointing to a CoAP Option.
*
*/
bool IsDone(void) const { return mOption.mLength == kIteratorDoneLength; }
/**
* This method indicates whether or not there was a earlier parse error (i.e., whether the iterator is valid).
*
* After a parse errors, iterator would also be marked as done.
*
* @retval TRUE There was an earlier parse error and the iterator is not valid.
* @retval FALSE There was no earlier parse error and the iterator is valid.
*
*/
bool HasParseErrored(void) const { return mNextOptionOffset == kNextOptionOffsetParseError; }
/**
* This method advances the iterator to the next CoAP Option in the header.
*
* The iterator is updated to point to the next option or marked as done when there are no more options.
*
* @retval OT_ERROR_NONE Successfully advanced the iterator.
* @retval OT_ERROR_PARSE CoAP Option header is not well-formed.
*
*/
otError Advance(void);
/**
* This method advances the iterator to the next CoAP Option in the header matching a given Option Number value.
*
* The iterator is updated to point to the next matching option or marked as done when there are no more
* matching options.
*
* @param[in] aNumber The CoAP Option Number.
*
* @retval OT_ERROR_NONE Successfully advanced the iterator.
* @retval OT_ERROR_PARSE CoAP Option header is not well-formed.
*
*/
otError Advance(uint16_t aNumber) { return InitOrAdvance(nullptr, aNumber); }
/**
* This method gets the CoAP message associated with the iterator.
*
* @returns A reference to the CoAP message.
*
*/
const Message &GetMessage(void) const { return *static_cast<const Message *>(mMessage); }
/**
* This methods gets a pointer to the current CoAP Option to which the iterator is currently pointing.
*
* @returns A pointer to the current CoAP Option, or nullptr if iterator is done (or there was an earlier
* parse error).
*
*/
const Option *GetOption(void) const { return IsDone() ? nullptr : static_cast<const Option *>(&mOption); }
/**
* This method reads the current Option Value into a given buffer.
*
* @param[out] aValue The pointer to a buffer to copy the Option Value. The buffer is assumed to be
* sufficiently large (i.e. at least `GetOption()->GetLength()` bytes).
*
* @retval OT_ERROR_NONE Successfully read and copied the Option Value into given buffer.
* @retval OT_ERROR_NOT_FOUND Iterator is done (not pointing to any option).
*
*/
otError ReadOptionValue(void *aValue) const;
/**
* This method read the current Option Value which is assumed to be an unsigned integer.
*
* @param[out] aValue A reference to `uint64_t` to output the read Option Value.
*
* @retval OT_ERROR_NONE Successfully read the Option value.
* @retval OT_ERROR_NO_BUFS Value is too long to fit in an `uint64_t`.
* @retval OT_ERROR_NOT_FOUND Iterator is done (not pointing to any option).
*
*/
otError ReadOptionValue(uint64_t &aValue) const;
/**
* This method gets the offset of beginning of the CoAP message payload (after the CoAP header).
*
* This method MUST be used after the iterator is done (i.e. iterated through all options).
*
* @returns The offset of beginning of the CoAP message payload
*
*/
uint16_t GetPayloadMessageOffset(void) const { return mNextOptionOffset; }
private:
enum : uint16_t
{
kIteratorDoneLength = 0xffff, // `mOption.mLength` value to indicate iterator is done.
kNextOptionOffsetParseError = 0, // Special `mNextOptionOffset` value to indicate a parse error.
};
void MarkAsDone(void) { mOption.mLength = kIteratorDoneLength; }
void MarkAsParseErrored(void) { MarkAsDone(), mNextOptionOffset = kNextOptionOffsetParseError; }
otError Read(uint16_t aLength, void *aBuffer);
otError ReadExtendedOptionField(uint16_t &aValue);
otError InitOrAdvance(const Message *aMessage, uint16_t aNumber);
};
/**
* This method returns a pointer to the first option matching the given option number.
* This method gets the CoAP Option Number.
*
* The internal option pointer is advanced until matching option is seen, if no matching
* option is seen, the iterator will advance to the end of the options block.
* @returns The CoAP Option Number.
*
* @param[in] aOption Option number to look for.
*
* @returns A pointer to the first matching option. If no option matching @p aOption is seen, nullptr pointer is
* returned.
*/
const otCoapOption *GetFirstOptionMatching(uint16_t aOption);
uint16_t GetNumber(void) const { return mNumber; }
/**
* This method returns a pointer to the first option.
* This method gets the CoAP Option Length (length of Option Value in bytes).
*
* @returns A pointer to the first option. If no option is present nullptr pointer is returned.
*/
const otCoapOption *GetFirstOption(void);
/**
* This method returns a pointer to the next option matching the given option number.
*
* The internal option pointer is advanced until matching option is seen, if no matching
* option is seen, the iterator will advance to the end of the options block.
*
* @param[in] aOption Option number to look for.
*
* @returns A pointer to the next matching option (relative to current iterator position). If no option matching @p
* aOption is seen, nullptr pointer is returned.
*/
const otCoapOption *GetNextOptionMatching(uint16_t aOption);
/**
* This method returns a pointer to the next option.
*
* @returns A pointer to the next option. If no more options are present nullptr pointer is returned.
*/
const otCoapOption *GetNextOption(void);
/**
* This function fills current option value into @p aValue. The option is assumed to be an unsigned integer.
*
* @param[out] aValue Buffer to store the option value.
*
* @retval OT_ERROR_NONE Successfully filled value.
* @retval OT_ERROR_NOT_FOUND No more options, aIterator->mNextOptionOffset is set to offset of payload.
* @retval OT_ERROR_NO_BUFS Value is too long to fit in a uint64_t.
* @returns The CoAP Option Length (in bytes).
*
*/
otError GetOptionValue(uint64_t &aValue) const;
/**
* This function fills current option value into @p aValue.
*
* @param[out] aValue Buffer to store the option value. This buffer is assumed to be sufficiently large
* (see @ref otCoapOption::mLength).
*
* @retval OT_ERROR_NONE Successfully filled value.
* @retval OT_ERROR_NOT_FOUND No more options, mNextOptionOffset is set to offset of payload.
*
*/
otError GetOptionValue(void *aValue) const;
private:
void ClearOption(void) { memset(&mOption, 0, sizeof(mOption)); }
const Message &GetMessage(void) const { return *static_cast<const Message *>(mMessage); }
uint16_t GetLength(void) const { return mLength; }
};
/**