| /* |
| * Copyright (c) 2016, The OpenThread Authors. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. Neither the name of the copyright holder nor the |
| * names of its contributors may be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
| * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| * POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| /** |
| * @file |
| * This file includes definitions for generating and processing CoAP messages. |
| */ |
| |
| #ifndef COAP_HEADER_HPP_ |
| #define COAP_HEADER_HPP_ |
| |
| #include "openthread-core-config.h" |
| |
| #include <openthread/coap.h> |
| |
| #include "common/code_utils.hpp" |
| #include "common/encoding.hpp" |
| #include "common/message.hpp" |
| #include "utils/static_assert.hpp" |
| |
| namespace ot { |
| |
| /** |
| * @namespace ot::Coap |
| * @brief |
| * This namespace includes definitions for CoAP. |
| * |
| */ |
| namespace Coap { |
| |
| using ot::Encoding::BigEndian::HostSwap16; |
| |
| /** |
| * @addtogroup core-coap |
| * |
| * @brief |
| * This module includes definitions for CoAP. |
| * |
| * @{ |
| * |
| */ |
| |
| class OptionIterator; |
| |
| /** |
| * This class implements CoAP message generation and parsing. |
| * |
| */ |
| class Message : public ot::Message |
| { |
| friend class OptionIterator; |
| |
| public: |
| enum |
| { |
| kVersion1 = 1, ///< Version 1 |
| kMinHeaderLength = 4, ///< Minimum header length |
| kMaxHeaderLength = 512, ///< Maximum header length |
| kDefaultTokenLength = 2, ///< Default token length |
| kTypeOffset = 4, ///< The type offset in the first byte of a CoAP header |
| }; |
| |
| /** |
| * CoAP Type values. |
| * |
| */ |
| typedef otCoapType Type; |
| |
| /** |
| * CoAP Code values. |
| * |
| */ |
| typedef otCoapCode Code; |
| |
| /** |
| * This method initializes the CoAP header. |
| * |
| */ |
| void Init(void); |
| |
| /** |
| * This method initializes the CoAP header with specific Type and Code. |
| * |
| * @param[in] aType The Type value. |
| * @param[in] aCode The Code value. |
| * |
| */ |
| void Init(Type aType, Code aCode); |
| |
| /** |
| * This method initializes the CoAP header with specific Type and Code. |
| * |
| * @param[in] aType The Type value. |
| * @param[in] aCode The Code value. |
| * @param[in] aUriPath A pointer to a NULL-terminated string. |
| * |
| * @retval OT_ERROR_NONE Successfully appended the option. |
| * @retval OT_ERROR_NO_BUFS The option length exceeds the buffer size. |
| * |
| */ |
| otError Init(Type aType, Code aCode, const char *aUriPath); |
| |
| /** |
| * This method writes header to the message. This must be called before sending the message. |
| * |
| */ |
| void Finish(void); |
| |
| /** |
| * This method returns the Version value. |
| * |
| * @returns The Version value. |
| * |
| */ |
| uint8_t GetVersion(void) const |
| { |
| return (GetHelpData().mHeader.mVersionTypeToken & kVersionMask) >> kVersionOffset; |
| } |
| |
| /** |
| * This method sets the Version value. |
| * |
| * @param[in] aVersion The Version value. |
| * |
| */ |
| void SetVersion(uint8_t aVersion) |
| { |
| GetHelpData().mHeader.mVersionTypeToken &= ~kVersionMask; |
| GetHelpData().mHeader.mVersionTypeToken |= aVersion << kVersionOffset; |
| } |
| |
| /** |
| * This method returns the Type value. |
| * |
| * @returns The Type value. |
| * |
| */ |
| Type GetType(void) const { return static_cast<Type>(GetHelpData().mHeader.mVersionTypeToken & kTypeMask); } |
| |
| /** |
| * This method sets the Type value. |
| * |
| * @param[in] aType The Type value. |
| * |
| */ |
| void SetType(Type aType) |
| { |
| GetHelpData().mHeader.mVersionTypeToken &= ~kTypeMask; |
| GetHelpData().mHeader.mVersionTypeToken |= aType; |
| } |
| |
| /** |
| * This method returns the Code value. |
| * |
| * @returns The Code value. |
| * |
| */ |
| Code GetCode(void) const { return static_cast<Code>(GetHelpData().mHeader.mCode); } |
| |
| /** |
| * This method sets the Code value. |
| * |
| * @param[in] aCode The Code value. |
| * |
| */ |
| void SetCode(Code aCode) { GetHelpData().mHeader.mCode = static_cast<uint8_t>(aCode); } |
| |
| #if OPENTHREAD_CONFIG_COAP_API_ENABLE |
| /** |
| * This method returns the CoAP Code as human readable string. |
| * |
| * @ returns The CoAP Code as string. |
| * |
| */ |
| const char *CodeToString(void) const; |
| #endif // OPENTHREAD_CONFIG_COAP_API_ENABLE |
| |
| /** |
| * This method returns the Message ID value. |
| * |
| * @returns The Message ID value. |
| * |
| */ |
| uint16_t GetMessageId(void) const { return HostSwap16(GetHelpData().mHeader.mMessageId); } |
| |
| /** |
| * This method sets the Message ID value. |
| * |
| * @param[in] aMessageId The Message ID value. |
| * |
| */ |
| void SetMessageId(uint16_t aMessageId) { GetHelpData().mHeader.mMessageId = HostSwap16(aMessageId); } |
| |
| /** |
| * This method returns the Token length. |
| * |
| * @returns The Token length. |
| * |
| */ |
| uint8_t GetTokenLength(void) const |
| { |
| return (GetHelpData().mHeader.mVersionTypeToken & kTokenLengthMask) >> kTokenLengthOffset; |
| } |
| |
| /** |
| * This method returns a pointer to the Token value. |
| * |
| * @returns A pointer to the Token value. |
| * |
| */ |
| const uint8_t *GetToken(void) const { return GetHelpData().mHeader.mToken; } |
| |
| /** |
| * This method sets the Token value and length. |
| * |
| * @param[in] aToken A pointer to the Token value. |
| * @param[in] aTokenLength The Length of @p aToken. |
| * |
| * @retval OT_ERROR_NONE Successfully set the token value. |
| * @retval OT_ERROR_NO_BUFS Insufficient message buffers available to set the token value. |
| * |
| */ |
| otError SetToken(const uint8_t *aToken, uint8_t aTokenLength); |
| |
| /** |
| * This method sets the Token length and randomizes its value. |
| * |
| * @param[in] aTokenLength The Length of a Token to set. |
| * |
| * @retval OT_ERROR_NONE Successfully set the token value. |
| * @retval OT_ERROR_NO_BUFS Insufficient message buffers available to set the token value. |
| * |
| */ |
| otError SetToken(uint8_t aTokenLength); |
| |
| /** |
| * This method checks if Tokens in two CoAP headers are equal. |
| * |
| * @param[in] aMessage A header to compare. |
| * |
| * @retval TRUE If two Tokens are equal. |
| * @retval FALSE If Tokens differ in length or value. |
| * |
| */ |
| bool IsTokenEqual(const Message &aMessage) const |
| { |
| return ((this->GetTokenLength() == aMessage.GetTokenLength()) && |
| (memcmp(this->GetToken(), aMessage.GetToken(), this->GetTokenLength()) == 0)); |
| } |
| |
| /** |
| * This method appends a CoAP option. |
| * |
| * @param[in] aOption The CoAP Option. |
| * |
| * @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. |
| * @retval OT_ERROR_NO_BUFS The option length exceeds the buffer size. |
| * |
| */ |
| 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 |
| * |
| * @param[in] aNumber The CoAP Option number. |
| * @param[in] aValue The CoAP Option unsigned integer 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. |
| * @retval OT_ERROR_NO_BUFS The option length exceeds the buffer size. |
| * |
| */ |
| otError AppendUintOption(uint16_t aNumber, uint32_t aValue); |
| |
| /** |
| * This method appends a string CoAP option. |
| * |
| * @param[in] aNumber The CoAP Option number. |
| * @param[in] aValue The CoAP Option string 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. |
| * @retval OT_ERROR_NO_BUFS The option length exceeds the buffer size. |
| * |
| */ |
| otError AppendStringOption(uint16_t aNumber, const char *aValue); |
| |
| /** |
| * This method appends an Observe option. |
| * |
| * @param[in] aObserve Observe field 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. |
| * @retval OT_ERROR_NO_BUFS The option length exceeds the buffer size. |
| */ |
| otError AppendObserveOption(uint32_t aObserve); |
| |
| /** |
| * This method appends a Uri-Path option. |
| * |
| * @param[in] aUriPath A pointer to a NULL-terminated string. |
| * |
| * @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. |
| * @retval OT_ERROR_NO_BUFS The option length exceeds the buffer size. |
| * |
| */ |
| otError AppendUriPathOptions(const char *aUriPath); |
| |
| /** |
| * This method appends a Proxy-Uri option. |
| * |
| * @param[in] aProxyUri A pointer to a NULL-terminated string. |
| * |
| * @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. |
| * @retval OT_ERROR_NO_BUFS The option length exceeds the buffer size. |
| * |
| */ |
| otError AppendProxyUriOption(const char *aProxyUri); |
| |
| /** |
| * This method appends a Content-Format option. |
| * |
| * @param[in] aContentFormat The Content Format 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. |
| * @retval OT_ERROR_NO_BUFS The option length exceeds the buffer size. |
| * |
| */ |
| otError AppendContentFormatOption(otCoapOptionContentFormat aContentFormat); |
| |
| /** |
| * This method appends a Max-Age option. |
| * |
| * @param[in] aMaxAge The Max-Age 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. |
| * @retval OT_ERROR_NO_BUFS The option length exceeds the buffer size. |
| */ |
| otError AppendMaxAgeOption(uint32_t aMaxAge); |
| |
| /** |
| * This method appends a single Uri-Query option. |
| * |
| * @param[in] aUriQuery A pointer to NULL-terminated string, which should contain a single key=value pair. |
| * |
| * @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. |
| * @retval OT_ERROR_NO_BUFS The option length exceeds the buffer size. |
| */ |
| otError AppendUriQueryOption(const char *aUriQuery); |
| |
| /** |
| * This method adds Payload Marker indicating beginning of the payload to the CoAP header. |
| * |
| * It also set offset to the start of payload. |
| * |
| * @retval OT_ERROR_NONE Payload Marker successfully added. |
| * @retval OT_ERROR_NO_BUFS Message Payload Marker exceeds the buffer size. |
| * |
| */ |
| otError SetPayloadMarker(void); |
| |
| /** |
| * This method returns the offset of the first CoAP option. |
| * |
| * @returns The offset of the first CoAP option. |
| * |
| */ |
| uint16_t GetOptionStart(void) const { return kMinHeaderLength + GetTokenLength(); } |
| |
| /** |
| * This method parses CoAP header and moves offset end of CoAP header. |
| * |
| * @retval OT_ERROR_NONE Successfully parsed CoAP header from the message. |
| * @retval OT_ERROR_PARSE Failed to parse the CoAP header. |
| * |
| */ |
| otError ParseHeader(void); |
| |
| /** |
| * This method sets a default response header based on request header. |
| * |
| * @param[in] aRequest The request message. |
| * |
| * @retval OT_ERROR_NONE Successfully set the default response header. |
| * @retval OT_ERROR_NO_BUFS Insufficient message buffers available to set the default response header. |
| * |
| */ |
| otError SetDefaultResponseHeader(const Message &aRequest); |
| |
| /** |
| * This method checks if a header is an empty message header. |
| * |
| * @retval TRUE Message is an empty message header. |
| * @retval FALSE Message is not an empty message header. |
| * |
| */ |
| bool IsEmpty(void) const { return (GetCode() == 0); } |
| |
| /** |
| * This method checks if a header is a request header. |
| * |
| * @retval TRUE Message is a request header. |
| * @retval FALSE Message is not a request header. |
| * |
| */ |
| bool IsRequest(void) const { return (GetCode() >= OT_COAP_CODE_GET && GetCode() <= OT_COAP_CODE_DELETE); } |
| |
| /** |
| * This method checks if a header is a response header. |
| * |
| * @retval TRUE Message is a response header. |
| * @retval FALSE Message is not a response header. |
| * |
| */ |
| bool IsResponse(void) const { return (GetCode() >= OT_COAP_CODE_RESPONSE_MIN); } |
| |
| /** |
| * This method checks if a header is a CON message header. |
| * |
| * @retval TRUE Message is a CON message header. |
| * @retval FALSE Message is not is a CON message header. |
| * |
| */ |
| bool IsConfirmable(void) const { return (GetType() == OT_COAP_TYPE_CONFIRMABLE); } |
| |
| /** |
| * This method checks if a header is a NON message header. |
| * |
| * @retval TRUE Message is a NON message header. |
| * @retval FALSE Message is not is a NON message header. |
| * |
| */ |
| bool IsNonConfirmable(void) const { return (GetType() == OT_COAP_TYPE_NON_CONFIRMABLE); } |
| |
| /** |
| * This method checks if a header is a ACK message header. |
| * |
| * @retval TRUE Message is a ACK message header. |
| * @retval FALSE Message is not is a ACK message header. |
| * |
| */ |
| bool IsAck(void) const { return (GetType() == OT_COAP_TYPE_ACKNOWLEDGMENT); } |
| |
| /** |
| * This method checks if a header is a RST message header. |
| * |
| * @retval TRUE Message is a RST message header. |
| * @retval FALSE Message is not is a RST message header. |
| * |
| */ |
| bool IsReset(void) const { return (GetType() == OT_COAP_TYPE_RESET); } |
| |
| /** |
| * This method creates a copy of this CoAP message. |
| * |
| * It allocates the new message from the same message pool as the original one and copies @p aLength octets |
| * of the payload. The `Type`, `SubType`, `LinkSecurity`, `Offset`, `InterfaceId`, and `Priority` fields on the |
| * cloned message are also copied from the original one. |
| * |
| * @param[in] aLength Number of payload bytes to copy. |
| * |
| * @returns A pointer to the message or NULL if insufficient message buffers are available. |
| * |
| */ |
| Message *Clone(uint16_t aLength) const; |
| |
| /** |
| * This method creates a copy of the message. |
| * |
| * It allocates the new message from the same message pool as the original one and copies the entire payload. The |
| * `Type`, `SubType`, `LinkSecurity`, `Offset`, `InterfaceId`, and `Priority` fields on the cloned message are also |
| * copied from the original one. |
| * |
| * @returns A pointer to the message or NULL if insufficient message buffers are available. |
| * |
| */ |
| Message *Clone(void) const { return Clone(GetLength()); } |
| |
| /** |
| * This method returns the minimal reserved bytes required for CoAP message. |
| * |
| */ |
| static uint16_t GetHelpDataReserved(void) { return sizeof(HelpData) + kHelpDataAlignment; } |
| |
| private: |
| /** |
| * Protocol Constants (RFC 7252). |
| * |
| */ |
| enum |
| { |
| kOptionDeltaOffset = 4, ///< Delta Offset |
| kOptionDeltaMask = 0xf << kOptionDeltaOffset, ///< Delta Mask |
| |
| kMaxTokenLength = OT_COAP_MAX_TOKEN_LENGTH, |
| |
| kVersionMask = 0xc0, ///< Version mask as specified (RFC 7252). |
| kVersionOffset = 6, ///< Version offset as specified (RFC 7252). |
| |
| kTypeMask = 0x30, ///< Type mask as specified (RFC 7252). |
| |
| kTokenLengthMask = 0x0f, ///< Token Length mask as specified (RFC 7252). |
| kTokenLengthOffset = 0, ///< Token Length offset as specified (RFC 7252). |
| kTokenOffset = 4, ///< Token offset as specified (RFC 7252). |
| |
| kMaxOptionHeaderSize = 5, ///< Maximum size of an Option header |
| |
| kOption1ByteExtension = 13, ///< Indicates a 1 byte extension (RFC 7252). |
| kOption2ByteExtension = 14, ///< Indicates a 1 byte extension (RFC 7252). |
| |
| kOption1ByteExtensionOffset = 13, ///< Delta/Length offset as specified (RFC 7252). |
| kOption2ByteExtensionOffset = 269, ///< Delta/Length offset as specified (RFC 7252). |
| |
| kHelpDataAlignment = sizeof(uint16_t), ///< Alignment of help data. |
| }; |
| |
| /** |
| * This structure represents a CoAP header excluding CoAP options. |
| * |
| */ |
| OT_TOOL_PACKED_BEGIN |
| struct Header |
| { |
| uint8_t mVersionTypeToken; ///< The CoAP Version, Type, and Token Length |
| uint8_t mCode; ///< The CoAP Code |
| uint16_t mMessageId; ///< The CoAP Message ID |
| uint8_t mToken[OT_COAP_MAX_TOKEN_LENGTH]; ///< The CoAP Token |
| } OT_TOOL_PACKED_END; |
| |
| /** |
| * This structure represents a HelpData used by this CoAP message. |
| * |
| */ |
| struct HelpData |
| { |
| void Clear(void) { memset(this, 0, sizeof(*this)); } |
| |
| Header mHeader; |
| uint16_t mOptionLast; |
| uint16_t mHeaderOffset; ///< The byte offset for the CoAP Header |
| uint16_t mHeaderLength; |
| }; |
| |
| const HelpData &GetHelpData(void) const |
| { |
| OT_STATIC_ASSERT(sizeof(mBuffer.mHead.mInfo) + sizeof(HelpData) + kHelpDataAlignment <= sizeof(mBuffer), |
| "Insufficient buffer size for CoAP processing!"); |
| |
| return *static_cast<const HelpData *>(OT_ALIGN(mBuffer.mHead.mData, kHelpDataAlignment)); |
| } |
| |
| 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); } |
| }; |
| |
| /** |
| * @} |
| * |
| */ |
| |
| } // namespace Coap |
| } // namespace ot |
| |
| #endif // COAP_HEADER_HPP_ |