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