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