#include "coap.hpp"
#include "common/code_utils.hpp"
#include "common/debug.hpp"
#include "common/instance.hpp"
#include "common/locator-getters.hpp"
#include "common/logging.hpp"
#include "common/random.hpp"
#include "net/ip6.hpp"
#include "net/udp6.hpp"
#include "thread/thread_netif.hpp"
namespace ot {
namespace Coap {
CoapBase::CoapBase(Instance &aInstance, Sender aSender)
: InstanceLocator(aInstance)
, mPendingRequests()
, mMessageId(Random::NonCrypto::GetUint16())
, mRetransmissionTimer(aInstance, &Coap::HandleRetransmissionTimer, this)
, mResources()
, mContext(NULL)
, mInterceptor(NULL)
, mResponsesQueue(aInstance)
, mDefaultHandler(NULL)
, mDefaultHandlerContext(NULL)
, mSender(aSender)
{
}
void CoapBase::ClearRequestsAndResponses(void)
{
ClearRequests(NULL); mResponsesQueue.DequeueAllResponses();
}
void CoapBase::ClearRequests(const Ip6::Address &aAddress)
{
ClearRequests(&aAddress);
}
void CoapBase::ClearRequests(const Ip6::Address *aAddress)
{
Message *nextMessage;
for (Message *message = mPendingRequests.GetHead(); message != NULL; message = nextMessage)
{
Metadata metadata;
nextMessage = message->GetNextCoapMessage();
metadata.ReadFrom(*message);
if ((aAddress == NULL) || (metadata.mSourceAddress == *aAddress))
{
FinalizeCoapTransaction(*message, metadata, NULL, NULL, OT_ERROR_ABORT);
}
}
}
void CoapBase::AddResource(Resource &aResource)
{
IgnoreError(mResources.Add(aResource));
}
void CoapBase::RemoveResource(Resource &aResource)
{
IgnoreError(mResources.Remove(aResource));
aResource.SetNext(NULL);
}
void CoapBase::SetDefaultHandler(RequestHandler aHandler, void *aContext)
{
mDefaultHandler = aHandler;
mDefaultHandlerContext = aContext;
}
void CoapBase::SetInterceptor(Interceptor aInterceptor, void *aContext)
{
mInterceptor = aInterceptor;
mContext = aContext;
}
Message *CoapBase::NewMessage(const Message::Settings &aSettings)
{
Message *message = NULL;
VerifyOrExit((message = static_cast<Message *>(Get<Ip6::Udp>().NewMessage(0, aSettings))) != NULL, OT_NOOP);
message->SetOffset(0);
exit:
return message;
}
otError CoapBase::Send(ot::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
{
return mSender(*this, aMessage, aMessageInfo);
}
otError CoapBase::SendMessage(Message & aMessage,
const Ip6::MessageInfo &aMessageInfo,
const TxParameters & aTxParameters,
ResponseHandler aHandler,
void * aContext)
{
otError error;
Message *storedCopy = NULL;
uint16_t copyLength = 0;
switch (aMessage.GetType())
{
case OT_COAP_TYPE_ACKNOWLEDGMENT:
mResponsesQueue.EnqueueResponse(aMessage, aMessageInfo, aTxParameters);
break;
case OT_COAP_TYPE_RESET:
OT_ASSERT(aMessage.GetCode() == OT_COAP_CODE_EMPTY);
break;
default:
aMessage.SetMessageId(mMessageId++);
break;
}
aMessage.Finish();
if (aMessage.IsConfirmable())
{
copyLength = aMessage.GetLength();
}
else if (aMessage.IsNonConfirmable() && (aHandler != NULL))
{
copyLength = aMessage.GetOptionStart();
}
if (copyLength > 0)
{
Metadata metadata;
#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
OptionIterator iterator;
bool observe;
SuccessOrExit(error = iterator.Init(&aMessage));
observe = (iterator.GetFirstOptionMatching(OT_COAP_OPTION_OBSERVE) != NULL);
if (observe && (aMessage.GetCode() == OT_COAP_CODE_GET))
{
uint64_t observeVal = 0;
SuccessOrExit(error = iterator.GetOptionValue(observeVal));
if (observeVal == 1)
{
Metadata handlerMetadata;
observe = false;
Message *origRequest = FindRelatedRequest(aMessage, aMessageInfo, handlerMetadata);
if (origRequest != NULL)
{
FinalizeCoapTransaction(*origRequest, handlerMetadata, NULL, NULL, OT_ERROR_NONE);
}
}
}
#endif
metadata.mSourceAddress = aMessageInfo.GetSockAddr();
metadata.mDestinationPort = aMessageInfo.GetPeerPort();
metadata.mDestinationAddress = aMessageInfo.GetPeerAddr();
metadata.mResponseHandler = aHandler;
metadata.mResponseContext = aContext;
metadata.mRetransmissionsRemaining = aTxParameters.mMaxRetransmit;
metadata.mRetransmissionTimeout = aTxParameters.CalculateInitialRetransmissionTimeout();
metadata.mAcknowledged = false;
metadata.mConfirmable = aMessage.IsConfirmable();
#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
metadata.mObserve = observe;
#endif
metadata.mNextTimerShot =
TimerMilli::GetNow() +
(metadata.mConfirmable ? metadata.mRetransmissionTimeout : aTxParameters.CalculateMaxTransmitWait());
storedCopy = CopyAndEnqueueMessage(aMessage, copyLength, metadata);
VerifyOrExit(storedCopy != NULL, error = OT_ERROR_NO_BUFS);
}
SuccessOrExit(error = Send(aMessage, aMessageInfo));
exit:
if (error != OT_ERROR_NONE && storedCopy != NULL)
{
DequeueMessage(*storedCopy);
}
return error;
}
otError CoapBase::SendMessage(Message & aMessage,
const Ip6::MessageInfo &aMessageInfo,
ResponseHandler aHandler,
void * aContext)
{
return SendMessage(aMessage, aMessageInfo, TxParameters::GetDefault(), aHandler, aContext);
}
otError CoapBase::SendReset(Message &aRequest, const Ip6::MessageInfo &aMessageInfo)
{
return SendEmptyMessage(OT_COAP_TYPE_RESET, aRequest, aMessageInfo);
}
otError CoapBase::SendAck(const Message &aRequest, const Ip6::MessageInfo &aMessageInfo)
{
return SendEmptyMessage(OT_COAP_TYPE_ACKNOWLEDGMENT, aRequest, aMessageInfo);
}
otError CoapBase::SendEmptyAck(const Message &aRequest, const Ip6::MessageInfo &aMessageInfo)
{
return (aRequest.IsConfirmable() ? SendHeaderResponse(OT_COAP_CODE_CHANGED, aRequest, aMessageInfo)
: OT_ERROR_INVALID_ARGS);
}
otError CoapBase::SendNotFound(const Message &aRequest, const Ip6::MessageInfo &aMessageInfo)
{
return SendHeaderResponse(OT_COAP_CODE_NOT_FOUND, aRequest, aMessageInfo);
}
otError CoapBase::SendEmptyMessage(Message::Type aType, const Message &aRequest, const Ip6::MessageInfo &aMessageInfo)
{
otError error = OT_ERROR_NONE;
Message *message = NULL;
VerifyOrExit(aRequest.IsConfirmable(), error = OT_ERROR_INVALID_ARGS);
VerifyOrExit((message = NewMessage()) != NULL, error = OT_ERROR_NO_BUFS);
message->Init(aType, OT_COAP_CODE_EMPTY);
message->SetMessageId(aRequest.GetMessageId());
message->Finish();
SuccessOrExit(error = Send(*message, aMessageInfo));
exit:
if (error != OT_ERROR_NONE && message != NULL)
{
message->Free();
}
return error;
}
otError CoapBase::SendHeaderResponse(Message::Code aCode, const Message &aRequest, const Ip6::MessageInfo &aMessageInfo)
{
otError error = OT_ERROR_NONE;
Message *message = NULL;
VerifyOrExit(aRequest.IsRequest(), error = OT_ERROR_INVALID_ARGS);
VerifyOrExit((message = NewMessage()) != NULL, error = OT_ERROR_NO_BUFS);
switch (aRequest.GetType())
{
case OT_COAP_TYPE_CONFIRMABLE:
message->Init(OT_COAP_TYPE_ACKNOWLEDGMENT, aCode);
message->SetMessageId(aRequest.GetMessageId());
break;
case OT_COAP_TYPE_NON_CONFIRMABLE:
message->Init(OT_COAP_TYPE_NON_CONFIRMABLE, aCode);
break;
default:
ExitNow(error = OT_ERROR_INVALID_ARGS);
OT_UNREACHABLE_CODE(break);
}
SuccessOrExit(error = message->SetToken(aRequest.GetToken(), aRequest.GetTokenLength()));
SuccessOrExit(error = SendMessage(*message, aMessageInfo));
exit:
if (error != OT_ERROR_NONE && message != NULL)
{
message->Free();
}
return error;
}
void CoapBase::HandleRetransmissionTimer(Timer &aTimer)
{
static_cast<Coap *>(static_cast<TimerMilliContext &>(aTimer).GetContext())->HandleRetransmissionTimer();
}
void CoapBase::HandleRetransmissionTimer(void)
{
TimeMilli now = TimerMilli::GetNow();
TimeMilli nextTime = now.GetDistantFuture();
Metadata metadata;
Message * nextMessage;
Ip6::MessageInfo messageInfo;
for (Message *message = mPendingRequests.GetHead(); message != NULL; message = nextMessage)
{
nextMessage = message->GetNextCoapMessage();
metadata.ReadFrom(*message);
if (now >= metadata.mNextTimerShot)
{
#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
if (message->IsRequest() && metadata.mObserve && metadata.mAcknowledged)
{
continue;
}
#endif
if (!metadata.mConfirmable || (metadata.mRetransmissionsRemaining == 0))
{
FinalizeCoapTransaction(*message, metadata, NULL, NULL, OT_ERROR_RESPONSE_TIMEOUT);
continue;
}
metadata.mRetransmissionsRemaining--;
metadata.mRetransmissionTimeout *= 2;
metadata.mNextTimerShot = now + metadata.mRetransmissionTimeout;
metadata.UpdateIn(*message);
if (!metadata.mAcknowledged)
{
messageInfo.SetPeerAddr(metadata.mDestinationAddress);
messageInfo.SetPeerPort(metadata.mDestinationPort);
messageInfo.SetSockAddr(metadata.mSourceAddress);
SendCopy(*message, messageInfo);
}
}
if (nextTime > metadata.mNextTimerShot)
{
nextTime = metadata.mNextTimerShot;
}
}
if (nextTime < now.GetDistantFuture())
{
mRetransmissionTimer.FireAt(nextTime);
}
}
void CoapBase::FinalizeCoapTransaction(Message & aRequest,
const Metadata & aMetadata,
Message * aResponse,
const Ip6::MessageInfo *aMessageInfo,
otError aResult)
{
DequeueMessage(aRequest);
if (aMetadata.mResponseHandler != NULL)
{
aMetadata.mResponseHandler(aMetadata.mResponseContext, aResponse, aMessageInfo, aResult);
}
}
otError CoapBase::AbortTransaction(ResponseHandler aHandler, void *aContext)
{
otError error = OT_ERROR_NOT_FOUND;
Message *nextMessage;
Metadata metadata;
for (Message *message = mPendingRequests.GetHead(); message != NULL; message = nextMessage)
{
nextMessage = message->GetNextCoapMessage();
metadata.ReadFrom(*message);
if (metadata.mResponseHandler == aHandler && metadata.mResponseContext == aContext)
{
FinalizeCoapTransaction(*message, metadata, NULL, NULL, OT_ERROR_ABORT);
error = OT_ERROR_NONE;
}
}
return error;
}
Message *CoapBase::CopyAndEnqueueMessage(const Message &aMessage, uint16_t aCopyLength, const Metadata &aMetadata)
{
otError error = OT_ERROR_NONE;
Message *messageCopy = NULL;
VerifyOrExit((messageCopy = aMessage.Clone(aCopyLength)) != NULL, error = OT_ERROR_NO_BUFS);
SuccessOrExit(error = aMetadata.AppendTo(*messageCopy));
mRetransmissionTimer.FireAtIfEarlier(aMetadata.mNextTimerShot);
mPendingRequests.Enqueue(*messageCopy);
exit:
if (error != OT_ERROR_NONE && messageCopy != NULL)
{
messageCopy->Free();
messageCopy = NULL;
}
return messageCopy;
}
void CoapBase::DequeueMessage(Message &aMessage)
{
mPendingRequests.Dequeue(aMessage);
if (mRetransmissionTimer.IsRunning() && (mPendingRequests.GetHead() == NULL))
{
mRetransmissionTimer.Stop();
}
aMessage.Free();
}
void CoapBase::SendCopy(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
{
otError error;
Message *messageCopy = NULL;
messageCopy = aMessage.Clone(aMessage.GetLength() - sizeof(Metadata));
VerifyOrExit(messageCopy != NULL, error = OT_ERROR_NO_BUFS);
SuccessOrExit(error = Send(*messageCopy, aMessageInfo));
exit:
if (error != OT_ERROR_NONE)
{
otLogWarnCoap("Failed to send copy: %s", otThreadErrorToString(error));
if (messageCopy != NULL)
{
messageCopy->Free();
}
}
}
Message *CoapBase::FindRelatedRequest(const Message & aResponse,
const Ip6::MessageInfo &aMessageInfo,
Metadata & aMetadata)
{
Message *message;
for (message = mPendingRequests.GetHead(); message != NULL; message = message->GetNextCoapMessage())
{
aMetadata.ReadFrom(*message);
if (((aMetadata.mDestinationAddress == aMessageInfo.GetPeerAddr()) ||
aMetadata.mDestinationAddress.IsMulticast() || aMetadata.mDestinationAddress.IsIidAnycastLocator()) &&
(aMetadata.mDestinationPort == aMessageInfo.GetPeerPort()))
{
switch (aResponse.GetType())
{
case OT_COAP_TYPE_RESET:
case OT_COAP_TYPE_ACKNOWLEDGMENT:
if (aResponse.GetMessageId() == message->GetMessageId())
{
ExitNow();
}
break;
case OT_COAP_TYPE_CONFIRMABLE:
case OT_COAP_TYPE_NON_CONFIRMABLE:
if (aResponse.IsTokenEqual(*message))
{
ExitNow();
}
break;
}
}
}
exit:
return message;
}
void CoapBase::Receive(ot::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
{
Message &message = static_cast<Message &>(aMessage);
if (message.ParseHeader() != OT_ERROR_NONE)
{
otLogDebgCoap("Failed to parse CoAP header");
if (!aMessageInfo.GetSockAddr().IsMulticast() && message.IsConfirmable())
{
IgnoreError(SendReset(message, aMessageInfo));
}
}
else if (message.IsRequest())
{
ProcessReceivedRequest(message, aMessageInfo);
}
else
{
ProcessReceivedResponse(message, aMessageInfo);
}
}
void CoapBase::ProcessReceivedResponse(Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
{
Metadata metadata;
Message *request = NULL;
otError error = OT_ERROR_NONE;
#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
bool responseObserve = false;
#endif
request = FindRelatedRequest(aMessage, aMessageInfo, metadata);
VerifyOrExit(request != NULL, OT_NOOP);
#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
if (metadata.mObserve && request->IsRequest())
{
OptionIterator iterator;
SuccessOrExit(error = iterator.Init(&aMessage));
responseObserve = (iterator.GetFirstOptionMatching(OT_COAP_OPTION_OBSERVE) != NULL);
}
#endif
switch (aMessage.GetType())
{
case OT_COAP_TYPE_RESET:
if (aMessage.IsEmpty())
{
FinalizeCoapTransaction(*request, metadata, NULL, NULL, OT_ERROR_ABORT);
}
break;
case OT_COAP_TYPE_ACKNOWLEDGMENT:
if (aMessage.IsEmpty())
{
#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
if (metadata.mObserve && !request->IsRequest())
{
FinalizeCoapTransaction(*request, metadata, &aMessage, &aMessageInfo, OT_ERROR_NONE);
}
else
#endif
{
if (metadata.mConfirmable)
{
metadata.mAcknowledged = true;
metadata.UpdateIn(*request);
}
if (metadata.mResponseHandler == NULL)
{
DequeueMessage(*request);
}
}
}
else if (aMessage.IsResponse() && aMessage.IsTokenEqual(*request))
{
#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
if (metadata.mObserve && responseObserve && (metadata.mResponseHandler != NULL))
{
metadata.mResponseHandler(metadata.mResponseContext, &aMessage, &aMessageInfo, OT_ERROR_NONE);
metadata.mAcknowledged = true;
metadata.UpdateIn(*request);
}
else
#endif
{
FinalizeCoapTransaction(*request, metadata, &aMessage, &aMessageInfo, OT_ERROR_NONE);
}
}
break;
case OT_COAP_TYPE_CONFIRMABLE:
IgnoreError(SendAck(aMessage, aMessageInfo));
case OT_COAP_TYPE_NON_CONFIRMABLE:
if ((metadata.mResponseHandler != NULL) && (metadata.mDestinationAddress.IsMulticast()
#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
|| (metadata.mObserve && responseObserve)
#endif
))
{
metadata.mResponseHandler(metadata.mResponseContext, &aMessage, &aMessageInfo, OT_ERROR_NONE);
}
else
{
FinalizeCoapTransaction(*request, metadata, &aMessage, &aMessageInfo, OT_ERROR_NONE);
}
break;
}
exit:
if (error == OT_ERROR_NONE && request == NULL)
{
if (aMessage.IsConfirmable() || aMessage.IsNonConfirmable())
{
IgnoreError(SendReset(aMessage, aMessageInfo));
}
}
}
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;
OptionIterator iterator;
if (mInterceptor != NULL)
{
SuccessOrExit(error = mInterceptor(aMessage, aMessageInfo, mContext));
}
switch (mResponsesQueue.GetMatchedResponseCopy(aMessage, aMessageInfo, &cachedResponse))
{
case OT_ERROR_NONE:
cachedResponse->Finish();
error = Send(*cachedResponse, aMessageInfo);
case OT_ERROR_NO_BUFS:
ExitNow();
case OT_ERROR_NOT_FOUND:
default:
break;
}
SuccessOrExit(error = iterator.Init(&aMessage));
for (const otCoapOption *option = iterator.GetFirstOption(); option != NULL; option = iterator.GetNextOption())
{
switch (option->mNumber)
{
case OT_COAP_OPTION_URI_PATH:
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[0] = '\0';
for (const Resource *resource = mResources.GetHead(); resource; resource = resource->GetNext())
{
if (strcmp(resource->mUriPath, uriPath) == 0)
{
resource->HandleRequest(aMessage, aMessageInfo);
error = OT_ERROR_NONE;
ExitNow();
}
}
if (mDefaultHandler)
{
mDefaultHandler(mDefaultHandlerContext, &aMessage, &aMessageInfo);
error = OT_ERROR_NONE;
}
exit:
if (error != OT_ERROR_NONE)
{
otLogInfoCoap("Failed to process request: %s", otThreadErrorToString(error));
if (error == OT_ERROR_NOT_FOUND && !aMessageInfo.GetSockAddr().IsMulticast())
{
IgnoreError(SendNotFound(aMessage, aMessageInfo));
}
if (cachedResponse != NULL)
{
cachedResponse->Free();
}
}
}
void CoapBase::Metadata::ReadFrom(const Message &aMessage)
{
uint16_t length = aMessage.GetLength();
OT_ASSERT(length >= sizeof(*this));
aMessage.Read(length - sizeof(*this), sizeof(*this), this);
}
int CoapBase::Metadata::UpdateIn(Message &aMessage) const
{
return aMessage.Write(aMessage.GetLength() - sizeof(*this), sizeof(*this), this);
}
ResponsesQueue::ResponsesQueue(Instance &aInstance)
: mQueue()
, mTimer(aInstance, &ResponsesQueue::HandleTimer, this)
{
}
otError ResponsesQueue::GetMatchedResponseCopy(const Message & aRequest,
const Ip6::MessageInfo &aMessageInfo,
Message ** aResponse)
{
otError error = OT_ERROR_NONE;
const Message *cacheResponse;
cacheResponse = FindMatchedResponse(aRequest, aMessageInfo);
VerifyOrExit(cacheResponse != NULL, error = OT_ERROR_NOT_FOUND);
*aResponse = cacheResponse->Clone(cacheResponse->GetLength() - sizeof(ResponseMetadata));
VerifyOrExit(*aResponse != NULL, error = OT_ERROR_NO_BUFS);
exit:
return error;
}
const Message *ResponsesQueue::FindMatchedResponse(const Message &aRequest, const Ip6::MessageInfo &aMessageInfo) const
{
Message *message;
for (message = mQueue.GetHead(); message != NULL; message = message->GetNextCoapMessage())
{
if (message->GetMessageId() == aRequest.GetMessageId())
{
ResponseMetadata metadata;
metadata.ReadFrom(*message);
if ((metadata.mMessageInfo.GetPeerPort() == aMessageInfo.GetPeerPort()) &&
(metadata.mMessageInfo.GetPeerAddr() == aMessageInfo.GetPeerAddr()))
{
break;
}
}
}
return message;
}
void ResponsesQueue::EnqueueResponse(Message & aMessage,
const Ip6::MessageInfo &aMessageInfo,
const TxParameters & aTxParameters)
{
Message * responseCopy;
ResponseMetadata metadata;
metadata.mDequeueTime = TimerMilli::GetNow() + aTxParameters.CalculateExchangeLifetime();
metadata.mMessageInfo = aMessageInfo;
VerifyOrExit(FindMatchedResponse(aMessage, aMessageInfo) == NULL, OT_NOOP);
UpdateQueue();
VerifyOrExit((responseCopy = aMessage.Clone()) != NULL, OT_NOOP);
VerifyOrExit(metadata.AppendTo(*responseCopy) == OT_ERROR_NONE, responseCopy->Free());
mQueue.Enqueue(*responseCopy);
mTimer.FireAtIfEarlier(metadata.mDequeueTime);
exit:
return;
}
void ResponsesQueue::UpdateQueue(void)
{
uint16_t msgCount = 0;
Message * earliestMsg = NULL;
TimeMilli earliestDequeueTime(0);
for (Message *message = mQueue.GetHead(); message != NULL; message = message->GetNextCoapMessage())
{
ResponseMetadata metadata;
metadata.ReadFrom(*message);
if ((earliestMsg == NULL) || (metadata.mDequeueTime < earliestDequeueTime))
{
earliestMsg = message;
earliestDequeueTime = metadata.mDequeueTime;
}
msgCount++;
}
if (msgCount >= kMaxCachedResponses)
{
DequeueResponse(*earliestMsg);
}
}
void ResponsesQueue::DequeueResponse(Message &aMessage)
{
mQueue.Dequeue(aMessage);
aMessage.Free();
}
void ResponsesQueue::DequeueAllResponses(void)
{
Message *message;
while ((message = mQueue.GetHead()) != NULL)
{
DequeueResponse(*message);
}
}
void ResponsesQueue::HandleTimer(Timer &aTimer)
{
static_cast<ResponsesQueue *>(static_cast<TimerMilliContext &>(aTimer).GetContext())->HandleTimer();
}
void ResponsesQueue::HandleTimer(void)
{
TimeMilli now = TimerMilli::GetNow();
TimeMilli nextDequeueTime = now.GetDistantFuture();
Message * nextMessage;
for (Message *message = mQueue.GetHead(); message != NULL; message = nextMessage)
{
ResponseMetadata metadata;
nextMessage = message->GetNextCoapMessage();
metadata.ReadFrom(*message);
if (now >= metadata.mDequeueTime)
{
DequeueResponse(*message);
continue;
}
if (metadata.mDequeueTime < nextDequeueTime)
{
nextDequeueTime = metadata.mDequeueTime;
}
}
if (nextDequeueTime < now.GetDistantFuture())
{
mTimer.FireAt(nextDequeueTime);
}
}
void ResponsesQueue::ResponseMetadata::ReadFrom(const Message &aMessage)
{
uint16_t length = aMessage.GetLength();
OT_ASSERT(length >= sizeof(*this));
aMessage.Read(length - sizeof(*this), sizeof(*this), this);
}
static uint32_t Multiply(uint32_t aValueA, uint32_t aValueB)
{
uint32_t result = 0;
VerifyOrExit(aValueA, OT_NOOP);
result = aValueA * aValueB;
result = (result / aValueA == aValueB) ? result : 0;
exit:
return result;
}
bool TxParameters::IsValid(void) const
{
bool rval = false;
if ((mAckRandomFactorDenominator > 0) && (mAckRandomFactorNumerator >= mAckRandomFactorDenominator) &&
(mAckTimeout >= OT_COAP_MIN_ACK_TIMEOUT) && (mMaxRetransmit <= OT_COAP_MAX_RETRANSMIT))
{
uint32_t tmp = Multiply(mAckTimeout, (1U << (mMaxRetransmit + 1)) - 1);
tmp /= mAckRandomFactorDenominator;
tmp = Multiply(tmp, mAckRandomFactorNumerator);
rval = (tmp != 0 && (tmp + mAckTimeout + 2 * kDefaultMaxLatency) > tmp);
}
return rval;
}
uint32_t TxParameters::CalculateInitialRetransmissionTimeout(void) const
{
return Random::NonCrypto::GetUint32InRange(
mAckTimeout, mAckTimeout * mAckRandomFactorNumerator / mAckRandomFactorDenominator + 1);
}
uint32_t TxParameters::CalculateExchangeLifetime(void) const
{
return CalculateSpan(mMaxRetransmit) + 2 * kDefaultMaxLatency + mAckTimeout;
}
uint32_t TxParameters::CalculateMaxTransmitWait(void) const
{
return CalculateSpan(mMaxRetransmit + 1);
}
uint32_t TxParameters::CalculateSpan(uint8_t aMaxRetx) const
{
return static_cast<uint32_t>(mAckTimeout * ((1U << aMaxRetx) - 1) / mAckRandomFactorDenominator *
mAckRandomFactorNumerator);
}
const otCoapTxParameters TxParameters::kDefaultTxParameters = {
kDefaultAckTimeout,
kDefaultAckRandomFactorNumerator,
kDefaultAckRandomFactorDenominator,
kDefaultMaxRetransmit,
};
Coap::Coap(Instance &aInstance)
: CoapBase(aInstance, &Coap::Send)
, mSocket(aInstance.Get<Ip6::Udp>())
{
}
otError Coap::Start(uint16_t aPort)
{
otError error;
Ip6::SockAddr sockaddr;
sockaddr.mPort = aPort;
SuccessOrExit(error = mSocket.Open(&Coap::HandleUdpReceive, this));
VerifyOrExit((error = mSocket.Bind(sockaddr)) == OT_ERROR_NONE, IgnoreError(mSocket.Close()));
exit:
return error;
}
otError Coap::Stop(void)
{
otError error;
SuccessOrExit(error = mSocket.Close());
ClearRequestsAndResponses();
exit:
return error;
}
void Coap::HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
{
static_cast<Coap *>(aContext)->Receive(*static_cast<Message *>(aMessage),
*static_cast<const Ip6::MessageInfo *>(aMessageInfo));
}
otError Coap::Send(CoapBase &aCoapBase, ot::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
{
return static_cast<Coap &>(aCoapBase).Send(aMessage, aMessageInfo);
}
otError Coap::Send(ot::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
{
return mSocket.IsBound() ? mSocket.SendTo(aMessage, aMessageInfo) : OT_ERROR_INVALID_STATE;
}
} }