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