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