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