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