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