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