blob: bcc9dbaf9d71c443fa790d9ba6f4662f45c576fa [file] [log] [blame]
/*
* 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.
*/
#ifndef COAP_HPP_
#define COAP_HPP_
#include <openthread/coap.h>
#include "coap/coap_header.hpp"
#include "common/debug.hpp"
#include "common/locator.hpp"
#include "common/message.hpp"
#include "common/timer.hpp"
#include "net/ip6.hpp"
#include "net/netif.hpp"
#include "net/udp6.hpp"
/**
* @file
* This file includes definitions for CoAP client and server functionality.
*/
namespace ot {
class ThreadNetif;
namespace Coap {
/**
* @addtogroup core-coap
*
* @{
*
*/
/**
* Protocol Constants (RFC 7252).
*
*/
enum
{
kAckTimeout = OPENTHREAD_CONFIG_COAP_ACK_TIMEOUT,
kAckRandomFactorNumerator = OPENTHREAD_CONFIG_COAP_ACK_RANDOM_FACTOR_NUMERATOR,
kAckRandomFactorDenominator = OPENTHREAD_CONFIG_COAP_ACK_RANDOM_FACTOR_DENOMINATOR,
kMaxRetransmit = OPENTHREAD_CONFIG_COAP_MAX_RETRANSMIT,
kNStart = 1,
kDefaultLeisure = 5,
kProbingRate = 1,
// Note that 2 << (kMaxRetransmit - 1) is equal to kMaxRetransmit power of 2
kMaxTransmitSpan = kAckTimeout * ((2 << (kMaxRetransmit - 1)) - 1) *
kAckRandomFactorNumerator / kAckRandomFactorDenominator,
kMaxTransmitWait = kAckTimeout * ((2 << kMaxRetransmit) - 1) *
kAckRandomFactorNumerator / kAckRandomFactorDenominator,
kMaxLatency = 100,
kProcessingDelay = kAckTimeout,
kMaxRtt = 2 * kMaxLatency + kProcessingDelay,
kExchangeLifetime = kMaxTransmitSpan + 2 * (kMaxLatency) + kProcessingDelay,
kNonLifetime = kMaxTransmitSpan + kMaxLatency
};
/**
* This class implements metadata required for CoAP retransmission.
*
*/
OT_TOOL_PACKED_BEGIN
class CoapMetadata
{
friend class Coap;
public:
/**
* Default constructor for the object.
*
*/
CoapMetadata(void):
mDestinationPort(0),
mResponseHandler(NULL),
mResponseContext(NULL),
mNextTimerShot(0),
mRetransmissionTimeout(0),
mRetransmissionCount(0),
mAcknowledged(false),
mConfirmable(false) {};
/**
* This constructor initializes the object with specific values.
*
* @param[in] aConfirmable Information if the request is confirmable or not.
* @param[in] aMessageInfo Addressing information.
* @param[in] aHandler Pointer to a handler function for the response.
* @param[in] aContext Context for the handler function.
*
*/
CoapMetadata(bool aConfirmable, const Ip6::MessageInfo &aMessageInfo,
otCoapResponseHandler aHandler, void *aContext);
/**
* This method appends request data to the message.
*
* @param[in] aMessage A reference to the message.
*
* @retval OT_ERROR_NONE Successfully appended the bytes.
* @retval OT_ERROR_NO_BUFS Insufficient available buffers to grow the message.
*
*/
otError AppendTo(Message &aMessage) const {
return aMessage.Append(this, sizeof(*this));
};
/**
* This method reads request data from the message.
*
* @param[in] aMessage A reference to the message.
*
* @returns The number of bytes read.
*
*/
uint16_t ReadFrom(const Message &aMessage) {
return aMessage.Read(aMessage.GetLength() - sizeof(*this), sizeof(*this), this);
};
/**
* This method updates request data in the message.
*
* @param[in] aMessage A reference to the message.
*
* @returns The number of bytes updated.
*
*/
int UpdateIn(Message &aMessage) const {
return aMessage.Write(aMessage.GetLength() - sizeof(*this), sizeof(*this), this);
}
/**
* This method checks if the message shall be sent before the given time.
*
* @param[in] aTime A time to compare.
*
* @retval TRUE If the message shall be sent before the given time.
* @retval FALSE Otherwise.
*/
bool IsEarlier(uint32_t aTime) const { return (static_cast<int32_t>(aTime - mNextTimerShot) > 0); };
/**
* This method checks if the message shall be sent after the given time.
*
* @param[in] aTime A time to compare.
*
* @retval TRUE If the message shall be sent after the given time.
* @retval FALSE Otherwise.
*/
bool IsLater(uint32_t aTime) const { return (static_cast<int32_t>(aTime - mNextTimerShot) < 0); };
private:
Ip6::Address mSourceAddress; ///< IPv6 address of the message source.
Ip6::Address mDestinationAddress; ///< IPv6 address of the message destination.
uint16_t mDestinationPort; ///< UDP port of the message destination.
otCoapResponseHandler mResponseHandler; ///< A function pointer that is called on response reception.
void *mResponseContext; ///< A pointer to arbitrary context information.
uint32_t mNextTimerShot; ///< Time when the timer should shoot for this message.
uint32_t mRetransmissionTimeout; ///< Delay that is applied to next retransmission.
uint8_t mRetransmissionCount; ///< Number of retransmissions.
bool mAcknowledged: 1; ///< Information that request was acknowledged.
bool mConfirmable: 1; ///< Information that message is confirmable.
} OT_TOOL_PACKED_END;
/**
* This class implements CoAP resource handling.
*
*/
class Resource : public otCoapResource
{
friend class Coap;
public:
enum
{
kMaxReceivedUriPath = 32, ///< Maximum supported URI path on received messages.
};
/**
* This constructor initializes the resource.
*
* @param[in] aUriPath A pointer to a NULL-terminated string for the Uri-Path.
* @param[in] aHandler A function pointer that is called when receiving a CoAP message for @p aUriPath.
* @param[in] aContext A pointer to arbitrary context information.
*/
Resource(const char *aUriPath, otCoapRequestHandler aHandler, void *aContext) {
mUriPath = aUriPath;
mHandler = aHandler;
mContext = aContext;
mNext = NULL;
}
/**
* This method returns a pointer to the next resource.
*
* @returns A Pointer to the next resource.
*
*/
Resource *GetNext(void) const { return static_cast<Resource *>(mNext); };
private:
void HandleRequest(Header &aHeader, Message &aMessage, const Ip6::MessageInfo &aMessageInfo) const {
mHandler(mContext, &aHeader, &aMessage, &aMessageInfo);
}
};
/**
* This class implements metadata required for caching CoAP responses.
*
*/
class EnqueuedResponseHeader
{
public:
/**
* Default constructor creating empty object.
*
*/
EnqueuedResponseHeader(void): mDequeueTime(0), mMessageInfo() {}
/**
* Constructor creating object with valid dequeue time and message info.
*
* @param[in] aMessageInfo The message info containing source endpoint identification.
*
*/
EnqueuedResponseHeader(const Ip6::MessageInfo &aMessageInfo):
mDequeueTime(Timer::GetNow() + Timer::SecToMsec(kExchangeLifetime)),
mMessageInfo(aMessageInfo) {}
/**
* This method append metadata to the message.
*
* @param[in] aMessage A reference to the message.
*
* @retval OT_ERROR_NONE Successfully appended the bytes.
* @retval OT_ERROR_NO_BUFS Insufficient available buffers to grow the message.
*/
otError AppendTo(Message &aMessage) const { return aMessage.Append(this, sizeof(*this)); }
/**
* This method reads request data from the message.
*
* @param[in] aMessage A reference to the message.
*
* @returns The number of bytes read.
*
*/
uint16_t ReadFrom(const Message &aMessage) {
return aMessage.Read(aMessage.GetLength() - sizeof(*this), sizeof(*this), this);
}
/**
* This method removes metadata from the message.
*
* @param[in] aMessage A reference to the message.
*
*/
static void RemoveFrom(Message &aMessage) {
assert(aMessage.SetLength(aMessage.GetLength() - sizeof(EnqueuedResponseHeader)) == OT_ERROR_NONE);
}
/**
* This method checks if the message shall be sent before the given time.
*
* @param[in] aTime A time to compare.
*
* @retval TRUE If the message shall be sent before the given time.
* @retval FALSE Otherwise.
*
*/
bool IsEarlier(uint32_t aTime) const { return (static_cast<int32_t>(aTime - mDequeueTime) > 0); }
/**
* This method returns number of milliseconds in which the message should be sent.
*
* @returns The number of milliseconds in which the message should be sent.
*
*/
uint32_t GetRemainingTime(void) const;
/**
* This method returns the message info of cached CoAP response.
*
* @returns The message info of the cached CoAP response.
*
*/
const Ip6::MessageInfo &GetMessageInfo(void) const { return mMessageInfo; }
private:
uint32_t mDequeueTime;
const Ip6::MessageInfo mMessageInfo;
};
/**
* This class caches CoAP responses to implement message deduplication.
*
*/
class ResponsesQueue
{
public:
/**
* Default class constructor.
*
* @param[in] aNetif A reference to the network interface that CoAP server should be assigned to.
*
*/
ResponsesQueue(ThreadNetif &aNetif);
/**
* Add given response to the cache.
*
* If matching response (the same Message ID, source endpoint address and port) exists in the cache given
* response is not added.
* The CoAP response is copied before it is added to the cache.
*
* @param[in] aMessage The CoAP response to add to the cache.
* @param[in] aMessageInfo The message info corresponding to @p aMessage.
*
*/
void EnqueueResponse(Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
/**
* Remove the oldest response from the cache.
*
*/
void DequeueOldestResponse(void);
/**
* Remove all responses from the cache.
*
*/
void DequeueAllResponses(void);
/**
* Get a copy of CoAP response from the cache that matches given Message ID and source endpoint.
*
* @param[in] aHeader The CoAP message header containing Message ID.
* @param[in] aMessageInfo The message info containing source endpoint address and port.
* @param[out] aResponse A pointer to a copy of a cached CoAP response matching given arguments.
*
* @retval OT_ERROR_NONE Matching response found and successfully created a copy.
* @retval OT_ERROR_NO_BUFS Matching response found but there is not sufficient buffer to create a copy.
* @retval OT_ERROR_NOT_FOUND Matching response not found.
*
*/
otError GetMatchedResponseCopy(const Header &aHeader,
const Ip6::MessageInfo &aMessageInfo,
Message **aResponse);
/**
* Get a copy of CoAP response from the cache that matches given Message ID and source endpoint.
*
* @param[in] aRequest The CoAP message containing Message ID.
* @param[in] aMessageInfo The message info containing source endpoint address and port.
* @param[out] aResponse A pointer to a copy of a cached CoAP response matching given arguments.
*
* @retval OT_ERROR_NONE Matching response found and successfully created a copy.
* @retval OT_ERROR_NO_BUFS Matching response found but there is not sufficient buffer to create a copy.
* @retval OT_ERROR_NOT_FOUND Matching response not found.
* @retval OT_ERROR_PARSE Could not parse CoAP header in the request message.
*
*/
otError GetMatchedResponseCopy(const Message &aRequest,
const Ip6::MessageInfo &aMessageInfo,
Message **aResponse);
/**
* Get a reference to the cached CoAP responses queue.
*
* @returns A reference to the cached CoAP responses queue.
*
*/
const MessageQueue &GetResponses(void) const { return mQueue; }
private:
enum
{
kMaxCachedResponses = OPENTHREAD_CONFIG_COAP_SERVER_MAX_CACHED_RESPONSES,
};
void DequeueResponse(Message &aMessage) { mQueue.Dequeue(aMessage); aMessage.Free(); }
static ResponsesQueue &GetOwner(const Context &aContext);
static void HandleTimer(Timer &aTimer);
void HandleTimer(void);
MessageQueue mQueue;
Timer mTimer;
};
/**
* This class implements the CoAP client and server.
*
*/
class Coap: public ThreadNetifLocator
{
friend class ResponsesQueue;
public:
/**
* This function pointer is called before CoAP server processing a CoAP packets.
*
* @param[in] aMessage A reference to the message.
@ @param[in] aMessageInfo A reference to the message info associated with @p aMessage.
* @param[in] aContext A pointer to arbitrary context information.
*
* @retval OT_ERROR_NONE Server should continue processing this message, other
* return values indicates the server should stop processing
* this message.
* @retval OT_ERROR_NOT_TMF The message is not a TMF message.
*
*/
typedef otError(* Interceptor)(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo, void *aContext);
/**
* This constructor initializes the object.
*
* @param[in] aNetif A reference to the Netif object.
*
*/
Coap(ThreadNetif &aNetif);
/**
* This method starts the CoAP service.
*
* @param[in] aPort The local UDP port to bind to.
*
* @retval OT_ERROR_NONE Successfully started the CoAP service.
*
*/
otError Start(uint16_t aPort);
/**
* This method stops the CoAP service.
*
* @retval OT_ERROR_NONE Successfully stopped the CoAP service.
*
*/
otError Stop(void);
/**
* This method returns a port number used by CoAP service.
*
* @returns A port number.
*
*/
uint16_t GetPort(void) { return mSocket.GetSockName().mPort; };
/**
* This method adds a resource to the CoAP server.
*
* @param[in] aResource A reference to the resource.
*
* @retval OT_ERROR_NONE Successfully added @p aResource.
* @retval OT_ERROR_ALREADY The @p aResource was already added.
*
*/
otError AddResource(Resource &aResource);
/**
* This method removes a resource from the CoAP server.
*
* @param[in] aResource A reference to the resource.
*
*/
void RemoveResource(Resource &aResource);
/* This function sets the default handler for unhandled CoAP requests.
*
* @param[in] aHandler A function pointer that shall be called when an unhandled request arrives.
* @param[in] aContext A pointer to arbitrary context information. May be NULL if not used.
*/
void SetDefaultHandler(otCoapRequestHandler aHandler, void *aContext);
/**
* This method creates a new message with a CoAP header.
*
* @param[in] aHeader A reference to a CoAP header that is used to create the message.
* @param[in] aPrority The message priority level.
*
* @returns A pointer to the message or NULL if failed to allocate message.
*
*/
Message *NewMessage(const Header &aHeader, uint8_t aPriority = kDefaultCoapMessagePriority);
/**
* This method sends a CoAP message.
*
* If a response for a request is expected, respective function and context information should be provided.
* If no response is expected, these arguments should be NULL pointers.
* If Message Id was not set in the header (equal to 0), this function will assign unique Message Id to the message.
*
* @param[in] aMessage A reference to the message to send.
* @param[in] aMessageInfo A reference to the message info associated with @p aMessage.
* @param[in] aHandler A function pointer that shall be called on response reception or time-out.
* @param[in] aContext A pointer to arbitrary context information.
*
* @retval OT_ERROR_NONE Successfully sent CoAP message.
* @retval OT_ERROR_NO_BUFS Failed to allocate retransmission data.
*
*/
otError SendMessage(Message &aMessage, const Ip6::MessageInfo &aMessageInfo,
otCoapResponseHandler aHandler = NULL, void *aContext = NULL);
/**
* This method sends a CoAP reset message.
*
* @param[in] aRequestHeader A reference to the CoAP Header that was used in CoAP request.
* @param[in] aMessageInfo The message info corresponding to the CoAP request.
*
* @retval OT_ERROR_NONE Successfully enqueued the CoAP response message.
* @retval OT_ERROR_NO_BUFS Insufficient buffers available to send the CoAP response.
* @retval OT_ERROR_INVALID_ARGS The @p aRequestHeader header is not of confirmable type.
*
*/
otError SendReset(Header &aRequestHeader, const Ip6::MessageInfo &aMessageInfo) {
return SendEmptyMessage(OT_COAP_TYPE_RESET, aRequestHeader, aMessageInfo);
};
/**
* This method sends header-only CoAP response message.
*
* @param[in] aCode The CoAP code of this response.
* @param[in] aRequestHeader A reference to the CoAP Header that was used in CoAP request.
* @param[in] aMessageInfo The message info corresponding to the CoAP request.
*
* @retval OT_ERROR_NONE Successfully enqueued the CoAP response message.
* @retval OT_ERROR_NO_BUFS Insufficient buffers available to send the CoAP response.
* @retval OT_ERROR_INVALID_ARGS The @p aRequestHeader header is not of confirmable type.
*
*/
otError SendHeaderResponse(Header::Code aCode, const Header &aRequestHeader, const Ip6::MessageInfo &aMessageInfo);
/**
* This method sends a CoAP ACK empty message which is used in Separate Response for confirmable requests.
*
* @param[in] aRequestHeader A reference to the CoAP Header that was used in CoAP request.
* @param[in] aMessageInfo The message info corresponding to the CoAP request.
*
* @retval OT_ERROR_NONE Successfully enqueued the CoAP response message.
* @retval OT_ERROR_NO_BUFS Insufficient buffers available to send the CoAP response.
* @retval OT_ERROR_INVALID_ARGS The @p aRequestHeader header is not of confirmable type.
*
*/
otError SendAck(Header &aRequestHeader, const Ip6::MessageInfo &aMessageInfo) {
return SendEmptyMessage(OT_COAP_TYPE_ACKNOWLEDGMENT, aRequestHeader, aMessageInfo);
};
/**
* This method sends a CoAP ACK message on which a dummy CoAP response is piggybacked.
*
* @param[in] aRequestHeader A reference to the CoAP Header that was used in CoAP request.
* @param[in] aMessageInfo The message info corresponding to the CoAP request.
*
* @retval OT_ERROR_NONE Successfully enqueued the CoAP response message.
* @retval OT_ERROR_NO_BUFS Insufficient buffers available to send the CoAP response.
* @retval OT_ERROR_INVALID_ARGS The @p aRequestHeader header is not of confirmable type.
*
*/
otError SendEmptyAck(const Header &aRequestHeader, const Ip6::MessageInfo &aMessageInfo) {
return (aRequestHeader.GetType() == OT_COAP_TYPE_CONFIRMABLE ?
SendHeaderResponse(OT_COAP_CODE_CHANGED, aRequestHeader, aMessageInfo) :
OT_ERROR_INVALID_ARGS);
}
/**
* This method sends a header-only CoAP message to indicate no resource matched for the request.
*
* @param[in] aRequestHeader A reference to the CoAP Header that was used in CoAP request.
* @param[in] aMessageInfo The message info corresponding to the CoAP request.
*
* @retval OT_ERROR_NONE Successfully enqueued the CoAP response message.
* @retval OT_ERROR_NO_BUFS Insufficient buffers available to send the CoAP response.
*
*/
otError SendNotFound(const Header &aRequestHeader, const Ip6::MessageInfo &aMessageInfo) {
return SendHeaderResponse(OT_COAP_CODE_NOT_FOUND, aRequestHeader, aMessageInfo);
}
/**
* This method aborts CoAP transactions associated with given handler and context.
*
* @param[in] aHandler A function pointer that should be called when the transaction ends.
* @param[in] aContext A pointer to arbitrary context information.
*
* @retval OT_ERROR_NONE Successfully aborted CoAP transactions.
* @retval OT_ERROR_NOT_FOUND CoAP transaction associated with given handler was not found.
*
*/
otError AbortTransaction(otCoapResponseHandler aHandler, void *aContext);
/**
* This method sets interceptor to be called before processing a CoAP packet.
*
* @param[in] aInterceptor A pointer to the interceptor.
* @param[in] aContext A pointer to arbitrary context information.
*
*/
void SetInterceptor(Interceptor aInterceptor, void *aContext) {
mInterceptor = aInterceptor;
mContext = aContext;
}
/**
* This method returns a reference to the request message list.
*
* @returns A reference to the request message list.
*
*/
const MessageQueue &GetRequestMessages(void) const { return mPendingRequests; }
/**
* This method returns a reference to the cached response list.
*
* @returns A reference to the cached response list.
*
*/
const MessageQueue &GetCachedResponses(void) const { return mResponsesQueue.GetResponses(); }
protected:
/**
* This method send a message.
*
* @param[in] aMessage A reference to the message to send.
* @param[in] aMessageInfo A reference to the message info associated with @p aMessage.
*
*/
virtual otError Send(Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
/**
* This method receives a message.
*
* @param[in] aMessage A reference to the received message.
* @param[in] aMessageInfo A reference to the message info associated with @p aMessage.
*
*/
virtual void Receive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
Ip6::UdpSocket mSocket;
private:
enum
{
kDefaultCoapMessagePriority = Message::kPriorityLow,
};
static void HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo);
Message *CopyAndEnqueueMessage(const Message &aMessage, uint16_t aCopyLength,
const CoapMetadata &aCoapMetadata);
void DequeueMessage(Message &aMessage);
Message *FindRelatedRequest(const Header &aResponseHeader, const Ip6::MessageInfo &aMessageInfo,
Header &aRequestHeader, CoapMetadata &aCoapMetadata);
void FinalizeCoapTransaction(Message &aRequest, const CoapMetadata &aCoapMetadata, Header *aResponseHeader,
Message *aResponse, const Ip6::MessageInfo *aMessageInfo, otError aResult);
void ProcessReceivedRequest(Header &aHeader, Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
void ProcessReceivedResponse(Header &aHeader, Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
otError SendCopy(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
otError SendEmptyMessage(Header::Type aType, const Header &aRequestHeader,
const Ip6::MessageInfo &aMessageInfo);
static void HandleRetransmissionTimer(Timer &aTimer);
void HandleRetransmissionTimer(void);
static Coap &GetOwner(const Context &aContext);
MessageQueue mPendingRequests;
uint16_t mMessageId;
Timer mRetransmissionTimer;
Resource *mResources;
void *mContext;
Interceptor mInterceptor;
ResponsesQueue mResponsesQueue;
otCoapRequestHandler mDefaultHandler;
void *mDefaultHandlerContext;
};
} // namespace Coap
} // namespace ot
#endif // COAP_HPP_