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