• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright (c) 2017, 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 /**
30  * @file
31  *   This file implements a simple CLI for the CoAP service.
32  */
33 
34 #include "cli_coap.hpp"
35 
36 #if OPENTHREAD_CONFIG_COAP_API_ENABLE
37 
38 #include <openthread/random_noncrypto.h>
39 
40 #include <ctype.h>
41 
42 #include "cli/cli.hpp"
43 
44 namespace ot {
45 namespace Cli {
46 
Coap(Output & aOutput)47 Coap::Coap(Output &aOutput)
48     : OutputWrapper(aOutput)
49     , mUseDefaultRequestTxParameters(true)
50     , mUseDefaultResponseTxParameters(true)
51 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
52     , mObserveSerial(0)
53     , mRequestTokenLength(0)
54     , mSubscriberTokenLength(0)
55     , mSubscriberConfirmableNotifications(false)
56 #endif
57 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
58     , mBlockCount(1)
59 #endif
60 {
61     memset(&mResource, 0, sizeof(mResource));
62 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
63     memset(&mRequestAddr, 0, sizeof(mRequestAddr));
64     memset(&mSubscriberSock, 0, sizeof(mSubscriberSock));
65     memset(&mRequestToken, 0, sizeof(mRequestToken));
66     memset(&mSubscriberToken, 0, sizeof(mSubscriberToken));
67     memset(&mRequestUri, 0, sizeof(mRequestUri));
68 #endif
69     memset(&mUriPath, 0, sizeof(mUriPath));
70     strncpy(mResourceContent, "0", sizeof(mResourceContent));
71     mResourceContent[sizeof(mResourceContent) - 1] = '\0';
72 }
73 
74 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
CancelResourceSubscription(void)75 otError Coap::CancelResourceSubscription(void)
76 {
77     otError       error   = OT_ERROR_NONE;
78     otMessage *   message = nullptr;
79     otMessageInfo messageInfo;
80 
81     memset(&messageInfo, 0, sizeof(messageInfo));
82     messageInfo.mPeerAddr = mRequestAddr;
83     messageInfo.mPeerPort = OT_DEFAULT_COAP_PORT;
84 
85     VerifyOrExit(mRequestTokenLength != 0, error = OT_ERROR_INVALID_STATE);
86 
87     message = otCoapNewMessage(GetInstancePtr(), nullptr);
88     VerifyOrExit(message != nullptr, error = OT_ERROR_NO_BUFS);
89 
90     otCoapMessageInit(message, OT_COAP_TYPE_CONFIRMABLE, OT_COAP_CODE_GET);
91 
92     SuccessOrExit(error = otCoapMessageSetToken(message, mRequestToken, mRequestTokenLength));
93     SuccessOrExit(error = otCoapMessageAppendObserveOption(message, 1));
94     SuccessOrExit(error = otCoapMessageAppendUriPathOptions(message, mRequestUri));
95     SuccessOrExit(error = otCoapSendRequest(GetInstancePtr(), message, &messageInfo, &Coap::HandleResponse, this));
96 
97     memset(&mRequestAddr, 0, sizeof(mRequestAddr));
98     memset(&mRequestUri, 0, sizeof(mRequestUri));
99     mRequestTokenLength = 0;
100 
101 exit:
102 
103     if ((error != OT_ERROR_NONE) && (message != nullptr))
104     {
105         otMessageFree(message);
106     }
107 
108     return error;
109 }
110 
CancelSubscriber(void)111 void Coap::CancelSubscriber(void)
112 {
113     memset(&mSubscriberSock, 0, sizeof(mSubscriberSock));
114     mSubscriberTokenLength = 0;
115 }
116 #endif // OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
117 
PrintPayload(otMessage * aMessage)118 void Coap::PrintPayload(otMessage *aMessage)
119 {
120     uint8_t  buf[kMaxBufferSize];
121     uint16_t bytesToPrint;
122     uint16_t bytesPrinted = 0;
123     uint16_t length       = otMessageGetLength(aMessage) - otMessageGetOffset(aMessage);
124 
125     if (length > 0)
126     {
127         OutputFormat(" with payload: ");
128 
129         while (length > 0)
130         {
131             bytesToPrint = (length < sizeof(buf)) ? length : sizeof(buf);
132             otMessageRead(aMessage, otMessageGetOffset(aMessage) + bytesPrinted, buf, bytesToPrint);
133 
134             OutputBytes(buf, static_cast<uint8_t>(bytesToPrint));
135 
136             length -= bytesToPrint;
137             bytesPrinted += bytesToPrint;
138         }
139     }
140 
141     OutputLine("");
142 }
143 
144 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
Process(Arg aArgs[])145 template <> otError Coap::Process<Cmd("cancel")>(Arg aArgs[])
146 {
147     OT_UNUSED_VARIABLE(aArgs);
148 
149     return CancelResourceSubscription();
150 }
151 #endif
152 
Process(Arg aArgs[])153 template <> otError Coap::Process<Cmd("resource")>(Arg aArgs[])
154 {
155     otError error = OT_ERROR_NONE;
156 
157     if (!aArgs[0].IsEmpty())
158     {
159         VerifyOrExit(aArgs[0].GetLength() < kMaxUriLength, error = OT_ERROR_INVALID_ARGS);
160 
161         mResource.mUriPath = mUriPath;
162         mResource.mContext = this;
163         mResource.mHandler = &Coap::HandleRequest;
164 
165 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
166         mResource.mReceiveHook  = &Coap::BlockwiseReceiveHook;
167         mResource.mTransmitHook = &Coap::BlockwiseTransmitHook;
168 
169         if (!aArgs[1].IsEmpty())
170         {
171             SuccessOrExit(error = aArgs[1].ParseAsUint32(mBlockCount));
172         }
173 #endif
174 
175         strncpy(mUriPath, aArgs[0].GetCString(), sizeof(mUriPath) - 1);
176 
177 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
178         otCoapAddBlockWiseResource(GetInstancePtr(), &mResource);
179 #else
180         otCoapAddResource(GetInstancePtr(), &mResource);
181 #endif
182     }
183     else
184     {
185         OutputLine("%s", mResource.mUriPath != nullptr ? mResource.mUriPath : "");
186     }
187 
188 exit:
189     return error;
190 }
191 
Process(Arg aArgs[])192 template <> otError Coap::Process<Cmd("set")>(Arg aArgs[])
193 {
194 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
195     otMessage *   notificationMessage = nullptr;
196     otMessageInfo messageInfo;
197 #endif
198     otError error = OT_ERROR_NONE;
199 
200     if (!aArgs[0].IsEmpty())
201     {
202         VerifyOrExit(aArgs[0].GetLength() < sizeof(mResourceContent), error = OT_ERROR_INVALID_ARGS);
203         strncpy(mResourceContent, aArgs[0].GetCString(), sizeof(mResourceContent));
204         mResourceContent[sizeof(mResourceContent) - 1] = '\0';
205 
206 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
207         if (mSubscriberTokenLength > 0)
208         {
209             // Notify the subscriber
210             memset(&messageInfo, 0, sizeof(messageInfo));
211             messageInfo.mPeerAddr = mSubscriberSock.mAddress;
212             messageInfo.mPeerPort = mSubscriberSock.mPort;
213 
214             OutputFormat("sending coap notification to ");
215             OutputIp6AddressLine(mSubscriberSock.mAddress);
216 
217             notificationMessage = otCoapNewMessage(GetInstancePtr(), nullptr);
218             VerifyOrExit(notificationMessage != nullptr, error = OT_ERROR_NO_BUFS);
219 
220             otCoapMessageInit(
221                 notificationMessage,
222                 ((mSubscriberConfirmableNotifications) ? OT_COAP_TYPE_CONFIRMABLE : OT_COAP_TYPE_NON_CONFIRMABLE),
223                 OT_COAP_CODE_CONTENT);
224 
225             SuccessOrExit(error = otCoapMessageSetToken(notificationMessage, mSubscriberToken, mSubscriberTokenLength));
226             SuccessOrExit(error = otCoapMessageAppendObserveOption(notificationMessage, mObserveSerial++));
227             SuccessOrExit(error = otCoapMessageSetPayloadMarker(notificationMessage));
228             SuccessOrExit(error = otMessageAppend(notificationMessage, mResourceContent,
229                                                   static_cast<uint16_t>(strlen(mResourceContent))));
230 
231             SuccessOrExit(error = otCoapSendRequest(GetInstancePtr(), notificationMessage, &messageInfo,
232                                                     &Coap::HandleNotificationResponse, this));
233         }
234 #endif // OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
235     }
236     else
237     {
238         OutputLine("%s", mResourceContent);
239     }
240 
241 exit:
242 
243 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
244     if ((error != OT_ERROR_NONE) && (notificationMessage != nullptr))
245     {
246         otMessageFree(notificationMessage);
247     }
248 #endif
249 
250     return error;
251 }
252 
Process(Arg aArgs[])253 template <> otError Coap::Process<Cmd("start")>(Arg aArgs[])
254 {
255     OT_UNUSED_VARIABLE(aArgs);
256 
257     return otCoapStart(GetInstancePtr(), OT_DEFAULT_COAP_PORT);
258 }
259 
Process(Arg aArgs[])260 template <> otError Coap::Process<Cmd("stop")>(Arg aArgs[])
261 {
262     OT_UNUSED_VARIABLE(aArgs);
263 
264 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
265     otCoapRemoveBlockWiseResource(GetInstancePtr(), &mResource);
266 #else
267     otCoapRemoveResource(GetInstancePtr(), &mResource);
268 #endif
269 
270     return otCoapStop(GetInstancePtr());
271 }
272 
Process(Arg aArgs[])273 template <> otError Coap::Process<Cmd("parameters")>(Arg aArgs[])
274 {
275     otError             error = OT_ERROR_NONE;
276     bool *              defaultTxParameters;
277     otCoapTxParameters *txParameters;
278 
279     if (aArgs[0] == "request")
280     {
281         txParameters        = &mRequestTxParameters;
282         defaultTxParameters = &mUseDefaultRequestTxParameters;
283     }
284     else if (aArgs[0] == "response")
285     {
286         txParameters        = &mResponseTxParameters;
287         defaultTxParameters = &mUseDefaultResponseTxParameters;
288     }
289     else
290     {
291         ExitNow(error = OT_ERROR_INVALID_ARGS);
292     }
293 
294     if (!aArgs[1].IsEmpty())
295     {
296         if (aArgs[1] == "default")
297         {
298             *defaultTxParameters = true;
299         }
300         else
301         {
302             SuccessOrExit(error = aArgs[1].ParseAsUint32(txParameters->mAckTimeout));
303             SuccessOrExit(error = aArgs[2].ParseAsUint8(txParameters->mAckRandomFactorNumerator));
304             SuccessOrExit(error = aArgs[3].ParseAsUint8(txParameters->mAckRandomFactorDenominator));
305             SuccessOrExit(error = aArgs[4].ParseAsUint8(txParameters->mMaxRetransmit));
306 
307             VerifyOrExit(txParameters->mAckRandomFactorNumerator > txParameters->mAckRandomFactorDenominator,
308                          error = OT_ERROR_INVALID_ARGS);
309 
310             *defaultTxParameters = false;
311         }
312     }
313 
314     OutputLine("Transmission parameters for %s:", aArgs[0].GetCString());
315 
316     if (*defaultTxParameters)
317     {
318         OutputLine("default");
319     }
320     else
321     {
322         OutputLine("ACK_TIMEOUT=%u ms, ACK_RANDOM_FACTOR=%u/%u, MAX_RETRANSMIT=%u", txParameters->mAckTimeout,
323                    txParameters->mAckRandomFactorNumerator, txParameters->mAckRandomFactorDenominator,
324                    txParameters->mMaxRetransmit);
325     }
326 
327 exit:
328     return error;
329 }
330 
Process(Arg aArgs[])331 template <> otError Coap::Process<Cmd("get")>(Arg aArgs[])
332 {
333     return ProcessRequest(aArgs, OT_COAP_CODE_GET);
334 }
335 
Process(Arg aArgs[])336 template <> otError Coap::Process<Cmd("post")>(Arg aArgs[])
337 {
338     return ProcessRequest(aArgs, OT_COAP_CODE_POST);
339 }
340 
Process(Arg aArgs[])341 template <> otError Coap::Process<Cmd("put")>(Arg aArgs[])
342 {
343     return ProcessRequest(aArgs, OT_COAP_CODE_PUT);
344 }
345 
Process(Arg aArgs[])346 template <> otError Coap::Process<Cmd("delete")>(Arg aArgs[])
347 {
348     return ProcessRequest(aArgs, OT_COAP_CODE_DELETE);
349 }
350 
351 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
Process(Arg aArgs[])352 template <> otError Coap::Process<Cmd("observe")>(Arg aArgs[])
353 {
354     return ProcessRequest(aArgs, OT_COAP_CODE_GET, /* aCoapObserve */ true);
355 }
356 #endif
357 
358 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
ProcessRequest(Arg aArgs[],otCoapCode aCoapCode,bool aCoapObserve)359 otError Coap::ProcessRequest(Arg aArgs[], otCoapCode aCoapCode, bool aCoapObserve)
360 #else
361 otError Coap::ProcessRequest(Arg aArgs[], otCoapCode aCoapCode)
362 #endif
363 {
364     otError       error   = OT_ERROR_NONE;
365     otMessage *   message = nullptr;
366     otMessageInfo messageInfo;
367     uint16_t      payloadLength = 0;
368 
369     // Default parameters
370     char         coapUri[kMaxUriLength] = "test";
371     otCoapType   coapType               = OT_COAP_TYPE_NON_CONFIRMABLE;
372     otIp6Address coapDestinationIp;
373 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
374     bool           coapBlock     = false;
375     otCoapBlockSzx coapBlockSize = OT_COAP_OPTION_BLOCK_SZX_16;
376     BlockType      coapBlockType = (aCoapCode == OT_COAP_CODE_GET) ? kBlockType2 : kBlockType1;
377 #endif
378 
379 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE && OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
380     if (aCoapObserve)
381     {
382         coapBlockType = kBlockType1;
383     }
384 #endif
385 
386     SuccessOrExit(error = aArgs[0].ParseAsIp6Address(coapDestinationIp));
387 
388     VerifyOrExit(!aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
389     VerifyOrExit(aArgs[1].GetLength() < sizeof(coapUri), error = OT_ERROR_INVALID_ARGS);
390     strcpy(coapUri, aArgs[1].GetCString());
391 
392     // CoAP-Type
393     if (!aArgs[2].IsEmpty())
394     {
395         if (aArgs[2] == "con")
396         {
397             coapType = OT_COAP_TYPE_CONFIRMABLE;
398         }
399 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
400         else if (aArgs[2] == "block-16")
401         {
402             coapType      = OT_COAP_TYPE_CONFIRMABLE;
403             coapBlock     = true;
404             coapBlockSize = OT_COAP_OPTION_BLOCK_SZX_16;
405         }
406         else if (aArgs[2] == "block-32")
407         {
408             coapType      = OT_COAP_TYPE_CONFIRMABLE;
409             coapBlock     = true;
410             coapBlockSize = OT_COAP_OPTION_BLOCK_SZX_32;
411         }
412         else if (aArgs[2] == "block-64")
413         {
414             coapType      = OT_COAP_TYPE_CONFIRMABLE;
415             coapBlock     = true;
416             coapBlockSize = OT_COAP_OPTION_BLOCK_SZX_64;
417         }
418         else if (aArgs[2] == "block-128")
419         {
420             coapType      = OT_COAP_TYPE_CONFIRMABLE;
421             coapBlock     = true;
422             coapBlockSize = OT_COAP_OPTION_BLOCK_SZX_128;
423         }
424         else if (aArgs[2] == "block-256")
425         {
426             coapType      = OT_COAP_TYPE_CONFIRMABLE;
427             coapBlock     = true;
428             coapBlockSize = OT_COAP_OPTION_BLOCK_SZX_256;
429         }
430         else if (aArgs[2] == "block-512")
431         {
432             coapType      = OT_COAP_TYPE_CONFIRMABLE;
433             coapBlock     = true;
434             coapBlockSize = OT_COAP_OPTION_BLOCK_SZX_512;
435         }
436         else if (aArgs[2] == "block-1024")
437         {
438             coapType      = OT_COAP_TYPE_CONFIRMABLE;
439             coapBlock     = true;
440             coapBlockSize = OT_COAP_OPTION_BLOCK_SZX_1024;
441         }
442 #endif // OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
443     }
444 
445 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
446     if (aCoapObserve && mRequestTokenLength)
447     {
448         // New observe request, cancel any existing observation
449         SuccessOrExit(error = CancelResourceSubscription());
450     }
451 #endif
452 
453     message = otCoapNewMessage(GetInstancePtr(), nullptr);
454     VerifyOrExit(message != nullptr, error = OT_ERROR_NO_BUFS);
455 
456     otCoapMessageInit(message, coapType, aCoapCode);
457     otCoapMessageGenerateToken(message, OT_COAP_DEFAULT_TOKEN_LENGTH);
458 
459 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
460     if (aCoapObserve)
461     {
462         SuccessOrExit(error = otCoapMessageAppendObserveOption(message, 0));
463     }
464 #endif
465 
466     SuccessOrExit(error = otCoapMessageAppendUriPathOptions(message, coapUri));
467 
468 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
469     if (coapBlock)
470     {
471         if (coapBlockType == kBlockType1)
472         {
473             SuccessOrExit(error = otCoapMessageAppendBlock1Option(message, 0, true, coapBlockSize));
474         }
475         else
476         {
477             SuccessOrExit(error = otCoapMessageAppendBlock2Option(message, 0, false, coapBlockSize));
478         }
479     }
480 #endif
481 
482     if (!aArgs[3].IsEmpty())
483     {
484 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
485         if (coapBlock)
486         {
487             SuccessOrExit(error = aArgs[3].ParseAsUint32(mBlockCount));
488         }
489         else
490         {
491 #endif
492             payloadLength = aArgs[3].GetLength();
493 
494             if (payloadLength > 0)
495             {
496                 SuccessOrExit(error = otCoapMessageSetPayloadMarker(message));
497             }
498 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
499         }
500 #endif
501     }
502 
503     // Embed content into message if given
504     if (payloadLength > 0)
505     {
506         SuccessOrExit(error = otMessageAppend(message, aArgs[3].GetCString(), payloadLength));
507     }
508 
509     memset(&messageInfo, 0, sizeof(messageInfo));
510     messageInfo.mPeerAddr = coapDestinationIp;
511     messageInfo.mPeerPort = OT_DEFAULT_COAP_PORT;
512 
513 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
514     if (aCoapObserve)
515     {
516         // Make a note of the message details for later so we can cancel it later.
517         memcpy(&mRequestAddr, &coapDestinationIp, sizeof(mRequestAddr));
518         mRequestTokenLength = otCoapMessageGetTokenLength(message);
519         memcpy(mRequestToken, otCoapMessageGetToken(message), mRequestTokenLength);
520         // Use `memcpy` instead of `strncpy` here because GCC will give warnings for `strncpy` when the dest's length is
521         // not bigger than the src's length.
522         memcpy(mRequestUri, coapUri, sizeof(mRequestUri) - 1);
523     }
524 #endif
525 
526     if ((coapType == OT_COAP_TYPE_CONFIRMABLE) || (aCoapCode == OT_COAP_CODE_GET))
527     {
528 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
529         if (coapBlock)
530         {
531             if (aCoapCode == OT_COAP_CODE_PUT || aCoapCode == OT_COAP_CODE_POST)
532             {
533                 SuccessOrExit(error = otCoapMessageSetPayloadMarker(message));
534             }
535             error = otCoapSendRequestBlockWiseWithParameters(GetInstancePtr(), message, &messageInfo,
536                                                              &Coap::HandleResponse, this, GetRequestTxParameters(),
537                                                              Coap::BlockwiseTransmitHook, Coap::BlockwiseReceiveHook);
538         }
539         else
540         {
541 #endif
542             error = otCoapSendRequestWithParameters(GetInstancePtr(), message, &messageInfo, &Coap::HandleResponse,
543                                                     this, GetRequestTxParameters());
544 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
545         }
546 #endif
547     }
548     else
549     {
550         error = otCoapSendRequestWithParameters(GetInstancePtr(), message, &messageInfo, nullptr, nullptr,
551                                                 GetResponseTxParameters());
552     }
553 
554 exit:
555 
556     if ((error != OT_ERROR_NONE) && (message != nullptr))
557     {
558         otMessageFree(message);
559     }
560 
561     return error;
562 }
563 
Process(Arg aArgs[])564 otError Coap::Process(Arg aArgs[])
565 {
566 #define CmdEntry(aCommandString)                            \
567     {                                                       \
568         aCommandString, &Coap::Process<Cmd(aCommandString)> \
569     }
570 
571     static constexpr Command kCommands[] = {
572 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
573         CmdEntry("cancel"),
574 #endif
575         CmdEntry("delete"),
576         CmdEntry("get"),
577 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
578         CmdEntry("observe"),
579 #endif
580         CmdEntry("parameters"),
581         CmdEntry("post"),
582         CmdEntry("put"),
583         CmdEntry("resource"),
584         CmdEntry("set"),
585         CmdEntry("start"),
586         CmdEntry("stop"),
587     };
588 
589 #undef CmdEntry
590 
591     static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted");
592 
593     otError        error = OT_ERROR_INVALID_COMMAND;
594     const Command *command;
595 
596     if (aArgs[0].IsEmpty() || (aArgs[0] == "help"))
597     {
598         OutputCommandTable(kCommands);
599         ExitNow(error = aArgs[0].IsEmpty() ? OT_ERROR_INVALID_ARGS : OT_ERROR_NONE);
600     }
601 
602     command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
603     VerifyOrExit(command != nullptr);
604 
605     error = (this->*command->mHandler)(aArgs + 1);
606 
607 exit:
608     return error;
609 }
610 
HandleRequest(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo)611 void Coap::HandleRequest(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
612 {
613     static_cast<Coap *>(aContext)->HandleRequest(aMessage, aMessageInfo);
614 }
615 
HandleRequest(otMessage * aMessage,const otMessageInfo * aMessageInfo)616 void Coap::HandleRequest(otMessage *aMessage, const otMessageInfo *aMessageInfo)
617 {
618     otError    error           = OT_ERROR_NONE;
619     otMessage *responseMessage = nullptr;
620     otCoapCode responseCode    = OT_COAP_CODE_EMPTY;
621 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
622     uint64_t observe        = 0;
623     bool     observePresent = false;
624 #endif
625 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
626     uint64_t blockValue   = 0;
627     bool     blockPresent = false;
628 #endif
629 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE || OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
630     otCoapOptionIterator iterator;
631 #endif
632 
633     OutputFormat("coap request from ");
634     OutputIp6Address(aMessageInfo->mPeerAddr);
635     OutputFormat(" ");
636 
637     switch (otCoapMessageGetCode(aMessage))
638     {
639     case OT_COAP_CODE_GET:
640         OutputFormat("GET");
641 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE || OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
642         SuccessOrExit(error = otCoapOptionIteratorInit(&iterator, aMessage));
643 #endif
644 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
645         if (otCoapOptionIteratorGetFirstOptionMatching(&iterator, OT_COAP_OPTION_OBSERVE) != nullptr)
646         {
647             SuccessOrExit(error = otCoapOptionIteratorGetOptionUintValue(&iterator, &observe));
648             observePresent = true;
649 
650             OutputFormat(" OBS=%lu", static_cast<uint32_t>(observe));
651         }
652 #endif
653 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
654         if (otCoapOptionIteratorGetFirstOptionMatching(&iterator, OT_COAP_OPTION_BLOCK2) != nullptr)
655         {
656             SuccessOrExit(error = otCoapOptionIteratorGetOptionUintValue(&iterator, &blockValue));
657             blockPresent = true;
658         }
659 #endif
660         break;
661 
662     case OT_COAP_CODE_DELETE:
663         OutputFormat("DELETE");
664         break;
665 
666     case OT_COAP_CODE_PUT:
667         OutputFormat("PUT");
668         break;
669 
670     case OT_COAP_CODE_POST:
671         OutputFormat("POST");
672         break;
673 
674     default:
675         OutputLine("Undefined");
676         ExitNow(error = OT_ERROR_PARSE);
677     }
678 
679     PrintPayload(aMessage);
680 
681     if (otCoapMessageGetType(aMessage) == OT_COAP_TYPE_CONFIRMABLE ||
682         otCoapMessageGetCode(aMessage) == OT_COAP_CODE_GET)
683     {
684 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
685         if (observePresent && (mSubscriberTokenLength > 0) && (observe == 0))
686         {
687             // There is already a subscriber
688             responseCode = OT_COAP_CODE_SERVICE_UNAVAILABLE;
689         }
690         else
691 #endif
692             if (otCoapMessageGetCode(aMessage) == OT_COAP_CODE_GET)
693         {
694             responseCode = OT_COAP_CODE_CONTENT;
695 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
696             if (observePresent)
697             {
698                 if (observe == 0)
699                 {
700                     // New subscriber
701                     OutputLine("Subscribing client");
702                     mSubscriberSock.mAddress = aMessageInfo->mPeerAddr;
703                     mSubscriberSock.mPort    = aMessageInfo->mPeerPort;
704                     mSubscriberTokenLength   = otCoapMessageGetTokenLength(aMessage);
705                     memcpy(mSubscriberToken, otCoapMessageGetToken(aMessage), mSubscriberTokenLength);
706 
707                     /*
708                      * Implementer note.
709                      *
710                      * Here, we try to match a confirmable GET request with confirmable
711                      * notifications, however this is not a requirement of RFC7641:
712                      * the server can send notifications of either type regardless of
713                      * what the client used to subscribe initially.
714                      */
715                     mSubscriberConfirmableNotifications = (otCoapMessageGetType(aMessage) == OT_COAP_TYPE_CONFIRMABLE);
716                 }
717                 else if (observe == 1)
718                 {
719                     // See if it matches our subscriber token
720                     if ((otCoapMessageGetTokenLength(aMessage) == mSubscriberTokenLength) &&
721                         (memcmp(otCoapMessageGetToken(aMessage), mSubscriberToken, mSubscriberTokenLength) == 0))
722                     {
723                         // Unsubscribe request
724                         CancelSubscriber();
725                     }
726                 }
727             }
728 #endif // OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
729         }
730         else
731         {
732             responseCode = OT_COAP_CODE_VALID;
733         }
734 
735         responseMessage = otCoapNewMessage(GetInstancePtr(), nullptr);
736         VerifyOrExit(responseMessage != nullptr, error = OT_ERROR_NO_BUFS);
737 
738         SuccessOrExit(
739             error = otCoapMessageInitResponse(responseMessage, aMessage, OT_COAP_TYPE_ACKNOWLEDGMENT, responseCode));
740 
741         if (responseCode == OT_COAP_CODE_CONTENT)
742         {
743 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
744             if (observePresent && (observe == 0))
745             {
746                 SuccessOrExit(error = otCoapMessageAppendObserveOption(responseMessage, mObserveSerial++));
747             }
748 #endif
749 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
750             if (blockPresent)
751             {
752                 SuccessOrExit(error = otCoapMessageAppendBlock2Option(responseMessage,
753                                                                       static_cast<uint32_t>(blockValue >> 4), true,
754                                                                       static_cast<otCoapBlockSzx>(blockValue & 0x7)));
755                 SuccessOrExit(error = otCoapMessageSetPayloadMarker(responseMessage));
756             }
757             else
758             {
759 #endif
760                 SuccessOrExit(error = otCoapMessageSetPayloadMarker(responseMessage));
761                 SuccessOrExit(error = otMessageAppend(responseMessage, mResourceContent,
762                                                       static_cast<uint16_t>(strlen(mResourceContent))));
763 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
764             }
765 #endif
766         }
767 
768 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
769         if (blockPresent)
770         {
771             SuccessOrExit(error = otCoapSendResponseBlockWiseWithParameters(GetInstancePtr(), responseMessage,
772                                                                             aMessageInfo, GetResponseTxParameters(),
773                                                                             this, mResource.mTransmitHook));
774         }
775         else
776         {
777 #endif
778             SuccessOrExit(error = otCoapSendResponseWithParameters(GetInstancePtr(), responseMessage, aMessageInfo,
779                                                                    GetResponseTxParameters()));
780 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
781         }
782 #endif
783     }
784 
785 exit:
786 
787     if (error != OT_ERROR_NONE)
788     {
789         if (responseMessage != nullptr)
790         {
791             OutputLine("coap send response error %d: %s", error, otThreadErrorToString(error));
792             otMessageFree(responseMessage);
793         }
794     }
795     else if (responseCode >= OT_COAP_CODE_RESPONSE_MIN)
796     {
797         OutputLine("coap response sent");
798     }
799 }
800 
801 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
HandleNotificationResponse(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo,otError aError)802 void Coap::HandleNotificationResponse(void *               aContext,
803                                       otMessage *          aMessage,
804                                       const otMessageInfo *aMessageInfo,
805                                       otError              aError)
806 {
807     static_cast<Coap *>(aContext)->HandleNotificationResponse(aMessage, aMessageInfo, aError);
808 }
809 
HandleNotificationResponse(otMessage * aMessage,const otMessageInfo * aMessageInfo,otError aError)810 void Coap::HandleNotificationResponse(otMessage *aMessage, const otMessageInfo *aMessageInfo, otError aError)
811 {
812     OT_UNUSED_VARIABLE(aMessage);
813 
814     switch (aError)
815     {
816     case OT_ERROR_NONE:
817         if (aMessageInfo != nullptr)
818         {
819             OutputFormat("Received ACK in reply to notification from ");
820             OutputIp6AddressLine(aMessageInfo->mPeerAddr);
821         }
822         break;
823 
824     default:
825         OutputLine("coap receive notification response error %d: %s", aError, otThreadErrorToString(aError));
826         CancelSubscriber();
827         break;
828     }
829 }
830 #endif // OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
831 
HandleResponse(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo,otError aError)832 void Coap::HandleResponse(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo, otError aError)
833 {
834     static_cast<Coap *>(aContext)->HandleResponse(aMessage, aMessageInfo, aError);
835 }
836 
HandleResponse(otMessage * aMessage,const otMessageInfo * aMessageInfo,otError aError)837 void Coap::HandleResponse(otMessage *aMessage, const otMessageInfo *aMessageInfo, otError aError)
838 {
839     if (aError != OT_ERROR_NONE)
840     {
841         OutputLine("coap receive response error %d: %s", aError, otThreadErrorToString(aError));
842     }
843     else if ((aMessageInfo != nullptr) && (aMessage != nullptr))
844     {
845 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
846         otCoapOptionIterator iterator;
847 #endif
848 
849         OutputFormat("coap response from ");
850         OutputIp6Address(aMessageInfo->mPeerAddr);
851 
852 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
853         if (otCoapOptionIteratorInit(&iterator, aMessage) == OT_ERROR_NONE)
854         {
855             const otCoapOption *observeOpt =
856                 otCoapOptionIteratorGetFirstOptionMatching(&iterator, OT_COAP_OPTION_OBSERVE);
857 
858             if (observeOpt != nullptr)
859             {
860                 uint64_t observeVal = 0;
861                 otError  error      = otCoapOptionIteratorGetOptionUintValue(&iterator, &observeVal);
862 
863                 if (error == OT_ERROR_NONE)
864                 {
865                     OutputFormat(" OBS=%u", observeVal);
866                 }
867             }
868         }
869 #endif
870         PrintPayload(aMessage);
871     }
872 }
873 
874 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
BlockwiseReceiveHook(void * aContext,const uint8_t * aBlock,uint32_t aPosition,uint16_t aBlockLength,bool aMore,uint32_t aTotalLength)875 otError Coap::BlockwiseReceiveHook(void *         aContext,
876                                    const uint8_t *aBlock,
877                                    uint32_t       aPosition,
878                                    uint16_t       aBlockLength,
879                                    bool           aMore,
880                                    uint32_t       aTotalLength)
881 {
882     return static_cast<Coap *>(aContext)->BlockwiseReceiveHook(aBlock, aPosition, aBlockLength, aMore, aTotalLength);
883 }
884 
BlockwiseReceiveHook(const uint8_t * aBlock,uint32_t aPosition,uint16_t aBlockLength,bool aMore,uint32_t aTotalLength)885 otError Coap::BlockwiseReceiveHook(const uint8_t *aBlock,
886                                    uint32_t       aPosition,
887                                    uint16_t       aBlockLength,
888                                    bool           aMore,
889                                    uint32_t       aTotalLength)
890 {
891     OT_UNUSED_VARIABLE(aMore);
892     OT_UNUSED_VARIABLE(aTotalLength);
893 
894     OutputLine("received block: Num %i Len %i", aPosition / aBlockLength, aBlockLength);
895 
896     for (uint16_t i = 0; i < aBlockLength / 16; i++)
897     {
898         OutputBytesLine(&aBlock[i * 16], 16);
899     }
900 
901     return OT_ERROR_NONE;
902 }
903 
BlockwiseTransmitHook(void * aContext,uint8_t * aBlock,uint32_t aPosition,uint16_t * aBlockLength,bool * aMore)904 otError Coap::BlockwiseTransmitHook(void *    aContext,
905                                     uint8_t * aBlock,
906                                     uint32_t  aPosition,
907                                     uint16_t *aBlockLength,
908                                     bool *    aMore)
909 {
910     return static_cast<Coap *>(aContext)->BlockwiseTransmitHook(aBlock, aPosition, aBlockLength, aMore);
911 }
912 
BlockwiseTransmitHook(uint8_t * aBlock,uint32_t aPosition,uint16_t * aBlockLength,bool * aMore)913 otError Coap::BlockwiseTransmitHook(uint8_t *aBlock, uint32_t aPosition, uint16_t *aBlockLength, bool *aMore)
914 {
915     static uint32_t blockCount = 0;
916     OT_UNUSED_VARIABLE(aPosition);
917 
918     // Send a random payload
919     otRandomNonCryptoFillBuffer(aBlock, *aBlockLength);
920 
921     OutputLine("send block: Num %i Len %i", blockCount, *aBlockLength);
922 
923     for (uint16_t i = 0; i < *aBlockLength / 16; i++)
924     {
925         OutputBytesLine(&aBlock[i * 16], 16);
926     }
927 
928     if (blockCount == mBlockCount - 1)
929     {
930         blockCount = 0;
931         *aMore     = false;
932     }
933     else
934     {
935         *aMore = true;
936         blockCount++;
937     }
938 
939     return OT_ERROR_NONE;
940 }
941 #endif // OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
942 
943 } // namespace Cli
944 } // namespace ot
945 
946 #endif // OPENTHREAD_CONFIG_COAP_API_ENABLE
947