• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright (c) 2016, The OpenThread Authors.
3  *  All rights reserved.
4  *
5  *  Redistribution and use in source and binary forms, with or without
6  *  modification, are permitted provided that the following conditions are met:
7  *  1. Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  *  2. Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *  3. Neither the name of the copyright holder nor the
13  *     names of its contributors may be used to endorse or promote products
14  *     derived from this software without specific prior written permission.
15  *
16  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  *  POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include "coap.hpp"
30 
31 #include "common/array.hpp"
32 #include "common/as_core_type.hpp"
33 #include "common/code_utils.hpp"
34 #include "common/debug.hpp"
35 #include "common/instance.hpp"
36 #include "common/locator_getters.hpp"
37 #include "common/log.hpp"
38 #include "common/random.hpp"
39 #include "net/ip6.hpp"
40 #include "net/udp6.hpp"
41 #include "thread/thread_netif.hpp"
42 
43 /**
44  * @file
45  *   This file contains common code base for CoAP client and server.
46  */
47 
48 namespace ot {
49 namespace Coap {
50 
51 RegisterLogModule("Coap");
52 
CoapBase(Instance & aInstance,Sender aSender)53 CoapBase::CoapBase(Instance &aInstance, Sender aSender)
54     : InstanceLocator(aInstance)
55     , mMessageId(Random::NonCrypto::GetUint16())
56     , mRetransmissionTimer(aInstance, Coap::HandleRetransmissionTimer, this)
57     , mContext(nullptr)
58     , mInterceptor(nullptr)
59     , mResponsesQueue(aInstance)
60     , mDefaultHandler(nullptr)
61     , mDefaultHandlerContext(nullptr)
62     , mSender(aSender)
63 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
64     , mLastResponse(nullptr)
65 #endif
66 {
67 }
68 
ClearRequestsAndResponses(void)69 void CoapBase::ClearRequestsAndResponses(void)
70 {
71     ClearRequests(nullptr); // Clear requests matching any address.
72     mResponsesQueue.DequeueAllResponses();
73 }
74 
ClearRequests(const Ip6::Address & aAddress)75 void CoapBase::ClearRequests(const Ip6::Address &aAddress)
76 {
77     ClearRequests(&aAddress);
78 }
79 
ClearRequests(const Ip6::Address * aAddress)80 void CoapBase::ClearRequests(const Ip6::Address *aAddress)
81 {
82     for (Message &message : mPendingRequests)
83     {
84         Metadata metadata;
85 
86         metadata.ReadFrom(message);
87 
88         if ((aAddress == nullptr) || (metadata.mSourceAddress == *aAddress))
89         {
90             FinalizeCoapTransaction(message, metadata, nullptr, nullptr, kErrorAbort);
91         }
92     }
93 }
94 
95 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
AddBlockWiseResource(ResourceBlockWise & aResource)96 void CoapBase::AddBlockWiseResource(ResourceBlockWise &aResource)
97 {
98     IgnoreError(mBlockWiseResources.Add(aResource));
99 }
100 
RemoveBlockWiseResource(ResourceBlockWise & aResource)101 void CoapBase::RemoveBlockWiseResource(ResourceBlockWise &aResource)
102 {
103     IgnoreError(mBlockWiseResources.Remove(aResource));
104     aResource.SetNext(nullptr);
105 }
106 #endif
107 
AddResource(Resource & aResource)108 void CoapBase::AddResource(Resource &aResource)
109 {
110     IgnoreError(mResources.Add(aResource));
111 }
112 
RemoveResource(Resource & aResource)113 void CoapBase::RemoveResource(Resource &aResource)
114 {
115     IgnoreError(mResources.Remove(aResource));
116     aResource.SetNext(nullptr);
117 }
118 
SetDefaultHandler(RequestHandler aHandler,void * aContext)119 void CoapBase::SetDefaultHandler(RequestHandler aHandler, void *aContext)
120 {
121     mDefaultHandler        = aHandler;
122     mDefaultHandlerContext = aContext;
123 }
124 
SetInterceptor(Interceptor aInterceptor,void * aContext)125 void CoapBase::SetInterceptor(Interceptor aInterceptor, void *aContext)
126 {
127     mInterceptor = aInterceptor;
128     mContext     = aContext;
129 }
130 
NewMessage(const Message::Settings & aSettings)131 Message *CoapBase::NewMessage(const Message::Settings &aSettings)
132 {
133     Message *message = nullptr;
134 
135     VerifyOrExit((message = AsCoapMessagePtr(Get<Ip6::Udp>().NewMessage(0, aSettings))) != nullptr);
136     message->SetOffset(0);
137 
138 exit:
139     return message;
140 }
141 
NewPriorityConfirmablePostMessage(const char * aUriPath)142 Message *CoapBase::NewPriorityConfirmablePostMessage(const char *aUriPath)
143 {
144     return InitMessage(NewPriorityMessage(), kTypeConfirmable, aUriPath);
145 }
146 
NewConfirmablePostMessage(const char * aUriPath)147 Message *CoapBase::NewConfirmablePostMessage(const char *aUriPath)
148 {
149     return InitMessage(NewMessage(), kTypeConfirmable, aUriPath);
150 }
151 
NewPriorityNonConfirmablePostMessage(const char * aUriPath)152 Message *CoapBase::NewPriorityNonConfirmablePostMessage(const char *aUriPath)
153 {
154     return InitMessage(NewPriorityMessage(), kTypeNonConfirmable, aUriPath);
155 }
156 
NewNonConfirmablePostMessage(const char * aUriPath)157 Message *CoapBase::NewNonConfirmablePostMessage(const char *aUriPath)
158 {
159     return InitMessage(NewMessage(), kTypeNonConfirmable, aUriPath);
160 }
161 
NewPriorityResponseMessage(const Message & aRequest)162 Message *CoapBase::NewPriorityResponseMessage(const Message &aRequest)
163 {
164     return InitResponse(NewPriorityMessage(), aRequest);
165 }
166 
NewResponseMessage(const Message & aRequest)167 Message *CoapBase::NewResponseMessage(const Message &aRequest)
168 {
169     return InitResponse(NewMessage(), aRequest);
170 }
171 
InitMessage(Message * aMessage,Type aType,const char * aUriPath)172 Message *CoapBase::InitMessage(Message *aMessage, Type aType, const char *aUriPath)
173 {
174     Error error = kErrorNone;
175 
176     VerifyOrExit(aMessage != nullptr);
177 
178     SuccessOrExit(error = aMessage->Init(aType, kCodePost, aUriPath));
179     SuccessOrExit(error = aMessage->SetPayloadMarker());
180 
181 exit:
182     FreeAndNullMessageOnError(aMessage, error);
183     return aMessage;
184 }
185 
InitResponse(Message * aMessage,const Message & aResponse)186 Message *CoapBase::InitResponse(Message *aMessage, const Message &aResponse)
187 {
188     Error error = kErrorNone;
189 
190     VerifyOrExit(aMessage != nullptr);
191 
192     SuccessOrExit(error = aMessage->SetDefaultResponseHeader(aResponse));
193     SuccessOrExit(error = aMessage->SetPayloadMarker());
194 
195 exit:
196     FreeAndNullMessageOnError(aMessage, error);
197     return aMessage;
198 }
199 
Send(ot::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)200 Error CoapBase::Send(ot::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
201 {
202     Error error;
203 
204 #if OPENTHREAD_CONFIG_OTNS_ENABLE
205     Get<Utils::Otns>().EmitCoapSend(AsCoapMessage(&aMessage), aMessageInfo);
206 #endif
207 
208     error = mSender(*this, aMessage, aMessageInfo);
209 
210 #if OPENTHREAD_CONFIG_OTNS_ENABLE
211     if (error != kErrorNone)
212     {
213         Get<Utils::Otns>().EmitCoapSendFailure(error, AsCoapMessage(&aMessage), aMessageInfo);
214     }
215 #endif
216     return error;
217 }
218 
219 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
SendMessage(Message & aMessage,const Ip6::MessageInfo & aMessageInfo,const TxParameters & aTxParameters,ResponseHandler aHandler,void * aContext,otCoapBlockwiseTransmitHook aTransmitHook,otCoapBlockwiseReceiveHook aReceiveHook)220 Error CoapBase::SendMessage(Message &                   aMessage,
221                             const Ip6::MessageInfo &    aMessageInfo,
222                             const TxParameters &        aTxParameters,
223                             ResponseHandler             aHandler,
224                             void *                      aContext,
225                             otCoapBlockwiseTransmitHook aTransmitHook,
226                             otCoapBlockwiseReceiveHook  aReceiveHook)
227 #else
228 Error CoapBase::SendMessage(Message &               aMessage,
229                             const Ip6::MessageInfo &aMessageInfo,
230                             const TxParameters &    aTxParameters,
231                             ResponseHandler         aHandler,
232                             void *                  aContext)
233 #endif
234 {
235     Error    error;
236     Message *storedCopy = nullptr;
237     uint16_t copyLength = 0;
238 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
239     uint8_t  buf[kMaxBlockLength] = {0};
240     uint16_t bufLen               = kMaxBlockLength;
241     bool     moreBlocks           = false;
242 #endif
243 
244     switch (aMessage.GetType())
245     {
246     case kTypeAck:
247 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
248         // Check for block-wise transfer
249         if ((aTransmitHook != nullptr) && (aMessage.ReadBlockOptionValues(kOptionBlock2) == kErrorNone) &&
250             (aMessage.GetBlockWiseBlockNumber() == 0))
251         {
252             // Set payload for first block of the transfer
253             VerifyOrExit((bufLen = otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize())) <= kMaxBlockLength,
254                          error = kErrorNoBufs);
255             SuccessOrExit(error = aTransmitHook(aContext, buf, aMessage.GetBlockWiseBlockNumber() * bufLen, &bufLen,
256                                                 &moreBlocks));
257             SuccessOrExit(error = aMessage.AppendBytes(buf, bufLen));
258 
259             SuccessOrExit(error = CacheLastBlockResponse(&aMessage));
260         }
261 #endif
262 
263         mResponsesQueue.EnqueueResponse(aMessage, aMessageInfo, aTxParameters);
264         break;
265     case kTypeReset:
266         OT_ASSERT(aMessage.GetCode() == kCodeEmpty);
267         break;
268     default:
269 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
270         // Check for block-wise transfer
271         if ((aTransmitHook != nullptr) && (aMessage.ReadBlockOptionValues(kOptionBlock1) == kErrorNone) &&
272             (aMessage.GetBlockWiseBlockNumber() == 0))
273         {
274             // Set payload for first block of the transfer
275             VerifyOrExit((bufLen = otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize())) <= kMaxBlockLength,
276                          error = kErrorNoBufs);
277             SuccessOrExit(error = aTransmitHook(aContext, buf, aMessage.GetBlockWiseBlockNumber() * bufLen, &bufLen,
278                                                 &moreBlocks));
279             SuccessOrExit(error = aMessage.AppendBytes(buf, bufLen));
280 
281             // Block-Wise messages always have to be confirmable
282             if (aMessage.IsNonConfirmable())
283             {
284                 aMessage.SetType(kTypeConfirmable);
285             }
286         }
287 #endif
288 
289         aMessage.SetMessageId(mMessageId++);
290         break;
291     }
292 
293     aMessage.Finish();
294 
295     if (aMessage.IsConfirmable())
296     {
297         copyLength = aMessage.GetLength();
298     }
299     else if (aMessage.IsNonConfirmable() && (aHandler != nullptr))
300     {
301         // As we do not retransmit non confirmable messages, create a
302         // copy of header only, for token information.
303         copyLength = aMessage.GetOptionStart();
304     }
305 
306     if (copyLength > 0)
307     {
308         Metadata metadata;
309 
310 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
311         // Whether or not to turn on special "Observe" handling.
312         Option::Iterator iterator;
313         bool             observe;
314 
315         SuccessOrExit(error = iterator.Init(aMessage, kOptionObserve));
316         observe = !iterator.IsDone();
317 
318         // Special case, if we're sending a GET with Observe=1, that is a cancellation.
319         if (observe && aMessage.IsGetRequest())
320         {
321             uint64_t observeVal = 0;
322 
323             SuccessOrExit(error = iterator.ReadOptionValue(observeVal));
324 
325             if (observeVal == 1)
326             {
327                 Metadata handlerMetadata;
328 
329                 // We're cancelling our subscription, so disable special-case handling on this request.
330                 observe = false;
331 
332                 // If we can find the previous handler context, cancel that too.  Peer address
333                 // and tokens, etc should all match.
334                 Message *origRequest = FindRelatedRequest(aMessage, aMessageInfo, handlerMetadata);
335                 if (origRequest != nullptr)
336                 {
337                     FinalizeCoapTransaction(*origRequest, handlerMetadata, nullptr, nullptr, kErrorNone);
338                 }
339             }
340         }
341 #endif // OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
342 
343         metadata.mSourceAddress            = aMessageInfo.GetSockAddr();
344         metadata.mDestinationPort          = aMessageInfo.GetPeerPort();
345         metadata.mDestinationAddress       = aMessageInfo.GetPeerAddr();
346         metadata.mMulticastLoop            = aMessageInfo.GetMulticastLoop();
347         metadata.mResponseHandler          = aHandler;
348         metadata.mResponseContext          = aContext;
349         metadata.mRetransmissionsRemaining = aTxParameters.mMaxRetransmit;
350         metadata.mRetransmissionTimeout    = aTxParameters.CalculateInitialRetransmissionTimeout();
351         metadata.mAcknowledged             = false;
352         metadata.mConfirmable              = aMessage.IsConfirmable();
353 #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
354         metadata.mHopLimit        = aMessageInfo.GetHopLimit();
355         metadata.mIsHostInterface = aMessageInfo.IsHostInterface();
356 #endif
357 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
358         metadata.mBlockwiseReceiveHook  = aReceiveHook;
359         metadata.mBlockwiseTransmitHook = aTransmitHook;
360 #endif
361 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
362         metadata.mObserve = observe;
363 #endif
364         metadata.mNextTimerShot =
365             TimerMilli::GetNow() +
366             (metadata.mConfirmable ? metadata.mRetransmissionTimeout : aTxParameters.CalculateMaxTransmitWait());
367 
368         storedCopy = CopyAndEnqueueMessage(aMessage, copyLength, metadata);
369         VerifyOrExit(storedCopy != nullptr, error = kErrorNoBufs);
370     }
371 
372     SuccessOrExit(error = Send(aMessage, aMessageInfo));
373 
374 exit:
375 
376     if (error != kErrorNone && storedCopy != nullptr)
377     {
378         DequeueMessage(*storedCopy);
379     }
380 
381     return error;
382 }
383 
SendMessage(Message & aMessage,const Ip6::MessageInfo & aMessageInfo,ResponseHandler aHandler,void * aContext)384 Error CoapBase::SendMessage(Message &               aMessage,
385                             const Ip6::MessageInfo &aMessageInfo,
386                             ResponseHandler         aHandler,
387                             void *                  aContext)
388 {
389 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
390     return SendMessage(aMessage, aMessageInfo, TxParameters::GetDefault(), aHandler, aContext, nullptr, nullptr);
391 #else
392     return SendMessage(aMessage, aMessageInfo, TxParameters::GetDefault(), aHandler, aContext);
393 #endif
394 }
395 
SendReset(Message & aRequest,const Ip6::MessageInfo & aMessageInfo)396 Error CoapBase::SendReset(Message &aRequest, const Ip6::MessageInfo &aMessageInfo)
397 {
398     return SendEmptyMessage(kTypeReset, aRequest, aMessageInfo);
399 }
400 
SendAck(const Message & aRequest,const Ip6::MessageInfo & aMessageInfo)401 Error CoapBase::SendAck(const Message &aRequest, const Ip6::MessageInfo &aMessageInfo)
402 {
403     return SendEmptyMessage(kTypeAck, aRequest, aMessageInfo);
404 }
405 
SendEmptyAck(const Message & aRequest,const Ip6::MessageInfo & aMessageInfo,Code aCode)406 Error CoapBase::SendEmptyAck(const Message &aRequest, const Ip6::MessageInfo &aMessageInfo, Code aCode)
407 {
408     return (aRequest.IsConfirmable() ? SendHeaderResponse(aCode, aRequest, aMessageInfo) : kErrorInvalidArgs);
409 }
410 
SendNotFound(const Message & aRequest,const Ip6::MessageInfo & aMessageInfo)411 Error CoapBase::SendNotFound(const Message &aRequest, const Ip6::MessageInfo &aMessageInfo)
412 {
413     return SendHeaderResponse(kCodeNotFound, aRequest, aMessageInfo);
414 }
415 
SendEmptyMessage(Type aType,const Message & aRequest,const Ip6::MessageInfo & aMessageInfo)416 Error CoapBase::SendEmptyMessage(Type aType, const Message &aRequest, const Ip6::MessageInfo &aMessageInfo)
417 {
418     Error    error   = kErrorNone;
419     Message *message = nullptr;
420 
421     VerifyOrExit(aRequest.IsConfirmable(), error = kErrorInvalidArgs);
422 
423     VerifyOrExit((message = NewMessage()) != nullptr, error = kErrorNoBufs);
424 
425     message->Init(aType, kCodeEmpty);
426     message->SetMessageId(aRequest.GetMessageId());
427 
428     message->Finish();
429     SuccessOrExit(error = Send(*message, aMessageInfo));
430 
431 exit:
432     FreeMessageOnError(message, error);
433     return error;
434 }
435 
SendHeaderResponse(Message::Code aCode,const Message & aRequest,const Ip6::MessageInfo & aMessageInfo)436 Error CoapBase::SendHeaderResponse(Message::Code aCode, const Message &aRequest, const Ip6::MessageInfo &aMessageInfo)
437 {
438     Error    error   = kErrorNone;
439     Message *message = nullptr;
440 
441     VerifyOrExit(aRequest.IsRequest(), error = kErrorInvalidArgs);
442     VerifyOrExit((message = NewMessage()) != nullptr, error = kErrorNoBufs);
443 
444     switch (aRequest.GetType())
445     {
446     case kTypeConfirmable:
447         message->Init(kTypeAck, aCode);
448         message->SetMessageId(aRequest.GetMessageId());
449         break;
450 
451     case kTypeNonConfirmable:
452         message->Init(kTypeNonConfirmable, aCode);
453         break;
454 
455     default:
456         ExitNow(error = kErrorInvalidArgs);
457         OT_UNREACHABLE_CODE(break);
458     }
459 
460     SuccessOrExit(error = message->SetTokenFromMessage(aRequest));
461 
462     SuccessOrExit(error = SendMessage(*message, aMessageInfo));
463 
464 exit:
465     FreeMessageOnError(message, error);
466     return error;
467 }
468 
HandleRetransmissionTimer(Timer & aTimer)469 void CoapBase::HandleRetransmissionTimer(Timer &aTimer)
470 {
471     static_cast<Coap *>(static_cast<TimerMilliContext &>(aTimer).GetContext())->HandleRetransmissionTimer();
472 }
473 
HandleRetransmissionTimer(void)474 void CoapBase::HandleRetransmissionTimer(void)
475 {
476     TimeMilli        now      = TimerMilli::GetNow();
477     TimeMilli        nextTime = now.GetDistantFuture();
478     Metadata         metadata;
479     Ip6::MessageInfo messageInfo;
480 
481     for (Message &message : mPendingRequests)
482     {
483         metadata.ReadFrom(message);
484 
485         if (now >= metadata.mNextTimerShot)
486         {
487 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
488             if (message.IsRequest() && metadata.mObserve && metadata.mAcknowledged)
489             {
490                 // This is a RFC7641 subscription.  Do not time out.
491                 continue;
492             }
493 #endif
494 
495             if (!metadata.mConfirmable || (metadata.mRetransmissionsRemaining == 0))
496             {
497                 // No expected response or acknowledgment.
498                 FinalizeCoapTransaction(message, metadata, nullptr, nullptr, kErrorResponseTimeout);
499                 continue;
500             }
501 
502             // Increment retransmission counter and timer.
503             metadata.mRetransmissionsRemaining--;
504             metadata.mRetransmissionTimeout *= 2;
505             metadata.mNextTimerShot = now + metadata.mRetransmissionTimeout;
506             metadata.UpdateIn(message);
507 
508             // Retransmit
509             if (!metadata.mAcknowledged)
510             {
511                 messageInfo.SetPeerAddr(metadata.mDestinationAddress);
512                 messageInfo.SetPeerPort(metadata.mDestinationPort);
513                 messageInfo.SetSockAddr(metadata.mSourceAddress);
514 #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
515                 messageInfo.SetHopLimit(metadata.mHopLimit);
516                 messageInfo.SetIsHostInterface(metadata.mIsHostInterface);
517 #endif
518                 messageInfo.SetMulticastLoop(metadata.mMulticastLoop);
519 
520                 SendCopy(message, messageInfo);
521             }
522         }
523 
524         if (nextTime > metadata.mNextTimerShot)
525         {
526             nextTime = metadata.mNextTimerShot;
527         }
528     }
529 
530     if (nextTime < now.GetDistantFuture())
531     {
532         mRetransmissionTimer.FireAt(nextTime);
533     }
534 }
535 
FinalizeCoapTransaction(Message & aRequest,const Metadata & aMetadata,Message * aResponse,const Ip6::MessageInfo * aMessageInfo,Error aResult)536 void CoapBase::FinalizeCoapTransaction(Message &               aRequest,
537                                        const Metadata &        aMetadata,
538                                        Message *               aResponse,
539                                        const Ip6::MessageInfo *aMessageInfo,
540                                        Error                   aResult)
541 {
542     DequeueMessage(aRequest);
543 
544     if (aMetadata.mResponseHandler != nullptr)
545     {
546         aMetadata.mResponseHandler(aMetadata.mResponseContext, aResponse, aMessageInfo, aResult);
547     }
548 }
549 
AbortTransaction(ResponseHandler aHandler,void * aContext)550 Error CoapBase::AbortTransaction(ResponseHandler aHandler, void *aContext)
551 {
552     Error    error = kErrorNotFound;
553     Metadata metadata;
554 
555     for (Message &message : mPendingRequests)
556     {
557         metadata.ReadFrom(message);
558 
559         if (metadata.mResponseHandler == aHandler && metadata.mResponseContext == aContext)
560         {
561             FinalizeCoapTransaction(message, metadata, nullptr, nullptr, kErrorAbort);
562             error = kErrorNone;
563         }
564     }
565 
566     return error;
567 }
568 
CopyAndEnqueueMessage(const Message & aMessage,uint16_t aCopyLength,const Metadata & aMetadata)569 Message *CoapBase::CopyAndEnqueueMessage(const Message &aMessage, uint16_t aCopyLength, const Metadata &aMetadata)
570 {
571     Error    error       = kErrorNone;
572     Message *messageCopy = nullptr;
573 
574     VerifyOrExit((messageCopy = aMessage.Clone(aCopyLength)) != nullptr, error = kErrorNoBufs);
575 
576     SuccessOrExit(error = aMetadata.AppendTo(*messageCopy));
577 
578     mRetransmissionTimer.FireAtIfEarlier(aMetadata.mNextTimerShot);
579 
580     mPendingRequests.Enqueue(*messageCopy);
581 
582 exit:
583     FreeAndNullMessageOnError(messageCopy, error);
584     return messageCopy;
585 }
586 
DequeueMessage(Message & aMessage)587 void CoapBase::DequeueMessage(Message &aMessage)
588 {
589     mPendingRequests.Dequeue(aMessage);
590 
591     if (mRetransmissionTimer.IsRunning() && (mPendingRequests.GetHead() == nullptr))
592     {
593         mRetransmissionTimer.Stop();
594     }
595 
596     aMessage.Free();
597 
598     // No need to worry that the earliest pending message was removed -
599     // the timer would just shoot earlier and then it'd be setup again.
600 }
601 
602 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
FreeLastBlockResponse(void)603 void CoapBase::FreeLastBlockResponse(void)
604 {
605     if (mLastResponse != nullptr)
606     {
607         mLastResponse->Free();
608         mLastResponse = nullptr;
609     }
610 }
611 
CacheLastBlockResponse(Message * aResponse)612 Error CoapBase::CacheLastBlockResponse(Message *aResponse)
613 {
614     Error error = kErrorNone;
615     // Save last response for block-wise transfer
616     FreeLastBlockResponse();
617 
618     if ((mLastResponse = aResponse->Clone()) == nullptr)
619     {
620         error = kErrorNoBufs;
621     }
622 
623     return error;
624 }
625 
PrepareNextBlockRequest(Message::BlockType aType,bool aMoreBlocks,Message & aRequestOld,Message & aRequest,Message & aMessage)626 Error CoapBase::PrepareNextBlockRequest(Message::BlockType aType,
627                                         bool               aMoreBlocks,
628                                         Message &          aRequestOld,
629                                         Message &          aRequest,
630                                         Message &          aMessage)
631 {
632     Error            error       = kErrorNone;
633     bool             isOptionSet = false;
634     uint64_t         optionBuf   = 0;
635     uint16_t         blockOption = 0;
636     Option::Iterator iterator;
637 
638     blockOption = (aType == Message::kBlockType1) ? kOptionBlock1 : kOptionBlock2;
639 
640     aRequest.Init(kTypeConfirmable, static_cast<ot::Coap::Code>(aRequestOld.GetCode()));
641     SuccessOrExit(error = iterator.Init(aRequestOld));
642 
643     // Copy options from last response to next message
644     for (; !iterator.IsDone() && iterator.GetOption()->GetLength() != 0; error = iterator.Advance())
645     {
646         uint16_t optionNumber = iterator.GetOption()->GetNumber();
647 
648         SuccessOrExit(error);
649 
650         // Check if option to copy next is higher than or equal to Block1 option
651         if (optionNumber >= blockOption && !isOptionSet)
652         {
653             // Write Block1 option to next message
654             SuccessOrExit(error = aRequest.AppendBlockOption(aType, aMessage.GetBlockWiseBlockNumber() + 1, aMoreBlocks,
655                                                              aMessage.GetBlockWiseBlockSize()));
656             aRequest.SetBlockWiseBlockNumber(aMessage.GetBlockWiseBlockNumber() + 1);
657             aRequest.SetBlockWiseBlockSize(aMessage.GetBlockWiseBlockSize());
658             aRequest.SetMoreBlocksFlag(aMoreBlocks);
659 
660             isOptionSet = true;
661 
662             // If option to copy next is Block1 or Block2 option, option is not copied
663             if (optionNumber == kOptionBlock1 || optionNumber == kOptionBlock2)
664             {
665                 continue;
666             }
667         }
668 
669         // Copy option
670         SuccessOrExit(error = iterator.ReadOptionValue(&optionBuf));
671         SuccessOrExit(error = aRequest.AppendOption(optionNumber, iterator.GetOption()->GetLength(), &optionBuf));
672     }
673 
674     if (!isOptionSet)
675     {
676         // Write Block1 option to next message
677         SuccessOrExit(error = aRequest.AppendBlockOption(aType, aMessage.GetBlockWiseBlockNumber() + 1, aMoreBlocks,
678                                                          aMessage.GetBlockWiseBlockSize()));
679         aRequest.SetBlockWiseBlockNumber(aMessage.GetBlockWiseBlockNumber() + 1);
680         aRequest.SetBlockWiseBlockSize(aMessage.GetBlockWiseBlockSize());
681         aRequest.SetMoreBlocksFlag(aMoreBlocks);
682     }
683 
684 exit:
685     return error;
686 }
687 
SendNextBlock1Request(Message & aRequest,Message & aMessage,const Ip6::MessageInfo & aMessageInfo,const Metadata & aCoapMetadata)688 Error CoapBase::SendNextBlock1Request(Message &               aRequest,
689                                       Message &               aMessage,
690                                       const Ip6::MessageInfo &aMessageInfo,
691                                       const Metadata &        aCoapMetadata)
692 {
693     Error    error                = kErrorNone;
694     Message *request              = nullptr;
695     bool     moreBlocks           = false;
696     uint8_t  buf[kMaxBlockLength] = {0};
697     uint16_t bufLen               = kMaxBlockLength;
698 
699     SuccessOrExit(error = aRequest.ReadBlockOptionValues(kOptionBlock1));
700     SuccessOrExit(error = aMessage.ReadBlockOptionValues(kOptionBlock1));
701 
702     // Conclude block-wise transfer if last block has been received
703     if (!aRequest.IsMoreBlocksFlagSet())
704     {
705         FinalizeCoapTransaction(aRequest, aCoapMetadata, &aMessage, &aMessageInfo, kErrorNone);
706         ExitNow();
707     }
708 
709     // Get next block
710     VerifyOrExit((bufLen = otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize())) <= kMaxBlockLength,
711                  error = kErrorNoBufs);
712 
713     SuccessOrExit(
714         error = aCoapMetadata.mBlockwiseTransmitHook(aCoapMetadata.mResponseContext, buf,
715                                                      otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize()) *
716                                                          (aMessage.GetBlockWiseBlockNumber() + 1),
717                                                      &bufLen, &moreBlocks));
718 
719     // Check if block length is valid
720     VerifyOrExit(bufLen <= otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize()), error = kErrorInvalidArgs);
721 
722     // Init request for next block
723     VerifyOrExit((request = NewMessage()) != nullptr, error = kErrorNoBufs);
724     SuccessOrExit(error = PrepareNextBlockRequest(Message::kBlockType1, moreBlocks, aRequest, *request, aMessage));
725 
726     SuccessOrExit(error = request->SetPayloadMarker());
727 
728     SuccessOrExit(error = request->AppendBytes(buf, bufLen));
729 
730     DequeueMessage(aRequest);
731 
732     LogInfo("Send Block1 Nr. %d, Size: %d bytes, More Blocks Flag: %d", request->GetBlockWiseBlockNumber(),
733             otCoapBlockSizeFromExponent(request->GetBlockWiseBlockSize()), request->IsMoreBlocksFlagSet());
734 
735     SuccessOrExit(error = SendMessage(*request, aMessageInfo, TxParameters::GetDefault(),
736                                       aCoapMetadata.mResponseHandler, aCoapMetadata.mResponseContext,
737                                       aCoapMetadata.mBlockwiseTransmitHook, aCoapMetadata.mBlockwiseReceiveHook));
738 
739 exit:
740     FreeMessageOnError(request, error);
741 
742     return error;
743 }
744 
SendNextBlock2Request(Message & aRequest,Message & aMessage,const Ip6::MessageInfo & aMessageInfo,const Metadata & aCoapMetadata,uint32_t aTotalLength,bool aBeginBlock1Transfer)745 Error CoapBase::SendNextBlock2Request(Message &               aRequest,
746                                       Message &               aMessage,
747                                       const Ip6::MessageInfo &aMessageInfo,
748                                       const Metadata &        aCoapMetadata,
749                                       uint32_t                aTotalLength,
750                                       bool                    aBeginBlock1Transfer)
751 {
752     Error    error                = kErrorNone;
753     Message *request              = nullptr;
754     uint8_t  buf[kMaxBlockLength] = {0};
755     uint16_t bufLen               = kMaxBlockLength;
756 
757     SuccessOrExit(error = aMessage.ReadBlockOptionValues(kOptionBlock2));
758 
759     // Check payload and block length
760     VerifyOrExit((aMessage.GetLength() - aMessage.GetOffset()) <=
761                          otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize()) &&
762                      (aMessage.GetLength() - aMessage.GetOffset()) <= kMaxBlockLength,
763                  error = kErrorNoBufs);
764 
765     // Read and then forward payload to receive hook function
766     bufLen = aMessage.ReadBytes(aMessage.GetOffset(), buf, aMessage.GetLength() - aMessage.GetOffset());
767     SuccessOrExit(
768         error = aCoapMetadata.mBlockwiseReceiveHook(aCoapMetadata.mResponseContext, buf,
769                                                     otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize()) *
770                                                         aMessage.GetBlockWiseBlockNumber(),
771                                                     bufLen, aMessage.IsMoreBlocksFlagSet(), aTotalLength));
772 
773     // CoAP Block-Wise Transfer continues
774     LogInfo("Received Block2 Nr. %d , Size: %d bytes, More Blocks Flag: %d", aMessage.GetBlockWiseBlockNumber(),
775             otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize()), aMessage.IsMoreBlocksFlagSet());
776 
777     // Conclude block-wise transfer if last block has been received
778     if (!aMessage.IsMoreBlocksFlagSet())
779     {
780         FinalizeCoapTransaction(aRequest, aCoapMetadata, &aMessage, &aMessageInfo, kErrorNone);
781         ExitNow();
782     }
783 
784     // Init request for next block
785     VerifyOrExit((request = NewMessage()) != nullptr, error = kErrorNoBufs);
786     SuccessOrExit(error = PrepareNextBlockRequest(Message::kBlockType2, aMessage.IsMoreBlocksFlagSet(), aRequest,
787                                                   *request, aMessage));
788 
789     if (!aBeginBlock1Transfer)
790     {
791         DequeueMessage(aRequest);
792     }
793 
794     LogInfo("Request Block2 Nr. %d, Size: %d bytes", request->GetBlockWiseBlockNumber(),
795             otCoapBlockSizeFromExponent(request->GetBlockWiseBlockSize()));
796 
797     SuccessOrExit(error =
798                       SendMessage(*request, aMessageInfo, TxParameters::GetDefault(), aCoapMetadata.mResponseHandler,
799                                   aCoapMetadata.mResponseContext, nullptr, aCoapMetadata.mBlockwiseReceiveHook));
800 
801 exit:
802     FreeMessageOnError(request, error);
803 
804     return error;
805 }
806 
ProcessBlock1Request(Message & aMessage,const Ip6::MessageInfo & aMessageInfo,const ResourceBlockWise & aResource,uint32_t aTotalLength)807 Error CoapBase::ProcessBlock1Request(Message &                aMessage,
808                                      const Ip6::MessageInfo & aMessageInfo,
809                                      const ResourceBlockWise &aResource,
810                                      uint32_t                 aTotalLength)
811 {
812     Error    error                = kErrorNone;
813     Message *response             = nullptr;
814     uint8_t  buf[kMaxBlockLength] = {0};
815     uint16_t bufLen               = kMaxBlockLength;
816 
817     SuccessOrExit(error = aMessage.ReadBlockOptionValues(kOptionBlock1));
818 
819     // Read and then forward payload to receive hook function
820     VerifyOrExit((aMessage.GetLength() - aMessage.GetOffset()) <= kMaxBlockLength, error = kErrorNoBufs);
821     bufLen = aMessage.ReadBytes(aMessage.GetOffset(), buf, aMessage.GetLength() - aMessage.GetOffset());
822     SuccessOrExit(error = aResource.HandleBlockReceive(buf,
823                                                        otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize()) *
824                                                            aMessage.GetBlockWiseBlockNumber(),
825                                                        bufLen, aMessage.IsMoreBlocksFlagSet(), aTotalLength));
826 
827     if (aMessage.IsMoreBlocksFlagSet())
828     {
829         // Set up next response
830         VerifyOrExit((response = NewMessage()) != nullptr, error = kErrorFailed);
831         response->Init(kTypeAck, kCodeContinue);
832         response->SetMessageId(aMessage.GetMessageId());
833         IgnoreReturnValue(response->SetToken(AsConst(aMessage).GetToken(), aMessage.GetTokenLength()));
834 
835         response->SetBlockWiseBlockNumber(aMessage.GetBlockWiseBlockNumber());
836         response->SetMoreBlocksFlag(aMessage.IsMoreBlocksFlagSet());
837         response->SetBlockWiseBlockSize(aMessage.GetBlockWiseBlockSize());
838 
839         SuccessOrExit(error = response->AppendBlockOption(Message::kBlockType1, response->GetBlockWiseBlockNumber(),
840                                                           response->IsMoreBlocksFlagSet(),
841                                                           response->GetBlockWiseBlockSize()));
842 
843         SuccessOrExit(error = CacheLastBlockResponse(response));
844 
845         LogInfo("Acknowledge Block1 Nr. %d, Size: %d bytes", response->GetBlockWiseBlockNumber(),
846                 otCoapBlockSizeFromExponent(response->GetBlockWiseBlockSize()));
847 
848         SuccessOrExit(error = SendMessage(*response, aMessageInfo));
849 
850         error = kErrorBusy;
851     }
852     else
853     {
854         // Conclude block-wise transfer if last block has been received
855         FreeLastBlockResponse();
856         error = kErrorNone;
857     }
858 
859 exit:
860     if (error != kErrorNone && error != kErrorBusy && response != nullptr)
861     {
862         response->Free();
863     }
864 
865     return error;
866 }
867 
ProcessBlock2Request(Message & aMessage,const Ip6::MessageInfo & aMessageInfo,const ResourceBlockWise & aResource)868 Error CoapBase::ProcessBlock2Request(Message &                aMessage,
869                                      const Ip6::MessageInfo & aMessageInfo,
870                                      const ResourceBlockWise &aResource)
871 {
872     Error            error                = kErrorNone;
873     Message *        response             = nullptr;
874     uint8_t          buf[kMaxBlockLength] = {0};
875     uint16_t         bufLen               = kMaxBlockLength;
876     bool             moreBlocks           = false;
877     uint64_t         optionBuf            = 0;
878     Option::Iterator iterator;
879 
880     SuccessOrExit(error = aMessage.ReadBlockOptionValues(kOptionBlock2));
881 
882     LogInfo("Request for Block2 Nr. %d, Size: %d bytes received", aMessage.GetBlockWiseBlockNumber(),
883             otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize()));
884 
885     if (aMessage.GetBlockWiseBlockNumber() == 0)
886     {
887         aResource.HandleRequest(aMessage, aMessageInfo);
888         ExitNow();
889     }
890 
891     // Set up next response
892     VerifyOrExit((response = NewMessage()) != nullptr, error = kErrorNoBufs);
893     response->Init(kTypeAck, kCodeContent);
894     response->SetMessageId(aMessage.GetMessageId());
895 
896     VerifyOrExit((bufLen = otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize())) <= kMaxBlockLength,
897                  error = kErrorNoBufs);
898     SuccessOrExit(error = aResource.HandleBlockTransmit(buf,
899                                                         otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize()) *
900                                                             aMessage.GetBlockWiseBlockNumber(),
901                                                         &bufLen, &moreBlocks));
902 
903     response->SetMoreBlocksFlag(moreBlocks);
904     if (moreBlocks)
905     {
906         switch (bufLen)
907         {
908         case 1024:
909             response->SetBlockWiseBlockSize(OT_COAP_OPTION_BLOCK_SZX_1024);
910             break;
911         case 512:
912             response->SetBlockWiseBlockSize(OT_COAP_OPTION_BLOCK_SZX_512);
913             break;
914         case 256:
915             response->SetBlockWiseBlockSize(OT_COAP_OPTION_BLOCK_SZX_256);
916             break;
917         case 128:
918             response->SetBlockWiseBlockSize(OT_COAP_OPTION_BLOCK_SZX_128);
919             break;
920         case 64:
921             response->SetBlockWiseBlockSize(OT_COAP_OPTION_BLOCK_SZX_64);
922             break;
923         case 32:
924             response->SetBlockWiseBlockSize(OT_COAP_OPTION_BLOCK_SZX_32);
925             break;
926         case 16:
927             response->SetBlockWiseBlockSize(OT_COAP_OPTION_BLOCK_SZX_16);
928             break;
929         default:
930             error = kErrorInvalidArgs;
931             ExitNow();
932             break;
933         }
934     }
935     else
936     {
937         // Verify that buffer length is not larger than requested block size
938         VerifyOrExit(bufLen <= otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize()),
939                      error = kErrorInvalidArgs);
940         response->SetBlockWiseBlockSize(aMessage.GetBlockWiseBlockSize());
941     }
942 
943     response->SetBlockWiseBlockNumber(
944         (otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize()) * aMessage.GetBlockWiseBlockNumber()) /
945         (otCoapBlockSizeFromExponent(response->GetBlockWiseBlockSize())));
946 
947     // Copy options from last response
948     SuccessOrExit(error = iterator.Init(*mLastResponse));
949 
950     while (!iterator.IsDone())
951     {
952         uint16_t optionNumber = iterator.GetOption()->GetNumber();
953 
954         if (optionNumber == kOptionBlock2)
955         {
956             SuccessOrExit(error = response->AppendBlockOption(Message::kBlockType2, response->GetBlockWiseBlockNumber(),
957                                                               response->IsMoreBlocksFlagSet(),
958                                                               response->GetBlockWiseBlockSize()));
959         }
960         else if (optionNumber == kOptionBlock1)
961         {
962             SuccessOrExit(error = iterator.ReadOptionValue(&optionBuf));
963             SuccessOrExit(error = response->AppendOption(optionNumber, iterator.GetOption()->GetLength(), &optionBuf));
964         }
965 
966         SuccessOrExit(error = iterator.Advance());
967     }
968 
969     SuccessOrExit(error = response->SetPayloadMarker());
970     SuccessOrExit(error = response->AppendBytes(buf, bufLen));
971 
972     if (response->IsMoreBlocksFlagSet())
973     {
974         SuccessOrExit(error = CacheLastBlockResponse(response));
975     }
976     else
977     {
978         // Conclude block-wise transfer if last block has been received
979         FreeLastBlockResponse();
980     }
981 
982     LogInfo("Send Block2 Nr. %d, Size: %d bytes, More Blocks Flag %d", response->GetBlockWiseBlockNumber(),
983             otCoapBlockSizeFromExponent(response->GetBlockWiseBlockSize()), response->IsMoreBlocksFlagSet());
984 
985     SuccessOrExit(error = SendMessage(*response, aMessageInfo));
986 
987 exit:
988     FreeMessageOnError(response, error);
989 
990     return error;
991 }
992 #endif // OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
993 
SendCopy(const Message & aMessage,const Ip6::MessageInfo & aMessageInfo)994 void CoapBase::SendCopy(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
995 {
996     Error    error;
997     Message *messageCopy = nullptr;
998 
999     // Create a message copy for lower layers.
1000     messageCopy = aMessage.Clone(aMessage.GetLength() - sizeof(Metadata));
1001     VerifyOrExit(messageCopy != nullptr, error = kErrorNoBufs);
1002 
1003     SuccessOrExit(error = Send(*messageCopy, aMessageInfo));
1004 
1005 exit:
1006 
1007     if (error != kErrorNone)
1008     {
1009         LogWarn("Failed to send copy: %s", ErrorToString(error));
1010         FreeMessage(messageCopy);
1011     }
1012 }
1013 
FindRelatedRequest(const Message & aResponse,const Ip6::MessageInfo & aMessageInfo,Metadata & aMetadata)1014 Message *CoapBase::FindRelatedRequest(const Message &         aResponse,
1015                                       const Ip6::MessageInfo &aMessageInfo,
1016                                       Metadata &              aMetadata)
1017 {
1018     Message *request = nullptr;
1019 
1020     for (Message &message : mPendingRequests)
1021     {
1022         aMetadata.ReadFrom(message);
1023 
1024         if (((aMetadata.mDestinationAddress == aMessageInfo.GetPeerAddr()) ||
1025              aMetadata.mDestinationAddress.IsMulticast() ||
1026              aMetadata.mDestinationAddress.GetIid().IsAnycastLocator()) &&
1027             (aMetadata.mDestinationPort == aMessageInfo.GetPeerPort()))
1028         {
1029             switch (aResponse.GetType())
1030             {
1031             case kTypeReset:
1032             case kTypeAck:
1033                 if (aResponse.GetMessageId() == message.GetMessageId())
1034                 {
1035                     request = &message;
1036                     ExitNow();
1037                 }
1038 
1039                 break;
1040 
1041             case kTypeConfirmable:
1042             case kTypeNonConfirmable:
1043                 if (aResponse.IsTokenEqual(message))
1044                 {
1045                     request = &message;
1046                     ExitNow();
1047                 }
1048 
1049                 break;
1050             }
1051         }
1052     }
1053 
1054 exit:
1055     return request;
1056 }
1057 
Receive(ot::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)1058 void CoapBase::Receive(ot::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
1059 {
1060     Message &message = AsCoapMessage(&aMessage);
1061 
1062     if (message.ParseHeader() != kErrorNone)
1063     {
1064         LogDebg("Failed to parse CoAP header");
1065 
1066         if (!aMessageInfo.GetSockAddr().IsMulticast() && message.IsConfirmable())
1067         {
1068             IgnoreError(SendReset(message, aMessageInfo));
1069         }
1070     }
1071     else if (message.IsRequest())
1072     {
1073         ProcessReceivedRequest(message, aMessageInfo);
1074     }
1075     else
1076     {
1077         ProcessReceivedResponse(message, aMessageInfo);
1078     }
1079 
1080 #if OPENTHREAD_CONFIG_OTNS_ENABLE
1081     Get<Utils::Otns>().EmitCoapReceive(message, aMessageInfo);
1082 #endif
1083 }
1084 
ProcessReceivedResponse(Message & aMessage,const Ip6::MessageInfo & aMessageInfo)1085 void CoapBase::ProcessReceivedResponse(Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
1086 {
1087     Metadata metadata;
1088     Message *request = nullptr;
1089     Error    error   = kErrorNone;
1090 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
1091     bool responseObserve = false;
1092 #endif
1093 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
1094     uint8_t  blockOptionType    = 0;
1095     uint32_t totalTransfereSize = 0;
1096 #endif
1097 
1098     request = FindRelatedRequest(aMessage, aMessageInfo, metadata);
1099     VerifyOrExit(request != nullptr);
1100 
1101 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
1102     if (metadata.mObserve && request->IsRequest())
1103     {
1104         // We sent Observe in our request, see if we received Observe in the response too.
1105         Option::Iterator iterator;
1106 
1107         SuccessOrExit(error = iterator.Init(aMessage, kOptionObserve));
1108         responseObserve = !iterator.IsDone();
1109     }
1110 #endif
1111 
1112     switch (aMessage.GetType())
1113     {
1114     case kTypeReset:
1115         if (aMessage.IsEmpty())
1116         {
1117             FinalizeCoapTransaction(*request, metadata, nullptr, nullptr, kErrorAbort);
1118         }
1119 
1120         // Silently ignore non-empty reset messages (RFC 7252, p. 4.2).
1121         break;
1122 
1123     case kTypeAck:
1124         if (aMessage.IsEmpty())
1125         {
1126             // Empty acknowledgment.
1127 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
1128             if (metadata.mObserve && !request->IsRequest())
1129             {
1130                 // This is the ACK to our RFC7641 notification.  There will be no
1131                 // "separate" response so pass it back as if it were a piggy-backed
1132                 // response so we can stop re-sending and the application can move on.
1133                 FinalizeCoapTransaction(*request, metadata, &aMessage, &aMessageInfo, kErrorNone);
1134             }
1135             else
1136 #endif
1137             {
1138                 // This is not related to RFC7641 or the outgoing "request" was not a
1139                 // notification.
1140                 if (metadata.mConfirmable)
1141                 {
1142                     metadata.mAcknowledged = true;
1143                     metadata.UpdateIn(*request);
1144                 }
1145 
1146                 // Remove the message if response is not expected, otherwise await
1147                 // response.
1148                 if (metadata.mResponseHandler == nullptr)
1149                 {
1150                     DequeueMessage(*request);
1151                 }
1152             }
1153         }
1154         else if (aMessage.IsResponse() && aMessage.IsTokenEqual(*request))
1155         {
1156             // Piggybacked response.  If there's an Observe option present in both
1157             // request and response, and we have a response handler; then we're
1158             // dealing with RFC7641 rules here.
1159             // (If there is no response handler, then we're wasting our time!)
1160 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
1161             if (metadata.mObserve && responseObserve && (metadata.mResponseHandler != nullptr))
1162             {
1163                 // This is a RFC7641 notification.  The request is *not* done!
1164                 metadata.mResponseHandler(metadata.mResponseContext, &aMessage, &aMessageInfo, kErrorNone);
1165 
1166                 // Consider the message acknowledged at this point.
1167                 metadata.mAcknowledged = true;
1168                 metadata.UpdateIn(*request);
1169             }
1170             else
1171 #endif
1172 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
1173             {
1174                 if (metadata.mBlockwiseTransmitHook != nullptr || metadata.mBlockwiseReceiveHook != nullptr)
1175                 {
1176                     // Search for CoAP Block-Wise Option [RFC7959]
1177                     Option::Iterator iterator;
1178 
1179                     SuccessOrExit(error = iterator.Init(aMessage));
1180                     while (!iterator.IsDone())
1181                     {
1182                         switch (iterator.GetOption()->GetNumber())
1183                         {
1184                         case kOptionBlock1:
1185                             blockOptionType += 1;
1186                             break;
1187 
1188                         case kOptionBlock2:
1189                             blockOptionType += 2;
1190                             break;
1191 
1192                         case kOptionSize2:
1193                             // ToDo: wait for method to read uint option values
1194                             totalTransfereSize = 0;
1195                             break;
1196 
1197                         default:
1198                             break;
1199                         }
1200 
1201                         SuccessOrExit(error = iterator.Advance());
1202                     }
1203                 }
1204                 switch (blockOptionType)
1205                 {
1206                 case 0:
1207                     // Piggybacked response.
1208                     FinalizeCoapTransaction(*request, metadata, &aMessage, &aMessageInfo, kErrorNone);
1209                     break;
1210                 case 1: // Block1 option
1211                     if (aMessage.GetCode() == kCodeContinue && metadata.mBlockwiseTransmitHook != nullptr)
1212                     {
1213                         error = SendNextBlock1Request(*request, aMessage, aMessageInfo, metadata);
1214                     }
1215 
1216                     if (aMessage.GetCode() != kCodeContinue || metadata.mBlockwiseTransmitHook == nullptr ||
1217                         error != kErrorNone)
1218                     {
1219                         FinalizeCoapTransaction(*request, metadata, &aMessage, &aMessageInfo, error);
1220                     }
1221                     break;
1222                 case 2: // Block2 option
1223                     if (aMessage.GetCode() < kCodeBadRequest && metadata.mBlockwiseReceiveHook != nullptr)
1224                     {
1225                         error = SendNextBlock2Request(*request, aMessage, aMessageInfo, metadata, totalTransfereSize,
1226                                                       false);
1227                     }
1228 
1229                     if (aMessage.GetCode() >= kCodeBadRequest || metadata.mBlockwiseReceiveHook == nullptr ||
1230                         error != kErrorNone)
1231                     {
1232                         FinalizeCoapTransaction(*request, metadata, &aMessage, &aMessageInfo, error);
1233                     }
1234                     break;
1235                 case 3: // Block1 & Block2 option
1236                     if (aMessage.GetCode() < kCodeBadRequest && metadata.mBlockwiseReceiveHook != nullptr)
1237                     {
1238                         error =
1239                             SendNextBlock2Request(*request, aMessage, aMessageInfo, metadata, totalTransfereSize, true);
1240                     }
1241 
1242                     FinalizeCoapTransaction(*request, metadata, &aMessage, &aMessageInfo, error);
1243                     break;
1244                 default:
1245                     error = kErrorAbort;
1246                     FinalizeCoapTransaction(*request, metadata, &aMessage, &aMessageInfo, error);
1247                     break;
1248                 }
1249             }
1250 #else  // OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
1251             {
1252                 FinalizeCoapTransaction(*request, metadata, &aMessage, &aMessageInfo, kErrorNone);
1253             }
1254 #endif // OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
1255         }
1256 
1257         // Silently ignore acknowledgments carrying requests (RFC 7252, p. 4.2)
1258         // or with no token match (RFC 7252, p. 5.3.2)
1259         break;
1260 
1261     case kTypeConfirmable:
1262         // Send empty ACK if it is a CON message.
1263         IgnoreError(SendAck(aMessage, aMessageInfo));
1264 
1265         OT_FALL_THROUGH;
1266         // Handling of RFC7641 and multicast is below.
1267     case kTypeNonConfirmable:
1268         // Separate response or observation notification.  If the request was to a multicast
1269         // address, OR both the request and response carry Observe options, then this is NOT
1270         // the final message, we may see multiples.
1271         if ((metadata.mResponseHandler != nullptr) && (metadata.mDestinationAddress.IsMulticast()
1272 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
1273                                                        || (metadata.mObserve && responseObserve)
1274 #endif
1275                                                            ))
1276         {
1277             // If multicast non-confirmable request, allow multiple responses
1278             metadata.mResponseHandler(metadata.mResponseContext, &aMessage, &aMessageInfo, kErrorNone);
1279         }
1280         else
1281         {
1282             FinalizeCoapTransaction(*request, metadata, &aMessage, &aMessageInfo, kErrorNone);
1283         }
1284 
1285         break;
1286     }
1287 
1288 exit:
1289 
1290     if (error == kErrorNone && request == nullptr)
1291     {
1292         if (aMessage.IsConfirmable() || aMessage.IsNonConfirmable())
1293         {
1294             // Successfully parsed a header but no matching request was
1295             // found - reject the message by sending reset.
1296             IgnoreError(SendReset(aMessage, aMessageInfo));
1297         }
1298     }
1299 }
1300 
ProcessReceivedRequest(Message & aMessage,const Ip6::MessageInfo & aMessageInfo)1301 void CoapBase::ProcessReceivedRequest(Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
1302 {
1303     char     uriPath[Message::kMaxReceivedUriPath + 1];
1304     Message *cachedResponse = nullptr;
1305     Error    error          = kErrorNotFound;
1306 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
1307     Option::Iterator iterator;
1308     char *           curUriPath         = uriPath;
1309     uint8_t          blockOptionType    = 0;
1310     uint32_t         totalTransfereSize = 0;
1311 #endif
1312 
1313     if (mInterceptor != nullptr)
1314     {
1315         SuccessOrExit(error = mInterceptor(aMessage, aMessageInfo, mContext));
1316     }
1317 
1318     switch (mResponsesQueue.GetMatchedResponseCopy(aMessage, aMessageInfo, &cachedResponse))
1319     {
1320     case kErrorNone:
1321         cachedResponse->Finish();
1322         error = Send(*cachedResponse, aMessageInfo);
1323 
1324         OT_FALL_THROUGH;
1325 
1326     case kErrorNoBufs:
1327         ExitNow();
1328 
1329     case kErrorNotFound:
1330     default:
1331         break;
1332     }
1333 
1334 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
1335     SuccessOrExit(error = iterator.Init(aMessage));
1336 
1337     while (!iterator.IsDone())
1338     {
1339         switch (iterator.GetOption()->GetNumber())
1340         {
1341         case kOptionUriPath:
1342             if (curUriPath != uriPath)
1343             {
1344                 *curUriPath++ = '/';
1345             }
1346 
1347             VerifyOrExit(curUriPath + iterator.GetOption()->GetLength() < GetArrayEnd(uriPath), error = kErrorParse);
1348 
1349             IgnoreError(iterator.ReadOptionValue(curUriPath));
1350             curUriPath += iterator.GetOption()->GetLength();
1351             break;
1352 
1353         case kOptionBlock1:
1354             blockOptionType += 1;
1355             break;
1356 
1357         case kOptionBlock2:
1358             blockOptionType += 2;
1359             break;
1360 
1361         case kOptionSize1:
1362             // ToDo: wait for method to read uint option values
1363             totalTransfereSize = 0;
1364             break;
1365 
1366         default:
1367             break;
1368         }
1369 
1370         SuccessOrExit(error = iterator.Advance());
1371     }
1372 
1373     curUriPath[0] = '\0';
1374 
1375     for (const ResourceBlockWise &resource : mBlockWiseResources)
1376     {
1377         if (strcmp(resource.GetUriPath(), uriPath) != 0)
1378         {
1379             continue;
1380         }
1381 
1382         if ((resource.mReceiveHook != nullptr || resource.mTransmitHook != nullptr) && blockOptionType != 0)
1383         {
1384             switch (blockOptionType)
1385             {
1386             case 1:
1387                 if (resource.mReceiveHook != nullptr)
1388                 {
1389                     switch (ProcessBlock1Request(aMessage, aMessageInfo, resource, totalTransfereSize))
1390                     {
1391                     case kErrorNone:
1392                         resource.HandleRequest(aMessage, aMessageInfo);
1393                         // Fall through
1394                     case kErrorBusy:
1395                         error = kErrorNone;
1396                         break;
1397                     case kErrorNoBufs:
1398                         IgnoreReturnValue(SendHeaderResponse(kCodeRequestTooLarge, aMessage, aMessageInfo));
1399                         error = kErrorDrop;
1400                         break;
1401                     case kErrorNoFrameReceived:
1402                         IgnoreReturnValue(SendHeaderResponse(kCodeRequestIncomplete, aMessage, aMessageInfo));
1403                         error = kErrorDrop;
1404                         break;
1405                     default:
1406                         IgnoreReturnValue(SendHeaderResponse(kCodeInternalError, aMessage, aMessageInfo));
1407                         error = kErrorDrop;
1408                         break;
1409                     }
1410                 }
1411                 break;
1412             case 2:
1413                 if (resource.mTransmitHook != nullptr)
1414                 {
1415                     if ((error = ProcessBlock2Request(aMessage, aMessageInfo, resource)) != kErrorNone)
1416                     {
1417                         IgnoreReturnValue(SendHeaderResponse(kCodeInternalError, aMessage, aMessageInfo));
1418                         error = kErrorDrop;
1419                     }
1420                 }
1421                 break;
1422             }
1423             ExitNow();
1424         }
1425         else
1426         {
1427             resource.HandleRequest(aMessage, aMessageInfo);
1428             error = kErrorNone;
1429             ExitNow();
1430         }
1431     }
1432 #else
1433     SuccessOrExit(error = aMessage.ReadUriPathOptions(uriPath));
1434 #endif // OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
1435 
1436     for (const Resource &resource : mResources)
1437     {
1438         if (strcmp(resource.mUriPath, uriPath) == 0)
1439         {
1440             resource.HandleRequest(aMessage, aMessageInfo);
1441             error = kErrorNone;
1442             ExitNow();
1443         }
1444     }
1445 
1446     if (mDefaultHandler)
1447     {
1448         mDefaultHandler(mDefaultHandlerContext, &aMessage, &aMessageInfo);
1449         error = kErrorNone;
1450     }
1451 
1452 exit:
1453 
1454     if (error != kErrorNone)
1455     {
1456         LogInfo("Failed to process request: %s", ErrorToString(error));
1457 
1458         if (error == kErrorNotFound && !aMessageInfo.GetSockAddr().IsMulticast())
1459         {
1460             IgnoreError(SendNotFound(aMessage, aMessageInfo));
1461         }
1462 
1463         FreeMessage(cachedResponse);
1464     }
1465 }
1466 
ReadFrom(const Message & aMessage)1467 void CoapBase::Metadata::ReadFrom(const Message &aMessage)
1468 {
1469     uint16_t length = aMessage.GetLength();
1470 
1471     OT_ASSERT(length >= sizeof(*this));
1472     IgnoreError(aMessage.Read(length - sizeof(*this), *this));
1473 }
1474 
UpdateIn(Message & aMessage) const1475 void CoapBase::Metadata::UpdateIn(Message &aMessage) const
1476 {
1477     aMessage.Write(aMessage.GetLength() - sizeof(*this), *this);
1478 }
1479 
ResponsesQueue(Instance & aInstance)1480 ResponsesQueue::ResponsesQueue(Instance &aInstance)
1481     : mTimer(aInstance, ResponsesQueue::HandleTimer, this)
1482 {
1483 }
1484 
GetMatchedResponseCopy(const Message & aRequest,const Ip6::MessageInfo & aMessageInfo,Message ** aResponse)1485 Error ResponsesQueue::GetMatchedResponseCopy(const Message &         aRequest,
1486                                              const Ip6::MessageInfo &aMessageInfo,
1487                                              Message **              aResponse)
1488 {
1489     Error          error = kErrorNone;
1490     const Message *cacheResponse;
1491 
1492     cacheResponse = FindMatchedResponse(aRequest, aMessageInfo);
1493     VerifyOrExit(cacheResponse != nullptr, error = kErrorNotFound);
1494 
1495     *aResponse = cacheResponse->Clone(cacheResponse->GetLength() - sizeof(ResponseMetadata));
1496     VerifyOrExit(*aResponse != nullptr, error = kErrorNoBufs);
1497 
1498 exit:
1499     return error;
1500 }
1501 
FindMatchedResponse(const Message & aRequest,const Ip6::MessageInfo & aMessageInfo) const1502 const Message *ResponsesQueue::FindMatchedResponse(const Message &aRequest, const Ip6::MessageInfo &aMessageInfo) const
1503 {
1504     const Message *response = nullptr;
1505 
1506     for (const Message &message : mQueue)
1507     {
1508         if (message.GetMessageId() == aRequest.GetMessageId())
1509         {
1510             ResponseMetadata metadata;
1511 
1512             metadata.ReadFrom(message);
1513 
1514             if ((metadata.mMessageInfo.GetPeerPort() == aMessageInfo.GetPeerPort()) &&
1515                 (metadata.mMessageInfo.GetPeerAddr() == aMessageInfo.GetPeerAddr()))
1516             {
1517                 response = &message;
1518                 break;
1519             }
1520         }
1521     }
1522 
1523     return response;
1524 }
1525 
EnqueueResponse(Message & aMessage,const Ip6::MessageInfo & aMessageInfo,const TxParameters & aTxParameters)1526 void ResponsesQueue::EnqueueResponse(Message &               aMessage,
1527                                      const Ip6::MessageInfo &aMessageInfo,
1528                                      const TxParameters &    aTxParameters)
1529 {
1530     Message *        responseCopy;
1531     ResponseMetadata metadata;
1532 
1533     metadata.mDequeueTime = TimerMilli::GetNow() + aTxParameters.CalculateExchangeLifetime();
1534     metadata.mMessageInfo = aMessageInfo;
1535 
1536     VerifyOrExit(FindMatchedResponse(aMessage, aMessageInfo) == nullptr);
1537 
1538     UpdateQueue();
1539 
1540     VerifyOrExit((responseCopy = aMessage.Clone()) != nullptr);
1541 
1542     VerifyOrExit(metadata.AppendTo(*responseCopy) == kErrorNone, responseCopy->Free());
1543 
1544     mQueue.Enqueue(*responseCopy);
1545 
1546     mTimer.FireAtIfEarlier(metadata.mDequeueTime);
1547 
1548 exit:
1549     return;
1550 }
1551 
UpdateQueue(void)1552 void ResponsesQueue::UpdateQueue(void)
1553 {
1554     uint16_t  msgCount    = 0;
1555     Message * earliestMsg = nullptr;
1556     TimeMilli earliestDequeueTime(0);
1557 
1558     // Check the number of messages in the queue and if number is at
1559     // `kMaxCachedResponses` remove the one with earliest dequeue
1560     // time.
1561 
1562     for (Message &message : mQueue)
1563     {
1564         ResponseMetadata metadata;
1565 
1566         metadata.ReadFrom(message);
1567 
1568         if ((earliestMsg == nullptr) || (metadata.mDequeueTime < earliestDequeueTime))
1569         {
1570             earliestMsg         = &message;
1571             earliestDequeueTime = metadata.mDequeueTime;
1572         }
1573 
1574         msgCount++;
1575     }
1576 
1577     if (msgCount >= kMaxCachedResponses)
1578     {
1579         DequeueResponse(*earliestMsg);
1580     }
1581 }
1582 
DequeueResponse(Message & aMessage)1583 void ResponsesQueue::DequeueResponse(Message &aMessage)
1584 {
1585     mQueue.DequeueAndFree(aMessage);
1586 }
1587 
DequeueAllResponses(void)1588 void ResponsesQueue::DequeueAllResponses(void)
1589 {
1590     mQueue.DequeueAndFreeAll();
1591 }
1592 
HandleTimer(Timer & aTimer)1593 void ResponsesQueue::HandleTimer(Timer &aTimer)
1594 {
1595     static_cast<ResponsesQueue *>(static_cast<TimerMilliContext &>(aTimer).GetContext())->HandleTimer();
1596 }
1597 
HandleTimer(void)1598 void ResponsesQueue::HandleTimer(void)
1599 {
1600     TimeMilli now             = TimerMilli::GetNow();
1601     TimeMilli nextDequeueTime = now.GetDistantFuture();
1602 
1603     for (Message &message : mQueue)
1604     {
1605         ResponseMetadata metadata;
1606 
1607         metadata.ReadFrom(message);
1608 
1609         if (now >= metadata.mDequeueTime)
1610         {
1611             DequeueResponse(message);
1612             continue;
1613         }
1614 
1615         if (metadata.mDequeueTime < nextDequeueTime)
1616         {
1617             nextDequeueTime = metadata.mDequeueTime;
1618         }
1619     }
1620 
1621     if (nextDequeueTime < now.GetDistantFuture())
1622     {
1623         mTimer.FireAt(nextDequeueTime);
1624     }
1625 }
1626 
ReadFrom(const Message & aMessage)1627 void ResponsesQueue::ResponseMetadata::ReadFrom(const Message &aMessage)
1628 {
1629     uint16_t length = aMessage.GetLength();
1630 
1631     OT_ASSERT(length >= sizeof(*this));
1632     IgnoreError(aMessage.Read(length - sizeof(*this), *this));
1633 }
1634 
1635 /// Return product of @p aValueA and @p aValueB if no overflow otherwise 0.
Multiply(uint32_t aValueA,uint32_t aValueB)1636 static uint32_t Multiply(uint32_t aValueA, uint32_t aValueB)
1637 {
1638     uint32_t result = 0;
1639 
1640     VerifyOrExit(aValueA);
1641 
1642     result = aValueA * aValueB;
1643     result = (result / aValueA == aValueB) ? result : 0;
1644 
1645 exit:
1646     return result;
1647 }
1648 
IsValid(void) const1649 bool TxParameters::IsValid(void) const
1650 {
1651     bool rval = false;
1652 
1653     if ((mAckRandomFactorDenominator > 0) && (mAckRandomFactorNumerator >= mAckRandomFactorDenominator) &&
1654         (mAckTimeout >= OT_COAP_MIN_ACK_TIMEOUT) && (mMaxRetransmit <= OT_COAP_MAX_RETRANSMIT))
1655     {
1656         // Calulate exchange lifetime step by step and verify no overflow.
1657         uint32_t tmp = Multiply(mAckTimeout, (1U << (mMaxRetransmit + 1)) - 1);
1658 
1659         tmp = Multiply(tmp, mAckRandomFactorNumerator);
1660         tmp /= mAckRandomFactorDenominator;
1661 
1662         rval = (tmp != 0 && (tmp + mAckTimeout + 2 * kDefaultMaxLatency) > tmp);
1663     }
1664 
1665     return rval;
1666 }
1667 
CalculateInitialRetransmissionTimeout(void) const1668 uint32_t TxParameters::CalculateInitialRetransmissionTimeout(void) const
1669 {
1670     return Random::NonCrypto::GetUint32InRange(
1671         mAckTimeout, mAckTimeout * mAckRandomFactorNumerator / mAckRandomFactorDenominator + 1);
1672 }
1673 
CalculateExchangeLifetime(void) const1674 uint32_t TxParameters::CalculateExchangeLifetime(void) const
1675 {
1676     // Final `mAckTimeout` is to account for processing delay.
1677     return CalculateSpan(mMaxRetransmit) + 2 * kDefaultMaxLatency + mAckTimeout;
1678 }
1679 
CalculateMaxTransmitWait(void) const1680 uint32_t TxParameters::CalculateMaxTransmitWait(void) const
1681 {
1682     return CalculateSpan(mMaxRetransmit + 1);
1683 }
1684 
CalculateSpan(uint8_t aMaxRetx) const1685 uint32_t TxParameters::CalculateSpan(uint8_t aMaxRetx) const
1686 {
1687     return static_cast<uint32_t>(mAckTimeout * ((1U << aMaxRetx) - 1) / mAckRandomFactorDenominator *
1688                                  mAckRandomFactorNumerator);
1689 }
1690 
1691 const otCoapTxParameters TxParameters::kDefaultTxParameters = {
1692     kDefaultAckTimeout,
1693     kDefaultAckRandomFactorNumerator,
1694     kDefaultAckRandomFactorDenominator,
1695     kDefaultMaxRetransmit,
1696 };
1697 
Coap(Instance & aInstance)1698 Coap::Coap(Instance &aInstance)
1699     : CoapBase(aInstance, &Coap::Send)
1700     , mSocket(aInstance)
1701 {
1702 }
1703 
Start(uint16_t aPort,otNetifIdentifier aNetifIdentifier)1704 Error Coap::Start(uint16_t aPort, otNetifIdentifier aNetifIdentifier)
1705 {
1706     Error error        = kErrorNone;
1707     bool  socketOpened = false;
1708 
1709     VerifyOrExit(!mSocket.IsBound());
1710 
1711     SuccessOrExit(error = mSocket.Open(&Coap::HandleUdpReceive, this));
1712     socketOpened = true;
1713 
1714     SuccessOrExit(error = mSocket.Bind(aPort, aNetifIdentifier));
1715 
1716 exit:
1717     if (error != kErrorNone && socketOpened)
1718     {
1719         IgnoreError(mSocket.Close());
1720     }
1721 
1722     return error;
1723 }
1724 
Stop(void)1725 Error Coap::Stop(void)
1726 {
1727     Error error = kErrorNone;
1728 
1729     VerifyOrExit(mSocket.IsBound());
1730 
1731     SuccessOrExit(error = mSocket.Close());
1732     ClearRequestsAndResponses();
1733 
1734 exit:
1735     return error;
1736 }
1737 
HandleUdpReceive(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo)1738 void Coap::HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
1739 {
1740     static_cast<Coap *>(aContext)->Receive(AsCoapMessage(aMessage), AsCoreType(aMessageInfo));
1741 }
1742 
Send(CoapBase & aCoapBase,ot::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)1743 Error Coap::Send(CoapBase &aCoapBase, ot::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
1744 {
1745     return static_cast<Coap &>(aCoapBase).Send(aMessage, aMessageInfo);
1746 }
1747 
Send(ot::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)1748 Error Coap::Send(ot::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
1749 {
1750     return mSocket.IsBound() ? mSocket.SendTo(aMessage, aMessageInfo) : kErrorInvalidState;
1751 }
1752 
1753 } // namespace Coap
1754 } // namespace ot
1755