[coap] add otCoapOptionIterator and related API functions (#4452)

This commit adds a otCoapOptionIterator structure which the user can
use to iterate over the CoAP options of a ot::Coap::Message object
without influencing any of the properties of that object.

Multiple otCoapOptionIterator structures may operate on the same
ot::Coap::Message object without interference.
This commit is contained in:
Stuart Longland
2020-01-10 08:46:53 -08:00
committed by Jonathan Hui
parent bfa84c26b0
commit 033cf4c4b1
5 changed files with 239 additions and 166 deletions
+30 -7
View File
@@ -149,6 +149,17 @@ typedef struct otCoapOption
uint16_t mLength; ///< Option Length
} otCoapOption;
/**
* This structure acts as an iterator for CoAP options
*
*/
typedef struct otCoapOptionIterator
{
const otMessage *mMessage; ///< CoAP message
otCoapOption mOption; ///< CoAP message option
uint16_t mNextOptionOffset; ///< Byte offset of next option
} otCoapOptionIterator;
/**
* CoAP Content Format codes. The full list is documented at
* https://www.iana.org/assignments/core-parameters/core-parameters.xhtml#content-formats
@@ -566,37 +577,49 @@ uint8_t otCoapMessageGetTokenLength(const otMessage *aMessage);
*/
const uint8_t *otCoapMessageGetToken(const otMessage *aMessage);
/**
* This function initialises an iterator for the options in the given message.
*
* @param[inout] aIterator A pointer to the CoAP message option iterator.
* @param[in] aMessage A pointer to the CoAP message.
*
* @retval OT_ERROR_NONE Successfully initialised.
* @retval OT_ERROR_PARSE Message state is inconsistent.
*
*/
otError otCoapOptionIteratorInit(otCoapOptionIterator *aIterator, const otMessage *aMessage);
/**
* This function returns a pointer to the first option.
*
* @param[in] aMessage A pointer to the CoAP message.
* @param[inout] aIterator A pointer to the CoAP message option iterator.
*
* @returns A pointer to the first option. If no option is present NULL pointer is returned.
*
*/
const otCoapOption *otCoapMessageGetFirstOption(otMessage *aMessage);
const otCoapOption *otCoapOptionIteratorGetFirstOption(otCoapOptionIterator *aIterator);
/**
* This function returns a pointer to the next option.
*
* @param[in] aMessage A pointer to the CoAP message.
* @param[inout] aIterator A pointer to the CoAP message option iterator.
*
* @returns A pointer to the next option. If no more options are present NULL pointer is returned.
*
*/
const otCoapOption *otCoapMessageGetNextOption(otMessage *aMessage);
const otCoapOption *otCoapOptionIteratorGetNextOption(otCoapOptionIterator *aIterator);
/**
* This function fills current option value into @p aValue.
*
* @param[in] aMessage A pointer to the CoAP message.
* @param[out] aValue A pointer to a buffer to receive the option value.
* @param[inout] aIterator A pointer to the CoAP message option iterator.
* @param[out] aValue A pointer to a buffer to receive the option value.
*
* @retval OT_ERROR_NONE Successfully filled value.
* @retval OT_ERROR_NOT_FOUND No current option.
*
*/
otError otCoapMessageGetOptionValue(otMessage *aMessage, void *aValue);
otError otCoapOptionIteratorGetOptionValue(otCoapOptionIterator *aIterator, void *aValue);
/**
* This function creates a new CoAP message.
+11 -6
View File
@@ -160,19 +160,24 @@ const uint8_t *otCoapMessageGetToken(const otMessage *aMessage)
return static_cast<const Coap::Message *>(aMessage)->GetToken();
}
const otCoapOption *otCoapMessageGetFirstOption(otMessage *aMessage)
otError otCoapOptionIteratorInit(otCoapOptionIterator *aIterator, const otMessage *aMessage)
{
return static_cast<Coap::Message *>(aMessage)->GetFirstOption();
return static_cast<Coap::OptionIterator *>(aIterator)->Init(static_cast<const Coap::Message *>(aMessage));
}
const otCoapOption *otCoapMessageGetNextOption(otMessage *aMessage)
const otCoapOption *otCoapOptionIteratorGetFirstOption(otCoapOptionIterator *aIterator)
{
return static_cast<Coap::Message *>(aMessage)->GetNextOption();
return static_cast<Coap::OptionIterator *>(aIterator)->GetFirstOption();
}
otError otCoapMessageGetOptionValue(otMessage *aMessage, void *aValue)
const otCoapOption *otCoapOptionIteratorGetNextOption(otCoapOptionIterator *aIterator)
{
return static_cast<Coap::Message *>(aMessage)->GetOptionValue(aValue);
return static_cast<Coap::OptionIterator *>(aIterator)->GetNextOption();
}
otError otCoapOptionIteratorGetOptionValue(otCoapOptionIterator *aIterator, void *aValue)
{
return static_cast<Coap::OptionIterator *>(aIterator)->GetOptionValue(aValue);
}
otError otCoapSendRequest(otInstance * aInstance,
+8 -6
View File
@@ -535,10 +535,11 @@ exit:
void CoapBase::ProcessReceivedRequest(Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
{
char uriPath[Resource::kMaxReceivedUriPath];
char * curUriPath = uriPath;
Message *cachedResponse = NULL;
otError error = OT_ERROR_NOT_FOUND;
char uriPath[Resource::kMaxReceivedUriPath];
char * curUriPath = uriPath;
Message * cachedResponse = NULL;
otError error = OT_ERROR_NOT_FOUND;
OptionIterator iterator;
if (mInterceptor != NULL)
{
@@ -561,7 +562,8 @@ void CoapBase::ProcessReceivedRequest(Message &aMessage, const Ip6::MessageInfo
break;
}
for (const otCoapOption *option = aMessage.GetFirstOption(); option != NULL; option = aMessage.GetNextOption())
SuccessOrExit(error = iterator.Init(&aMessage));
for (const otCoapOption *option = iterator.GetFirstOption(); option != NULL; option = iterator.GetNextOption())
{
switch (option->mNumber)
{
@@ -573,7 +575,7 @@ void CoapBase::ProcessReceivedRequest(Message &aMessage, const Ip6::MessageInfo
VerifyOrExit(option->mLength < sizeof(uriPath) - static_cast<size_t>(curUriPath + 1 - uriPath));
aMessage.GetOptionValue(curUriPath);
iterator.GetOptionValue(curUriPath);
curUriPath += option->mLength;
break;
+142 -119
View File
@@ -207,121 +207,6 @@ otError Message::AppendUriQueryOption(const char *aUriQuery)
return AppendStringOption(OT_COAP_OPTION_URI_QUERY, aUriQuery);
}
const otCoapOption *Message::GetFirstOption(void)
{
const otCoapOption *option = NULL;
GetHelpData().ClearOption();
VerifyOrExit(GetLength() - GetHelpData().mHeaderOffset >= GetOptionStart());
GetHelpData().mNextOptionOffset = GetHelpData().mHeaderOffset + GetOptionStart();
if (GetHelpData().mNextOptionOffset < GetLength())
{
option = GetNextOption();
}
exit:
return option;
}
const otCoapOption *Message::GetNextOption(void)
{
otError error = OT_ERROR_NONE;
uint16_t optionDelta;
uint16_t optionLength;
uint8_t buf[kMaxOptionHeaderSize];
uint8_t * cur = buf + 1;
otCoapOption *rval = NULL;
VerifyOrExit(GetHelpData().mNextOptionOffset < GetLength(), error = OT_ERROR_NOT_FOUND);
Read(GetHelpData().mNextOptionOffset, sizeof(buf), buf);
optionDelta = buf[0] >> 4;
optionLength = buf[0] & 0xf;
GetHelpData().mNextOptionOffset += sizeof(uint8_t);
if (optionDelta < kOption1ByteExtension)
{
// do nothing
}
else if (optionDelta == kOption1ByteExtension)
{
optionDelta = kOption1ByteExtensionOffset + cur[0];
GetHelpData().mNextOptionOffset += sizeof(uint8_t);
cur++;
}
else if (optionDelta == kOption2ByteExtension)
{
optionDelta = kOption2ByteExtensionOffset + static_cast<uint16_t>((cur[0] << 8) | cur[1]);
GetHelpData().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(GetHelpData().mNextOptionOffset < GetLength(), error = OT_ERROR_PARSE);
ExitNow(error = OT_ERROR_NOT_FOUND);
}
if (optionLength < kOption1ByteExtension)
{
// do nothing
}
else if (optionLength == kOption1ByteExtension)
{
optionLength = kOption1ByteExtensionOffset + cur[0];
GetHelpData().mNextOptionOffset += sizeof(uint8_t);
}
else if (optionLength == kOption2ByteExtension)
{
optionLength = kOption2ByteExtensionOffset + static_cast<uint16_t>((cur[0] << 8) | cur[1]);
GetHelpData().mNextOptionOffset += sizeof(uint16_t);
}
else
{
ExitNow(error = OT_ERROR_PARSE);
}
VerifyOrExit(optionLength <= GetLength() - GetHelpData().mNextOptionOffset, error = OT_ERROR_PARSE);
rval = &GetHelpData().mOption;
rval->mNumber += optionDelta;
rval->mLength = optionLength;
GetHelpData().mNextOptionOffset += optionLength;
exit:
if (error == OT_ERROR_PARSE)
{
GetHelpData().mNextOptionOffset = 0;
}
return rval;
}
otError Message::GetOptionValue(void *aValue) const
{
otError error = OT_ERROR_NONE;
const otCoapOption &option = GetHelpData().mOption;
VerifyOrExit(GetHelpData().mNextOptionOffset > 0, error = OT_ERROR_NOT_FOUND);
VerifyOrExit(Read(GetHelpData().mNextOptionOffset - option.mLength, option.mLength, aValue) == option.mLength,
error = OT_ERROR_PARSE);
exit:
return error;
}
otError Message::SetPayloadMarker(void)
{
otError error = OT_ERROR_NONE;
@@ -340,7 +225,8 @@ exit:
otError Message::ParseHeader(void)
{
otError error = OT_ERROR_NONE;
otError error = OT_ERROR_NONE;
OptionIterator iterator;
assert(mBuffer.mHead.mInfo.mReserved >=
sizeof(GetHelpData()) +
@@ -351,12 +237,13 @@ otError Message::ParseHeader(void)
GetHelpData().mHeaderOffset = GetOffset();
Read(GetHelpData().mHeaderOffset, sizeof(GetHelpData().mHeader), &GetHelpData().mHeader);
for (const otCoapOption *option = GetFirstOption(); option != NULL; option = GetNextOption())
SuccessOrExit(error = iterator.Init(this));
for (const otCoapOption *option = iterator.GetFirstOption(); option != NULL; option = iterator.GetNextOption())
{
}
VerifyOrExit(GetHelpData().mNextOptionOffset > 0, error = OT_ERROR_PARSE);
GetHelpData().mHeaderLength = GetHelpData().mNextOptionOffset - GetHelpData().mHeaderOffset;
VerifyOrExit(iterator.mNextOptionOffset > 0, error = OT_ERROR_PARSE);
GetHelpData().mHeaderLength = iterator.mNextOptionOffset - GetHelpData().mHeaderOffset;
MoveOffset(GetHelpData().mHeaderLength);
exit:
@@ -502,5 +389,141 @@ const char *Message::CodeToString(void) const
}
#endif // OPENTHREAD_CONFIG_COAP_API_ENABLE
otError OptionIterator::Init(const Message *aMessage)
{
otError err = OT_ERROR_NONE;
/*
* 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);
mMessage = aMessage;
GetFirstOption();
exit:
return err;
}
const otCoapOption *OptionIterator::GetFirstOption(void)
{
const otCoapOption *option = NULL;
const Message & message = GetMessage();
ClearOption();
mNextOptionOffset = message.GetHelpData().mHeaderOffset + message.GetOptionStart();
if (mNextOptionOffset < message.GetLength())
{
option = GetNextOption();
}
return option;
}
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 = NULL;
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;
mNextOptionOffset += optionLength;
exit:
if (error == OT_ERROR_PARSE)
{
mNextOptionOffset = 0;
}
return rval;
}
otError OptionIterator::GetOptionValue(void *aValue) const
{
otError error = OT_ERROR_NONE;
VerifyOrExit(mNextOptionOffset > 0, error = OT_ERROR_NOT_FOUND);
VerifyOrExit(GetMessage().Read(mNextOptionOffset - mOption.mLength, mOption.mLength, aValue) == mOption.mLength,
error = OT_ERROR_PARSE);
exit:
return error;
}
} // namespace Coap
} // namespace ot
+48 -28
View File
@@ -67,12 +67,16 @@ using ot::Encoding::BigEndian::HostSwap16;
*
*/
class OptionIterator;
/**
* This class implements CoAP message generation and parsing.
*
*/
class Message : public ot::Message
{
friend class OptionIterator;
public:
enum
{
@@ -379,27 +383,6 @@ public:
*/
otError AppendUriQueryOption(const char *aUriQuery);
/**
* This method returns a pointer to the first option.
*
*/
const otCoapOption *GetFirstOption(void);
/**
* This method returns a pointer to the next option.
*
*/
const otCoapOption *GetNextOption(void);
/**
* This function fills current option value into @p aValue.
*
* @retval OT_ERROR_NONE Successfully filled value.
* @retval OT_ERROR_NOT_FOUND No more options, aIterator->mNextOptionOffset is set to offset of payload.
*
*/
otError GetOptionValue(void *aValue) const;
/**
* This method adds Payload Marker indicating beginning of the payload to the CoAP header.
*
@@ -586,14 +569,11 @@ private:
struct HelpData
{
void Clear(void) { memset(this, 0, sizeof(*this)); }
void ClearOption(void) { memset(&mOption, 0, sizeof(mOption)); }
Header mHeader;
otCoapOption mOption;
uint16_t mNextOptionOffset; ///< The byte offset for the next CoAP Option
uint16_t mOptionLast;
uint16_t mHeaderOffset; ///< The byte offset for the CoAP Header
uint16_t mHeaderLength;
Header mHeader;
uint16_t mOptionLast;
uint16_t mHeaderOffset; ///< The byte offset for the CoAP Header
uint16_t mHeaderLength;
};
const HelpData &GetHelpData(void) const
@@ -607,6 +587,46 @@ private:
HelpData &GetHelpData(void) { return const_cast<HelpData &>(static_cast<const Message *>(this)->GetHelpData()); }
};
class OptionIterator : public ::otCoapOptionIterator
{
public:
/**
* Initialise the state of the iterator to iterate over the given message.
*
* @retval OT_ERROR_NONE Successfully initialised
* @retval OT_ERROR_PARSE Message state is inconsistent
*
*/
otError Init(const Message *aMessage);
/**
* This method returns a pointer to the first option.
*
* @returns A pointer to the first option. If no option is present NULL pointer is returned.
*/
const otCoapOption *GetFirstOption(void);
/**
* This method returns a pointer to the next option.
*
* @returns A pointer to the next option. If no more options are present NULL pointer is returned.
*/
const otCoapOption *GetNextOption(void);
/**
* This function fills current option value into @p aValue.
*
* @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); }
};
/**
* @}
*