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/locator_getters.hpp"
36 #include "common/log.hpp"
37 #include "common/random.hpp"
38 #include "instance/instance.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     NextFireTime     nextTime;
468     Metadata         metadata;
469     Ip6::MessageInfo messageInfo;
470 
471     for (Message &message : mPendingRequests)
472     {
473         metadata.ReadFrom(message);
474 
475         if (nextTime.GetNow() >= metadata.mNextTimerShot)
476         {
477 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
478             if (message.IsRequest() && metadata.mObserve && metadata.mAcknowledged)
479             {
480                 // This is a RFC7641 subscription.  Do not time out.
481                 continue;
482             }
483 #endif
484 
485             if (!metadata.mConfirmable || (metadata.mRetransmissionsRemaining == 0))
486             {
487                 // No expected response or acknowledgment.
488                 FinalizeCoapTransaction(message, metadata, nullptr, nullptr, kErrorResponseTimeout);
489                 continue;
490             }
491 
492             // Increment retransmission counter and timer.
493             metadata.mRetransmissionsRemaining--;
494             metadata.mRetransmissionTimeout *= 2;
495             metadata.mNextTimerShot = nextTime.GetNow() + metadata.mRetransmissionTimeout;
496             metadata.UpdateIn(message);
497 
498             // Retransmit
499             if (!metadata.mAcknowledged)
500             {
501                 messageInfo.SetPeerAddr(metadata.mDestinationAddress);
502                 messageInfo.SetPeerPort(metadata.mDestinationPort);
503                 messageInfo.SetSockAddr(metadata.mSourceAddress);
504 #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
505                 messageInfo.SetHopLimit(metadata.mHopLimit);
506                 messageInfo.SetIsHostInterface(metadata.mIsHostInterface);
507 #endif
508                 messageInfo.SetMulticastLoop(metadata.mMulticastLoop);
509 
510                 SendCopy(message, messageInfo);
511             }
512         }
513 
514         nextTime.UpdateIfEarlier(metadata.mNextTimerShot);
515     }
516 
517     mRetransmissionTimer.FireAt(nextTime);
518 }
519 
FinalizeCoapTransaction(Message & aRequest,const Metadata & aMetadata,Message * aResponse,const Ip6::MessageInfo * aMessageInfo,Error aResult)520 void CoapBase::FinalizeCoapTransaction(Message                &aRequest,
521                                        const Metadata         &aMetadata,
522                                        Message                *aResponse,
523                                        const Ip6::MessageInfo *aMessageInfo,
524                                        Error                   aResult)
525 {
526     DequeueMessage(aRequest);
527 
528     if (aMetadata.mResponseHandler != nullptr)
529     {
530         aMetadata.mResponseHandler(aMetadata.mResponseContext, aResponse, aMessageInfo, aResult);
531     }
532 }
533 
AbortTransaction(ResponseHandler aHandler,void * aContext)534 Error CoapBase::AbortTransaction(ResponseHandler aHandler, void *aContext)
535 {
536     Error    error = kErrorNotFound;
537     Metadata metadata;
538 
539     for (Message &message : mPendingRequests)
540     {
541         metadata.ReadFrom(message);
542 
543         if (metadata.mResponseHandler == aHandler && metadata.mResponseContext == aContext)
544         {
545             FinalizeCoapTransaction(message, metadata, nullptr, nullptr, kErrorAbort);
546             error = kErrorNone;
547         }
548     }
549 
550     return error;
551 }
552 
CopyAndEnqueueMessage(const Message & aMessage,uint16_t aCopyLength,const Metadata & aMetadata)553 Message *CoapBase::CopyAndEnqueueMessage(const Message &aMessage, uint16_t aCopyLength, const Metadata &aMetadata)
554 {
555     Error    error       = kErrorNone;
556     Message *messageCopy = nullptr;
557 
558     VerifyOrExit((messageCopy = aMessage.Clone(aCopyLength)) != nullptr, error = kErrorNoBufs);
559 
560     SuccessOrExit(error = aMetadata.AppendTo(*messageCopy));
561 
562     mRetransmissionTimer.FireAtIfEarlier(aMetadata.mNextTimerShot);
563 
564     mPendingRequests.Enqueue(*messageCopy);
565 
566 exit:
567     FreeAndNullMessageOnError(messageCopy, error);
568     return messageCopy;
569 }
570 
DequeueMessage(Message & aMessage)571 void CoapBase::DequeueMessage(Message &aMessage)
572 {
573     mPendingRequests.Dequeue(aMessage);
574 
575     if (mRetransmissionTimer.IsRunning() && (mPendingRequests.GetHead() == nullptr))
576     {
577         mRetransmissionTimer.Stop();
578     }
579 
580     aMessage.Free();
581 
582     // No need to worry that the earliest pending message was removed -
583     // the timer would just shoot earlier and then it'd be setup again.
584 }
585 
586 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
FreeLastBlockResponse(void)587 void CoapBase::FreeLastBlockResponse(void)
588 {
589     if (mLastResponse != nullptr)
590     {
591         mLastResponse->Free();
592         mLastResponse = nullptr;
593     }
594 }
595 
CacheLastBlockResponse(Message * aResponse)596 Error CoapBase::CacheLastBlockResponse(Message *aResponse)
597 {
598     Error error = kErrorNone;
599     // Save last response for block-wise transfer
600     FreeLastBlockResponse();
601 
602     if ((mLastResponse = aResponse->Clone()) == nullptr)
603     {
604         error = kErrorNoBufs;
605     }
606 
607     return error;
608 }
609 
PrepareNextBlockRequest(Message::BlockType aType,bool aMoreBlocks,Message & aRequestOld,Message & aRequest,Message & aMessage)610 Error CoapBase::PrepareNextBlockRequest(Message::BlockType aType,
611                                         bool               aMoreBlocks,
612                                         Message           &aRequestOld,
613                                         Message           &aRequest,
614                                         Message           &aMessage)
615 {
616     Error            error       = kErrorNone;
617     bool             isOptionSet = false;
618     uint16_t         blockOption = 0;
619     Option::Iterator iterator;
620 
621     blockOption = (aType == Message::kBlockType1) ? kOptionBlock1 : kOptionBlock2;
622 
623     aRequest.Init(kTypeConfirmable, static_cast<ot::Coap::Code>(aRequestOld.GetCode()));
624     SuccessOrExit(error = iterator.Init(aRequestOld));
625 
626     // Copy options from last response to next message
627     for (; !iterator.IsDone() && iterator.GetOption()->GetLength() != 0; error = iterator.Advance())
628     {
629         uint16_t optionNumber = iterator.GetOption()->GetNumber();
630 
631         SuccessOrExit(error);
632 
633         // Check if option to copy next is higher than or equal to Block1 option
634         if (optionNumber >= blockOption && !isOptionSet)
635         {
636             // Write Block1 option to next message
637             SuccessOrExit(error = aRequest.AppendBlockOption(aType, aMessage.GetBlockWiseBlockNumber() + 1, aMoreBlocks,
638                                                              aMessage.GetBlockWiseBlockSize()));
639             aRequest.SetBlockWiseBlockNumber(aMessage.GetBlockWiseBlockNumber() + 1);
640             aRequest.SetBlockWiseBlockSize(aMessage.GetBlockWiseBlockSize());
641             aRequest.SetMoreBlocksFlag(aMoreBlocks);
642 
643             isOptionSet = true;
644 
645             // If option to copy next is Block1 or Block2 option, option is not copied
646             if (optionNumber == kOptionBlock1 || optionNumber == kOptionBlock2)
647             {
648                 continue;
649             }
650         }
651 
652         // Copy option
653         SuccessOrExit(error = aRequest.AppendOptionFromMessage(optionNumber, iterator.GetOption()->GetLength(),
654                                                                iterator.GetMessage(),
655                                                                iterator.GetOptionValueMessageOffset()));
656     }
657 
658     if (!isOptionSet)
659     {
660         // Write Block1 option to next message
661         SuccessOrExit(error = aRequest.AppendBlockOption(aType, aMessage.GetBlockWiseBlockNumber() + 1, aMoreBlocks,
662                                                          aMessage.GetBlockWiseBlockSize()));
663         aRequest.SetBlockWiseBlockNumber(aMessage.GetBlockWiseBlockNumber() + 1);
664         aRequest.SetBlockWiseBlockSize(aMessage.GetBlockWiseBlockSize());
665         aRequest.SetMoreBlocksFlag(aMoreBlocks);
666     }
667 
668 exit:
669     return error;
670 }
671 
SendNextBlock1Request(Message & aRequest,Message & aMessage,const Ip6::MessageInfo & aMessageInfo,const Metadata & aCoapMetadata)672 Error CoapBase::SendNextBlock1Request(Message                &aRequest,
673                                       Message                &aMessage,
674                                       const Ip6::MessageInfo &aMessageInfo,
675                                       const Metadata         &aCoapMetadata)
676 {
677     Error    error                = kErrorNone;
678     Message *request              = nullptr;
679     bool     moreBlocks           = false;
680     uint8_t  buf[kMaxBlockLength] = {0};
681     uint16_t bufLen               = kMaxBlockLength;
682 
683     SuccessOrExit(error = aRequest.ReadBlockOptionValues(kOptionBlock1));
684     SuccessOrExit(error = aMessage.ReadBlockOptionValues(kOptionBlock1));
685 
686     // Conclude block-wise transfer if last block has been received
687     if (!aRequest.IsMoreBlocksFlagSet())
688     {
689         FinalizeCoapTransaction(aRequest, aCoapMetadata, &aMessage, &aMessageInfo, kErrorNone);
690         ExitNow();
691     }
692 
693     // Get next block
694     VerifyOrExit((bufLen = otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize())) <= kMaxBlockLength,
695                  error = kErrorNoBufs);
696 
697     SuccessOrExit(
698         error = aCoapMetadata.mBlockwiseTransmitHook(aCoapMetadata.mResponseContext, buf,
699                                                      otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize()) *
700                                                          (aMessage.GetBlockWiseBlockNumber() + 1),
701                                                      &bufLen, &moreBlocks));
702 
703     // Check if block length is valid
704     VerifyOrExit(bufLen <= otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize()), error = kErrorInvalidArgs);
705 
706     // Init request for next block
707     VerifyOrExit((request = NewMessage()) != nullptr, error = kErrorNoBufs);
708     SuccessOrExit(error = PrepareNextBlockRequest(Message::kBlockType1, moreBlocks, aRequest, *request, aMessage));
709 
710     SuccessOrExit(error = request->SetPayloadMarker());
711 
712     SuccessOrExit(error = request->AppendBytes(buf, bufLen));
713 
714     DequeueMessage(aRequest);
715 
716     LogInfo("Send Block1 Nr. %d, Size: %d bytes, More Blocks Flag: %d", request->GetBlockWiseBlockNumber(),
717             otCoapBlockSizeFromExponent(request->GetBlockWiseBlockSize()), request->IsMoreBlocksFlagSet());
718 
719     SuccessOrExit(error = SendMessage(*request, aMessageInfo, TxParameters::GetDefault(),
720                                       aCoapMetadata.mResponseHandler, aCoapMetadata.mResponseContext,
721                                       aCoapMetadata.mBlockwiseTransmitHook, aCoapMetadata.mBlockwiseReceiveHook));
722 
723 exit:
724     FreeMessageOnError(request, error);
725 
726     return error;
727 }
728 
SendNextBlock2Request(Message & aRequest,Message & aMessage,const Ip6::MessageInfo & aMessageInfo,const Metadata & aCoapMetadata,uint32_t aTotalLength,bool aBeginBlock1Transfer)729 Error CoapBase::SendNextBlock2Request(Message                &aRequest,
730                                       Message                &aMessage,
731                                       const Ip6::MessageInfo &aMessageInfo,
732                                       const Metadata         &aCoapMetadata,
733                                       uint32_t                aTotalLength,
734                                       bool                    aBeginBlock1Transfer)
735 {
736     Error    error                = kErrorNone;
737     Message *request              = nullptr;
738     uint8_t  buf[kMaxBlockLength] = {0};
739     uint16_t bufLen               = kMaxBlockLength;
740 
741     SuccessOrExit(error = aMessage.ReadBlockOptionValues(kOptionBlock2));
742 
743     // Check payload and block length
744     VerifyOrExit((aMessage.GetLength() - aMessage.GetOffset()) <=
745                          otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize()) &&
746                      (aMessage.GetLength() - aMessage.GetOffset()) <= kMaxBlockLength,
747                  error = kErrorNoBufs);
748 
749     // Read and then forward payload to receive hook function
750     bufLen = aMessage.ReadBytes(aMessage.GetOffset(), buf, aMessage.GetLength() - aMessage.GetOffset());
751     SuccessOrExit(
752         error = aCoapMetadata.mBlockwiseReceiveHook(aCoapMetadata.mResponseContext, buf,
753                                                     otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize()) *
754                                                         aMessage.GetBlockWiseBlockNumber(),
755                                                     bufLen, aMessage.IsMoreBlocksFlagSet(), aTotalLength));
756 
757     // CoAP Block-Wise Transfer continues
758     LogInfo("Received Block2 Nr. %d , Size: %d bytes, More Blocks Flag: %d", aMessage.GetBlockWiseBlockNumber(),
759             otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize()), aMessage.IsMoreBlocksFlagSet());
760 
761     // Conclude block-wise transfer if last block has been received
762     if (!aMessage.IsMoreBlocksFlagSet())
763     {
764         FinalizeCoapTransaction(aRequest, aCoapMetadata, &aMessage, &aMessageInfo, kErrorNone);
765         ExitNow();
766     }
767 
768     // Init request for next block
769     VerifyOrExit((request = NewMessage()) != nullptr, error = kErrorNoBufs);
770     SuccessOrExit(error = PrepareNextBlockRequest(Message::kBlockType2, aMessage.IsMoreBlocksFlagSet(), aRequest,
771                                                   *request, aMessage));
772 
773     if (!aBeginBlock1Transfer)
774     {
775         DequeueMessage(aRequest);
776     }
777 
778     LogInfo("Request Block2 Nr. %d, Size: %d bytes", request->GetBlockWiseBlockNumber(),
779             otCoapBlockSizeFromExponent(request->GetBlockWiseBlockSize()));
780 
781     SuccessOrExit(error =
782                       SendMessage(*request, aMessageInfo, TxParameters::GetDefault(), aCoapMetadata.mResponseHandler,
783                                   aCoapMetadata.mResponseContext, nullptr, aCoapMetadata.mBlockwiseReceiveHook));
784 
785 exit:
786     FreeMessageOnError(request, error);
787 
788     return error;
789 }
790 
ProcessBlock1Request(Message & aMessage,const Ip6::MessageInfo & aMessageInfo,const ResourceBlockWise & aResource,uint32_t aTotalLength)791 Error CoapBase::ProcessBlock1Request(Message                 &aMessage,
792                                      const Ip6::MessageInfo  &aMessageInfo,
793                                      const ResourceBlockWise &aResource,
794                                      uint32_t                 aTotalLength)
795 {
796     Error    error                = kErrorNone;
797     Message *response             = nullptr;
798     uint8_t  buf[kMaxBlockLength] = {0};
799     uint16_t bufLen               = kMaxBlockLength;
800 
801     SuccessOrExit(error = aMessage.ReadBlockOptionValues(kOptionBlock1));
802 
803     // Read and then forward payload to receive hook function
804     VerifyOrExit((aMessage.GetLength() - aMessage.GetOffset()) <= kMaxBlockLength, error = kErrorNoBufs);
805     bufLen = aMessage.ReadBytes(aMessage.GetOffset(), buf, aMessage.GetLength() - aMessage.GetOffset());
806     SuccessOrExit(error = aResource.HandleBlockReceive(buf,
807                                                        otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize()) *
808                                                            aMessage.GetBlockWiseBlockNumber(),
809                                                        bufLen, aMessage.IsMoreBlocksFlagSet(), aTotalLength));
810 
811     if (aMessage.IsMoreBlocksFlagSet())
812     {
813         // Set up next response
814         VerifyOrExit((response = NewMessage()) != nullptr, error = kErrorFailed);
815         response->Init(kTypeAck, kCodeContinue);
816         response->SetMessageId(aMessage.GetMessageId());
817         IgnoreReturnValue(response->SetToken(AsConst(aMessage).GetToken(), aMessage.GetTokenLength()));
818 
819         response->SetBlockWiseBlockNumber(aMessage.GetBlockWiseBlockNumber());
820         response->SetMoreBlocksFlag(aMessage.IsMoreBlocksFlagSet());
821         response->SetBlockWiseBlockSize(aMessage.GetBlockWiseBlockSize());
822 
823         SuccessOrExit(error = response->AppendBlockOption(Message::kBlockType1, response->GetBlockWiseBlockNumber(),
824                                                           response->IsMoreBlocksFlagSet(),
825                                                           response->GetBlockWiseBlockSize()));
826 
827         SuccessOrExit(error = CacheLastBlockResponse(response));
828 
829         LogInfo("Acknowledge Block1 Nr. %d, Size: %d bytes", response->GetBlockWiseBlockNumber(),
830                 otCoapBlockSizeFromExponent(response->GetBlockWiseBlockSize()));
831 
832         SuccessOrExit(error = SendMessage(*response, aMessageInfo));
833 
834         error = kErrorBusy;
835     }
836     else
837     {
838         // Conclude block-wise transfer if last block has been received
839         FreeLastBlockResponse();
840         error = kErrorNone;
841     }
842 
843 exit:
844     if (error != kErrorNone && error != kErrorBusy && response != nullptr)
845     {
846         response->Free();
847     }
848 
849     return error;
850 }
851 
ProcessBlock2Request(Message & aMessage,const Ip6::MessageInfo & aMessageInfo,const ResourceBlockWise & aResource)852 Error CoapBase::ProcessBlock2Request(Message                 &aMessage,
853                                      const Ip6::MessageInfo  &aMessageInfo,
854                                      const ResourceBlockWise &aResource)
855 {
856     Error            error                = kErrorNone;
857     Message         *response             = nullptr;
858     uint8_t          buf[kMaxBlockLength] = {0};
859     uint16_t         bufLen               = kMaxBlockLength;
860     bool             moreBlocks           = false;
861     uint64_t         optionBuf            = 0;
862     Option::Iterator iterator;
863 
864     SuccessOrExit(error = aMessage.ReadBlockOptionValues(kOptionBlock2));
865 
866     LogInfo("Request for Block2 Nr. %d, Size: %d bytes received", aMessage.GetBlockWiseBlockNumber(),
867             otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize()));
868 
869     if (aMessage.GetBlockWiseBlockNumber() == 0)
870     {
871         aResource.HandleRequest(aMessage, aMessageInfo);
872         ExitNow();
873     }
874 
875     // Set up next response
876     VerifyOrExit((response = NewMessage()) != nullptr, error = kErrorNoBufs);
877     response->Init(kTypeAck, kCodeContent);
878     response->SetMessageId(aMessage.GetMessageId());
879 
880     SuccessOrExit(error = response->SetTokenFromMessage(aMessage));
881 
882     VerifyOrExit((bufLen = otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize())) <= kMaxBlockLength,
883                  error = kErrorNoBufs);
884     SuccessOrExit(error = aResource.HandleBlockTransmit(buf,
885                                                         otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize()) *
886                                                             aMessage.GetBlockWiseBlockNumber(),
887                                                         &bufLen, &moreBlocks));
888 
889     response->SetMoreBlocksFlag(moreBlocks);
890     if (moreBlocks)
891     {
892         switch (bufLen)
893         {
894         case 1024:
895             response->SetBlockWiseBlockSize(OT_COAP_OPTION_BLOCK_SZX_1024);
896             break;
897         case 512:
898             response->SetBlockWiseBlockSize(OT_COAP_OPTION_BLOCK_SZX_512);
899             break;
900         case 256:
901             response->SetBlockWiseBlockSize(OT_COAP_OPTION_BLOCK_SZX_256);
902             break;
903         case 128:
904             response->SetBlockWiseBlockSize(OT_COAP_OPTION_BLOCK_SZX_128);
905             break;
906         case 64:
907             response->SetBlockWiseBlockSize(OT_COAP_OPTION_BLOCK_SZX_64);
908             break;
909         case 32:
910             response->SetBlockWiseBlockSize(OT_COAP_OPTION_BLOCK_SZX_32);
911             break;
912         case 16:
913             response->SetBlockWiseBlockSize(OT_COAP_OPTION_BLOCK_SZX_16);
914             break;
915         default:
916             error = kErrorInvalidArgs;
917             ExitNow();
918             break;
919         }
920     }
921     else
922     {
923         // Verify that buffer length is not larger than requested block size
924         VerifyOrExit(bufLen <= otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize()),
925                      error = kErrorInvalidArgs);
926         response->SetBlockWiseBlockSize(aMessage.GetBlockWiseBlockSize());
927     }
928 
929     response->SetBlockWiseBlockNumber(
930         (otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize()) * aMessage.GetBlockWiseBlockNumber()) /
931         (otCoapBlockSizeFromExponent(response->GetBlockWiseBlockSize())));
932 
933     // Copy options from last response
934     SuccessOrExit(error = iterator.Init(*mLastResponse));
935 
936     while (!iterator.IsDone())
937     {
938         uint16_t optionNumber = iterator.GetOption()->GetNumber();
939 
940         if (optionNumber == kOptionBlock2)
941         {
942             SuccessOrExit(error = response->AppendBlockOption(Message::kBlockType2, response->GetBlockWiseBlockNumber(),
943                                                               response->IsMoreBlocksFlagSet(),
944                                                               response->GetBlockWiseBlockSize()));
945         }
946         else if (optionNumber == kOptionBlock1)
947         {
948             SuccessOrExit(error = iterator.ReadOptionValue(&optionBuf));
949             SuccessOrExit(error = response->AppendOption(optionNumber, iterator.GetOption()->GetLength(), &optionBuf));
950         }
951 
952         SuccessOrExit(error = iterator.Advance());
953     }
954 
955     SuccessOrExit(error = response->SetPayloadMarker());
956     SuccessOrExit(error = response->AppendBytes(buf, bufLen));
957 
958     if (response->IsMoreBlocksFlagSet())
959     {
960         SuccessOrExit(error = CacheLastBlockResponse(response));
961     }
962     else
963     {
964         // Conclude block-wise transfer if last block has been received
965         FreeLastBlockResponse();
966     }
967 
968     LogInfo("Send Block2 Nr. %d, Size: %d bytes, More Blocks Flag %d", response->GetBlockWiseBlockNumber(),
969             otCoapBlockSizeFromExponent(response->GetBlockWiseBlockSize()), response->IsMoreBlocksFlagSet());
970 
971     SuccessOrExit(error = SendMessage(*response, aMessageInfo));
972 
973 exit:
974     FreeMessageOnError(response, error);
975 
976     return error;
977 }
978 #endif // OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
979 
SendCopy(const Message & aMessage,const Ip6::MessageInfo & aMessageInfo)980 void CoapBase::SendCopy(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
981 {
982     Error    error;
983     Message *messageCopy = nullptr;
984 
985     // Create a message copy for lower layers.
986     messageCopy = aMessage.Clone(aMessage.GetLength() - sizeof(Metadata));
987     VerifyOrExit(messageCopy != nullptr, error = kErrorNoBufs);
988 
989     SuccessOrExit(error = Send(*messageCopy, aMessageInfo));
990 
991 exit:
992 
993     if (error != kErrorNone)
994     {
995         LogWarn("Failed to send copy: %s", ErrorToString(error));
996         FreeMessage(messageCopy);
997     }
998 }
999 
FindRelatedRequest(const Message & aResponse,const Ip6::MessageInfo & aMessageInfo,Metadata & aMetadata)1000 Message *CoapBase::FindRelatedRequest(const Message          &aResponse,
1001                                       const Ip6::MessageInfo &aMessageInfo,
1002                                       Metadata               &aMetadata)
1003 {
1004     Message *request = nullptr;
1005 
1006     for (Message &message : mPendingRequests)
1007     {
1008         aMetadata.ReadFrom(message);
1009 
1010         if (((aMetadata.mDestinationAddress == aMessageInfo.GetPeerAddr()) ||
1011              aMetadata.mDestinationAddress.IsMulticast() ||
1012              aMetadata.mDestinationAddress.GetIid().IsAnycastLocator()) &&
1013             (aMetadata.mDestinationPort == aMessageInfo.GetPeerPort()))
1014         {
1015             switch (aResponse.GetType())
1016             {
1017             case kTypeReset:
1018             case kTypeAck:
1019                 if (aResponse.GetMessageId() == message.GetMessageId())
1020                 {
1021                     request = &message;
1022                     ExitNow();
1023                 }
1024 
1025                 break;
1026 
1027             case kTypeConfirmable:
1028             case kTypeNonConfirmable:
1029                 if (aResponse.IsTokenEqual(message))
1030                 {
1031                     request = &message;
1032                     ExitNow();
1033                 }
1034 
1035                 break;
1036             }
1037         }
1038     }
1039 
1040 exit:
1041     return request;
1042 }
1043 
Receive(ot::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)1044 void CoapBase::Receive(ot::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
1045 {
1046     Message &message = AsCoapMessage(&aMessage);
1047 
1048     if (message.ParseHeader() != kErrorNone)
1049     {
1050         LogDebg("Failed to parse CoAP header");
1051 
1052         if (!aMessageInfo.GetSockAddr().IsMulticast() && message.IsConfirmable())
1053         {
1054             IgnoreError(SendReset(message, aMessageInfo));
1055         }
1056     }
1057     else if (message.IsRequest())
1058     {
1059         ProcessReceivedRequest(message, aMessageInfo);
1060     }
1061     else
1062     {
1063         ProcessReceivedResponse(message, aMessageInfo);
1064     }
1065 
1066 #if OPENTHREAD_CONFIG_OTNS_ENABLE
1067     Get<Utils::Otns>().EmitCoapReceive(message, aMessageInfo);
1068 #endif
1069 }
1070 
ProcessReceivedResponse(Message & aMessage,const Ip6::MessageInfo & aMessageInfo)1071 void CoapBase::ProcessReceivedResponse(Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
1072 {
1073     Metadata metadata;
1074     Message *request = nullptr;
1075     Error    error   = kErrorNone;
1076 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
1077     bool responseObserve = false;
1078 #endif
1079 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
1080     uint8_t  blockOptionType   = 0;
1081     uint32_t totalTransferSize = 0;
1082 #endif
1083 
1084     request = FindRelatedRequest(aMessage, aMessageInfo, metadata);
1085     VerifyOrExit(request != nullptr);
1086 
1087 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
1088     if (metadata.mObserve && request->IsRequest())
1089     {
1090         // We sent Observe in our request, see if we received Observe in the response too.
1091         Option::Iterator iterator;
1092 
1093         SuccessOrExit(error = iterator.Init(aMessage, kOptionObserve));
1094         responseObserve = !iterator.IsDone();
1095     }
1096 #endif
1097 
1098     switch (aMessage.GetType())
1099     {
1100     case kTypeReset:
1101         if (aMessage.IsEmpty())
1102         {
1103             FinalizeCoapTransaction(*request, metadata, nullptr, nullptr, kErrorAbort);
1104         }
1105 
1106         // Silently ignore non-empty reset messages (RFC 7252, p. 4.2).
1107         break;
1108 
1109     case kTypeAck:
1110         if (aMessage.IsEmpty())
1111         {
1112             // Empty acknowledgment.
1113 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
1114             if (metadata.mObserve && !request->IsRequest())
1115             {
1116                 // This is the ACK to our RFC7641 notification.  There will be no
1117                 // "separate" response so pass it back as if it were a piggy-backed
1118                 // response so we can stop re-sending and the application can move on.
1119                 FinalizeCoapTransaction(*request, metadata, &aMessage, &aMessageInfo, kErrorNone);
1120             }
1121             else
1122 #endif
1123             {
1124                 // This is not related to RFC7641 or the outgoing "request" was not a
1125                 // notification.
1126                 if (metadata.mConfirmable)
1127                 {
1128                     metadata.mAcknowledged = true;
1129                     metadata.UpdateIn(*request);
1130                 }
1131 
1132                 // Remove the message if response is not expected, otherwise await
1133                 // response.
1134                 if (metadata.mResponseHandler == nullptr)
1135                 {
1136                     DequeueMessage(*request);
1137                 }
1138             }
1139         }
1140         else if (aMessage.IsResponse() && aMessage.IsTokenEqual(*request))
1141         {
1142             // Piggybacked response.  If there's an Observe option present in both
1143             // request and response, and we have a response handler; then we're
1144             // dealing with RFC7641 rules here.
1145             // (If there is no response handler, then we're wasting our time!)
1146 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
1147             if (metadata.mObserve && responseObserve && (metadata.mResponseHandler != nullptr))
1148             {
1149                 // This is a RFC7641 notification.  The request is *not* done!
1150                 metadata.mResponseHandler(metadata.mResponseContext, &aMessage, &aMessageInfo, kErrorNone);
1151 
1152                 // Consider the message acknowledged at this point.
1153                 metadata.mAcknowledged = true;
1154                 metadata.UpdateIn(*request);
1155             }
1156             else
1157 #endif
1158 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
1159             {
1160                 if (metadata.mBlockwiseTransmitHook != nullptr || metadata.mBlockwiseReceiveHook != nullptr)
1161                 {
1162                     // Search for CoAP Block-Wise Option [RFC7959]
1163                     Option::Iterator iterator;
1164 
1165                     SuccessOrExit(error = iterator.Init(aMessage));
1166                     while (!iterator.IsDone())
1167                     {
1168                         switch (iterator.GetOption()->GetNumber())
1169                         {
1170                         case kOptionBlock1:
1171                             blockOptionType += 1;
1172                             break;
1173 
1174                         case kOptionBlock2:
1175                             blockOptionType += 2;
1176                             break;
1177 
1178                         case kOptionSize2:
1179                             // ToDo: wait for method to read uint option values
1180                             totalTransferSize = 0;
1181                             break;
1182 
1183                         default:
1184                             break;
1185                         }
1186 
1187                         SuccessOrExit(error = iterator.Advance());
1188                     }
1189                 }
1190                 switch (blockOptionType)
1191                 {
1192                 case 0:
1193                     // Piggybacked response.
1194                     FinalizeCoapTransaction(*request, metadata, &aMessage, &aMessageInfo, kErrorNone);
1195                     break;
1196                 case 1: // Block1 option
1197                     if (aMessage.GetCode() == kCodeContinue && metadata.mBlockwiseTransmitHook != nullptr)
1198                     {
1199                         error = SendNextBlock1Request(*request, aMessage, aMessageInfo, metadata);
1200                     }
1201 
1202                     if (aMessage.GetCode() != kCodeContinue || metadata.mBlockwiseTransmitHook == nullptr ||
1203                         error != kErrorNone)
1204                     {
1205                         FinalizeCoapTransaction(*request, metadata, &aMessage, &aMessageInfo, error);
1206                     }
1207                     break;
1208                 case 2: // Block2 option
1209                     if (aMessage.GetCode() < kCodeBadRequest && metadata.mBlockwiseReceiveHook != nullptr)
1210                     {
1211                         error =
1212                             SendNextBlock2Request(*request, aMessage, aMessageInfo, metadata, totalTransferSize, false);
1213                     }
1214 
1215                     if (aMessage.GetCode() >= kCodeBadRequest || metadata.mBlockwiseReceiveHook == nullptr ||
1216                         error != kErrorNone)
1217                     {
1218                         FinalizeCoapTransaction(*request, metadata, &aMessage, &aMessageInfo, error);
1219                     }
1220                     break;
1221                 case 3: // Block1 & Block2 option
1222                     if (aMessage.GetCode() < kCodeBadRequest && metadata.mBlockwiseReceiveHook != nullptr)
1223                     {
1224                         error =
1225                             SendNextBlock2Request(*request, aMessage, aMessageInfo, metadata, totalTransferSize, true);
1226                     }
1227 
1228                     FinalizeCoapTransaction(*request, metadata, &aMessage, &aMessageInfo, error);
1229                     break;
1230                 default:
1231                     error = kErrorAbort;
1232                     FinalizeCoapTransaction(*request, metadata, &aMessage, &aMessageInfo, error);
1233                     break;
1234                 }
1235             }
1236 #else  // OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
1237             {
1238                                   FinalizeCoapTransaction(*request, metadata, &aMessage, &aMessageInfo, kErrorNone);
1239             }
1240 #endif // OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
1241         }
1242 
1243         // Silently ignore acknowledgments carrying requests (RFC 7252, p. 4.2)
1244         // or with no token match (RFC 7252, p. 5.3.2)
1245         break;
1246 
1247     case kTypeConfirmable:
1248         // Send empty ACK if it is a CON message.
1249         IgnoreError(SendAck(aMessage, aMessageInfo));
1250 
1251         OT_FALL_THROUGH;
1252         // Handling of RFC7641 and multicast is below.
1253     case kTypeNonConfirmable:
1254         // Separate response or observation notification.  If the request was to a multicast
1255         // address, OR both the request and response carry Observe options, then this is NOT
1256         // the final message, we may see multiples.
1257         if ((metadata.mResponseHandler != nullptr) && (metadata.mDestinationAddress.IsMulticast()
1258 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
1259                                                        || (metadata.mObserve && responseObserve)
1260 #endif
1261                                                            ))
1262         {
1263             // If multicast non-confirmable request, allow multiple responses
1264             metadata.mResponseHandler(metadata.mResponseContext, &aMessage, &aMessageInfo, kErrorNone);
1265         }
1266         else
1267         {
1268             FinalizeCoapTransaction(*request, metadata, &aMessage, &aMessageInfo, kErrorNone);
1269         }
1270 
1271         break;
1272     }
1273 
1274 exit:
1275 
1276     if (error == kErrorNone && request == nullptr)
1277     {
1278         if (aMessage.IsConfirmable() || aMessage.IsNonConfirmable())
1279         {
1280             // Successfully parsed a header but no matching request was
1281             // found - reject the message by sending reset.
1282             IgnoreError(SendReset(aMessage, aMessageInfo));
1283         }
1284     }
1285 }
1286 
ProcessReceivedRequest(Message & aMessage,const Ip6::MessageInfo & aMessageInfo)1287 void CoapBase::ProcessReceivedRequest(Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
1288 {
1289     char     uriPath[Message::kMaxReceivedUriPath + 1];
1290     Message *cachedResponse = nullptr;
1291     Error    error          = kErrorNone;
1292 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
1293     Option::Iterator iterator;
1294     char            *curUriPath        = uriPath;
1295     uint8_t          blockOptionType   = 0;
1296     uint32_t         totalTransferSize = 0;
1297 #endif
1298 
1299     if (mInterceptor.IsSet())
1300     {
1301         SuccessOrExit(error = mInterceptor.Invoke(aMessage, aMessageInfo));
1302     }
1303 
1304     switch (mResponsesQueue.GetMatchedResponseCopy(aMessage, aMessageInfo, &cachedResponse))
1305     {
1306     case kErrorNone:
1307         cachedResponse->Finish();
1308         error = Send(*cachedResponse, aMessageInfo);
1309         ExitNow();
1310 
1311     case kErrorNoBufs:
1312         error = kErrorNoBufs;
1313         ExitNow();
1314 
1315     case kErrorNotFound:
1316     default:
1317         break;
1318     }
1319 
1320 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
1321     SuccessOrExit(error = iterator.Init(aMessage));
1322 
1323     while (!iterator.IsDone())
1324     {
1325         switch (iterator.GetOption()->GetNumber())
1326         {
1327         case kOptionUriPath:
1328             if (curUriPath != uriPath)
1329             {
1330                 *curUriPath++ = '/';
1331             }
1332 
1333             VerifyOrExit(curUriPath + iterator.GetOption()->GetLength() < GetArrayEnd(uriPath), error = kErrorParse);
1334 
1335             IgnoreError(iterator.ReadOptionValue(curUriPath));
1336             curUriPath += iterator.GetOption()->GetLength();
1337             break;
1338 
1339         case kOptionBlock1:
1340             blockOptionType += 1;
1341             break;
1342 
1343         case kOptionBlock2:
1344             blockOptionType += 2;
1345             break;
1346 
1347         case kOptionSize1:
1348             // ToDo: wait for method to read uint option values
1349             totalTransferSize = 0;
1350             break;
1351 
1352         default:
1353             break;
1354         }
1355 
1356         SuccessOrExit(error = iterator.Advance());
1357     }
1358 
1359     curUriPath[0] = '\0';
1360 
1361     for (const ResourceBlockWise &resource : mBlockWiseResources)
1362     {
1363         if (strcmp(resource.GetUriPath(), uriPath) != 0)
1364         {
1365             continue;
1366         }
1367 
1368         if ((resource.mReceiveHook != nullptr || resource.mTransmitHook != nullptr) && blockOptionType != 0)
1369         {
1370             switch (blockOptionType)
1371             {
1372             case 1:
1373                 if (resource.mReceiveHook != nullptr)
1374                 {
1375                     switch (ProcessBlock1Request(aMessage, aMessageInfo, resource, totalTransferSize))
1376                     {
1377                     case kErrorNone:
1378                         resource.HandleRequest(aMessage, aMessageInfo);
1379                         // Fall through
1380                     case kErrorBusy:
1381                         error = kErrorNone;
1382                         break;
1383                     case kErrorNoBufs:
1384                         IgnoreReturnValue(SendHeaderResponse(kCodeRequestTooLarge, aMessage, aMessageInfo));
1385                         error = kErrorDrop;
1386                         break;
1387                     case kErrorNoFrameReceived:
1388                         IgnoreReturnValue(SendHeaderResponse(kCodeRequestIncomplete, aMessage, aMessageInfo));
1389                         error = kErrorDrop;
1390                         break;
1391                     default:
1392                         IgnoreReturnValue(SendHeaderResponse(kCodeInternalError, aMessage, aMessageInfo));
1393                         error = kErrorDrop;
1394                         break;
1395                     }
1396                 }
1397                 break;
1398             case 2:
1399                 if (resource.mTransmitHook != nullptr)
1400                 {
1401                     if ((error = ProcessBlock2Request(aMessage, aMessageInfo, resource)) != kErrorNone)
1402                     {
1403                         IgnoreReturnValue(SendHeaderResponse(kCodeInternalError, aMessage, aMessageInfo));
1404                         error = kErrorDrop;
1405                     }
1406                 }
1407                 break;
1408             }
1409             ExitNow();
1410         }
1411         else
1412         {
1413             resource.HandleRequest(aMessage, aMessageInfo);
1414             error = kErrorNone;
1415             ExitNow();
1416         }
1417     }
1418 #else
1419     SuccessOrExit(error = aMessage.ReadUriPathOptions(uriPath));
1420 #endif // OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
1421 
1422     if ((mResourceHandler != nullptr) && mResourceHandler(*this, uriPath, aMessage, aMessageInfo))
1423     {
1424         error = kErrorNone;
1425         ExitNow();
1426     }
1427 
1428     for (const Resource &resource : mResources)
1429     {
1430         if (strcmp(resource.mUriPath, uriPath) == 0)
1431         {
1432             resource.HandleRequest(aMessage, aMessageInfo);
1433             error = kErrorNone;
1434             ExitNow();
1435         }
1436     }
1437 
1438     if (mDefaultHandler.IsSet())
1439     {
1440         mDefaultHandler.Invoke(&aMessage, &aMessageInfo);
1441         error = kErrorNone;
1442         ExitNow();
1443     }
1444 
1445     error = kErrorNotFound;
1446 
1447 exit:
1448 
1449     if (error != kErrorNone)
1450     {
1451         LogInfo("Failed to process request: %s", ErrorToString(error));
1452 
1453         if (error == kErrorNotFound && !aMessageInfo.GetSockAddr().IsMulticast())
1454         {
1455             IgnoreError(SendNotFound(aMessage, aMessageInfo));
1456         }
1457 
1458         FreeMessage(cachedResponse);
1459     }
1460 }
1461 
ReadFrom(const Message & aMessage)1462 void CoapBase::Metadata::ReadFrom(const Message &aMessage)
1463 {
1464     uint16_t length = aMessage.GetLength();
1465 
1466     OT_ASSERT(length >= sizeof(*this));
1467     IgnoreError(aMessage.Read(length - sizeof(*this), *this));
1468 }
1469 
UpdateIn(Message & aMessage) const1470 void CoapBase::Metadata::UpdateIn(Message &aMessage) const
1471 {
1472     aMessage.Write(aMessage.GetLength() - sizeof(*this), *this);
1473 }
1474 
ResponsesQueue(Instance & aInstance)1475 ResponsesQueue::ResponsesQueue(Instance &aInstance)
1476     : mTimer(aInstance, ResponsesQueue::HandleTimer, this)
1477 {
1478 }
1479 
GetMatchedResponseCopy(const Message & aRequest,const Ip6::MessageInfo & aMessageInfo,Message ** aResponse)1480 Error ResponsesQueue::GetMatchedResponseCopy(const Message          &aRequest,
1481                                              const Ip6::MessageInfo &aMessageInfo,
1482                                              Message               **aResponse)
1483 {
1484     Error          error = kErrorNone;
1485     const Message *cacheResponse;
1486 
1487     cacheResponse = FindMatchedResponse(aRequest, aMessageInfo);
1488     VerifyOrExit(cacheResponse != nullptr, error = kErrorNotFound);
1489 
1490     *aResponse = cacheResponse->Clone(cacheResponse->GetLength() - sizeof(ResponseMetadata));
1491     VerifyOrExit(*aResponse != nullptr, error = kErrorNoBufs);
1492 
1493 exit:
1494     return error;
1495 }
1496 
FindMatchedResponse(const Message & aRequest,const Ip6::MessageInfo & aMessageInfo) const1497 const Message *ResponsesQueue::FindMatchedResponse(const Message &aRequest, const Ip6::MessageInfo &aMessageInfo) const
1498 {
1499     const Message *response = nullptr;
1500 
1501     for (const Message &message : mQueue)
1502     {
1503         if (message.GetMessageId() == aRequest.GetMessageId())
1504         {
1505             ResponseMetadata metadata;
1506 
1507             metadata.ReadFrom(message);
1508 
1509             if ((metadata.mMessageInfo.GetPeerPort() == aMessageInfo.GetPeerPort()) &&
1510                 (metadata.mMessageInfo.GetPeerAddr() == aMessageInfo.GetPeerAddr()))
1511             {
1512                 response = &message;
1513                 break;
1514             }
1515         }
1516     }
1517 
1518     return response;
1519 }
1520 
EnqueueResponse(Message & aMessage,const Ip6::MessageInfo & aMessageInfo,const TxParameters & aTxParameters)1521 void ResponsesQueue::EnqueueResponse(Message                &aMessage,
1522                                      const Ip6::MessageInfo &aMessageInfo,
1523                                      const TxParameters     &aTxParameters)
1524 {
1525     Message         *responseCopy;
1526     ResponseMetadata metadata;
1527 
1528     metadata.mDequeueTime = TimerMilli::GetNow() + aTxParameters.CalculateExchangeLifetime();
1529     metadata.mMessageInfo = aMessageInfo;
1530 
1531     VerifyOrExit(FindMatchedResponse(aMessage, aMessageInfo) == nullptr);
1532 
1533     UpdateQueue();
1534 
1535     VerifyOrExit((responseCopy = aMessage.Clone()) != nullptr);
1536 
1537     VerifyOrExit(metadata.AppendTo(*responseCopy) == kErrorNone, responseCopy->Free());
1538 
1539     mQueue.Enqueue(*responseCopy);
1540 
1541     mTimer.FireAtIfEarlier(metadata.mDequeueTime);
1542 
1543 exit:
1544     return;
1545 }
1546 
UpdateQueue(void)1547 void ResponsesQueue::UpdateQueue(void)
1548 {
1549     uint16_t  msgCount    = 0;
1550     Message  *earliestMsg = nullptr;
1551     TimeMilli earliestDequeueTime(0);
1552 
1553     // Check the number of messages in the queue and if number is at
1554     // `kMaxCachedResponses` remove the one with earliest dequeue
1555     // time.
1556 
1557     for (Message &message : mQueue)
1558     {
1559         ResponseMetadata metadata;
1560 
1561         metadata.ReadFrom(message);
1562 
1563         if ((earliestMsg == nullptr) || (metadata.mDequeueTime < earliestDequeueTime))
1564         {
1565             earliestMsg         = &message;
1566             earliestDequeueTime = metadata.mDequeueTime;
1567         }
1568 
1569         msgCount++;
1570     }
1571 
1572     if (msgCount >= kMaxCachedResponses)
1573     {
1574         DequeueResponse(*earliestMsg);
1575     }
1576 }
1577 
DequeueResponse(Message & aMessage)1578 void ResponsesQueue::DequeueResponse(Message &aMessage) { mQueue.DequeueAndFree(aMessage); }
1579 
DequeueAllResponses(void)1580 void ResponsesQueue::DequeueAllResponses(void) { mQueue.DequeueAndFreeAll(); }
1581 
HandleTimer(Timer & aTimer)1582 void ResponsesQueue::HandleTimer(Timer &aTimer)
1583 {
1584     static_cast<ResponsesQueue *>(static_cast<TimerMilliContext &>(aTimer).GetContext())->HandleTimer();
1585 }
1586 
HandleTimer(void)1587 void ResponsesQueue::HandleTimer(void)
1588 {
1589     NextFireTime nextDequeueTime;
1590 
1591     for (Message &message : mQueue)
1592     {
1593         ResponseMetadata metadata;
1594 
1595         metadata.ReadFrom(message);
1596 
1597         if (nextDequeueTime.GetNow() >= metadata.mDequeueTime)
1598         {
1599             DequeueResponse(message);
1600             continue;
1601         }
1602 
1603         nextDequeueTime.UpdateIfEarlier(metadata.mDequeueTime);
1604     }
1605 
1606     mTimer.FireAt(nextDequeueTime);
1607 }
1608 
ReadFrom(const Message & aMessage)1609 void ResponsesQueue::ResponseMetadata::ReadFrom(const Message &aMessage)
1610 {
1611     uint16_t length = aMessage.GetLength();
1612 
1613     OT_ASSERT(length >= sizeof(*this));
1614     IgnoreError(aMessage.Read(length - sizeof(*this), *this));
1615 }
1616 
1617 /// Return product of @p aValueA and @p aValueB if no overflow otherwise 0.
Multiply(uint32_t aValueA,uint32_t aValueB)1618 static uint32_t Multiply(uint32_t aValueA, uint32_t aValueB)
1619 {
1620     uint32_t result = 0;
1621 
1622     VerifyOrExit(aValueA);
1623 
1624     result = aValueA * aValueB;
1625     result = (result / aValueA == aValueB) ? result : 0;
1626 
1627 exit:
1628     return result;
1629 }
1630 
IsValid(void) const1631 bool TxParameters::IsValid(void) const
1632 {
1633     bool rval = false;
1634 
1635     if ((mAckRandomFactorDenominator > 0) && (mAckRandomFactorNumerator >= mAckRandomFactorDenominator) &&
1636         (mAckTimeout >= OT_COAP_MIN_ACK_TIMEOUT) && (mMaxRetransmit <= OT_COAP_MAX_RETRANSMIT))
1637     {
1638         // Calculate exchange lifetime step by step and verify no overflow.
1639         uint32_t tmp = Multiply(mAckTimeout, (1U << (mMaxRetransmit + 1)) - 1);
1640 
1641         tmp = Multiply(tmp, mAckRandomFactorNumerator);
1642         tmp /= mAckRandomFactorDenominator;
1643 
1644         rval = (tmp != 0 && (tmp + mAckTimeout + 2 * kDefaultMaxLatency) > tmp);
1645     }
1646 
1647     return rval;
1648 }
1649 
CalculateInitialRetransmissionTimeout(void) const1650 uint32_t TxParameters::CalculateInitialRetransmissionTimeout(void) const
1651 {
1652     return Random::NonCrypto::GetUint32InRange(
1653         mAckTimeout, mAckTimeout * mAckRandomFactorNumerator / mAckRandomFactorDenominator + 1);
1654 }
1655 
CalculateExchangeLifetime(void) const1656 uint32_t TxParameters::CalculateExchangeLifetime(void) const
1657 {
1658     // Final `mAckTimeout` is to account for processing delay.
1659     return CalculateSpan(mMaxRetransmit) + 2 * kDefaultMaxLatency + mAckTimeout;
1660 }
1661 
CalculateMaxTransmitWait(void) const1662 uint32_t TxParameters::CalculateMaxTransmitWait(void) const { return CalculateSpan(mMaxRetransmit + 1); }
1663 
CalculateSpan(uint8_t aMaxRetx) const1664 uint32_t TxParameters::CalculateSpan(uint8_t aMaxRetx) const
1665 {
1666     return static_cast<uint32_t>(mAckTimeout * ((1U << aMaxRetx) - 1) / mAckRandomFactorDenominator *
1667                                  mAckRandomFactorNumerator);
1668 }
1669 
1670 const otCoapTxParameters TxParameters::kDefaultTxParameters = {
1671     kDefaultAckTimeout,
1672     kDefaultAckRandomFactorNumerator,
1673     kDefaultAckRandomFactorDenominator,
1674     kDefaultMaxRetransmit,
1675 };
1676 
1677 //----------------------------------------------------------------------------------------------------------------------
1678 
Resource(const char * aUriPath,RequestHandler aHandler,void * aContext)1679 Resource::Resource(const char *aUriPath, RequestHandler aHandler, void *aContext)
1680 {
1681     mUriPath = aUriPath;
1682     mHandler = aHandler;
1683     mContext = aContext;
1684     mNext    = nullptr;
1685 }
1686 
Resource(Uri aUri,RequestHandler aHandler,void * aContext)1687 Resource::Resource(Uri aUri, RequestHandler aHandler, void *aContext)
1688     : Resource(PathForUri(aUri), aHandler, aContext)
1689 {
1690 }
1691 
1692 //----------------------------------------------------------------------------------------------------------------------
1693 
Coap(Instance & aInstance)1694 Coap::Coap(Instance &aInstance)
1695     : CoapBase(aInstance, &Coap::Send)
1696     , mSocket(aInstance)
1697 {
1698 }
1699 
Start(uint16_t aPort,Ip6::NetifIdentifier aNetifIdentifier)1700 Error Coap::Start(uint16_t aPort, Ip6::NetifIdentifier aNetifIdentifier)
1701 {
1702     Error error        = kErrorNone;
1703     bool  socketOpened = false;
1704 
1705     VerifyOrExit(!mSocket.IsBound());
1706 
1707     SuccessOrExit(error = mSocket.Open(&Coap::HandleUdpReceive, this));
1708     socketOpened = true;
1709 
1710     SuccessOrExit(error = mSocket.Bind(aPort, aNetifIdentifier));
1711 
1712 exit:
1713     if (error != kErrorNone && socketOpened)
1714     {
1715         IgnoreError(mSocket.Close());
1716     }
1717 
1718     return error;
1719 }
1720 
Stop(void)1721 Error Coap::Stop(void)
1722 {
1723     Error error = kErrorNone;
1724 
1725     VerifyOrExit(mSocket.IsBound());
1726 
1727     SuccessOrExit(error = mSocket.Close());
1728     ClearRequestsAndResponses();
1729 
1730 exit:
1731     return error;
1732 }
1733 
HandleUdpReceive(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo)1734 void Coap::HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
1735 {
1736     static_cast<Coap *>(aContext)->Receive(AsCoapMessage(aMessage), AsCoreType(aMessageInfo));
1737 }
1738 
Send(CoapBase & aCoapBase,ot::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)1739 Error Coap::Send(CoapBase &aCoapBase, ot::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
1740 {
1741     return static_cast<Coap &>(aCoapBase).Send(aMessage, aMessageInfo);
1742 }
1743 
Send(ot::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)1744 Error Coap::Send(ot::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
1745 {
1746     return mSocket.IsBound() ? mSocket.SendTo(aMessage, aMessageInfo) : kErrorInvalidState;
1747 }
1748 
1749 } // namespace Coap
1750 } // namespace ot
1751