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(otInstance * aInstance,OutputImplementer & aOutputImplementer)47 Coap::Coap(otInstance *aInstance, OutputImplementer &aOutputImplementer)
48 : Utils(aInstance, aOutputImplementer)
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 ClearAllBytes(mResource);
62 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
63 ClearAllBytes(mRequestAddr);
64 ClearAllBytes(mSubscriberSock);
65 ClearAllBytes(mRequestToken);
66 ClearAllBytes(mSubscriberToken);
67 ClearAllBytes(mRequestUri);
68 #endif
69 ClearAllBytes(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 ClearAllBytes(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 ClearAllBytes(mRequestAddr);
98 ClearAllBytes(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 ClearAllBytes(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 = Min(length, static_cast<uint16_t>(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 OutputNewLine();
142 }
143
144 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
145 /**
146 * @cli coap cancel
147 * @code
148 * coap cancel
149 * Done
150 * @endcode
151 * @par
152 * Cancels an existing observation subscription to a remote resource on the CoAP server.
153 * @note This command is available only when `OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE` is set.
154 * @csa{coap observe}
155 */
Process(Arg aArgs[])156 template <> otError Coap::Process<Cmd("cancel")>(Arg aArgs[])
157 {
158 OT_UNUSED_VARIABLE(aArgs);
159
160 return CancelResourceSubscription();
161 }
162 #endif
163
164 /**
165 * @cli coap resource (get,set)
166 * @code
167 * coap resource test-resource
168 * Done
169 * @endcode
170 * @code
171 * coap resource
172 * test-resource
173 * Done
174 * @endcode
175 * @cparam coap resource [@ca{uri-path}]
176 * @par
177 * Gets or sets the URI path of the CoAP server resource.
178 * @sa otCoapAddResource
179 * @sa otCoapAddBlockWiseResource
180 */
Process(Arg aArgs[])181 template <> otError Coap::Process<Cmd("resource")>(Arg aArgs[])
182 {
183 otError error = OT_ERROR_NONE;
184
185 if (!aArgs[0].IsEmpty())
186 {
187 VerifyOrExit(aArgs[0].GetLength() < kMaxUriLength, error = OT_ERROR_INVALID_ARGS);
188
189 mResource.mUriPath = mUriPath;
190 mResource.mContext = this;
191 mResource.mHandler = &Coap::HandleRequest;
192
193 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
194 mResource.mReceiveHook = &Coap::BlockwiseReceiveHook;
195 mResource.mTransmitHook = &Coap::BlockwiseTransmitHook;
196
197 if (!aArgs[1].IsEmpty())
198 {
199 SuccessOrExit(error = aArgs[1].ParseAsUint32(mBlockCount));
200 }
201 #endif
202
203 strncpy(mUriPath, aArgs[0].GetCString(), sizeof(mUriPath) - 1);
204
205 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
206 otCoapAddBlockWiseResource(GetInstancePtr(), &mResource);
207 #else
208 otCoapAddResource(GetInstancePtr(), &mResource);
209 #endif
210 }
211 else
212 {
213 OutputLine("%s", mResource.mUriPath != nullptr ? mResource.mUriPath : "");
214 }
215
216 exit:
217 return error;
218 }
219
220 /**
221 * @cli coap set
222 * @code
223 * coap set Testing123
224 * Done
225 * @endcode
226 * @cparam coap set @ca{new-content}
227 * @par
228 * Sets the content sent by the resource on the CoAP server.
229 * If a CoAP client is observing the resource, a notification is sent to that client.
230 * @csa{coap observe}
231 * @sa otCoapMessageInit
232 * @sa otCoapNewMessage
233 */
Process(Arg aArgs[])234 template <> otError Coap::Process<Cmd("set")>(Arg aArgs[])
235 {
236 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
237 otMessage *notificationMessage = nullptr;
238 otMessageInfo messageInfo;
239 #endif
240 otError error = OT_ERROR_NONE;
241
242 if (!aArgs[0].IsEmpty())
243 {
244 VerifyOrExit(aArgs[0].GetLength() < sizeof(mResourceContent), error = OT_ERROR_INVALID_ARGS);
245 strncpy(mResourceContent, aArgs[0].GetCString(), sizeof(mResourceContent));
246 mResourceContent[sizeof(mResourceContent) - 1] = '\0';
247
248 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
249 if (mSubscriberTokenLength > 0)
250 {
251 // Notify the subscriber
252 ClearAllBytes(messageInfo);
253 messageInfo.mPeerAddr = mSubscriberSock.mAddress;
254 messageInfo.mPeerPort = mSubscriberSock.mPort;
255
256 OutputFormat("sending coap notification to ");
257 OutputIp6AddressLine(mSubscriberSock.mAddress);
258
259 notificationMessage = otCoapNewMessage(GetInstancePtr(), nullptr);
260 VerifyOrExit(notificationMessage != nullptr, error = OT_ERROR_NO_BUFS);
261
262 otCoapMessageInit(
263 notificationMessage,
264 ((mSubscriberConfirmableNotifications) ? OT_COAP_TYPE_CONFIRMABLE : OT_COAP_TYPE_NON_CONFIRMABLE),
265 OT_COAP_CODE_CONTENT);
266
267 SuccessOrExit(error = otCoapMessageSetToken(notificationMessage, mSubscriberToken, mSubscriberTokenLength));
268 SuccessOrExit(error = otCoapMessageAppendObserveOption(notificationMessage, mObserveSerial++));
269 SuccessOrExit(error = otCoapMessageSetPayloadMarker(notificationMessage));
270 SuccessOrExit(error = otMessageAppend(notificationMessage, mResourceContent,
271 static_cast<uint16_t>(strlen(mResourceContent))));
272
273 SuccessOrExit(error = otCoapSendRequest(GetInstancePtr(), notificationMessage, &messageInfo,
274 &Coap::HandleNotificationResponse, this));
275 }
276 #endif // OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
277 }
278 else
279 {
280 OutputLine("%s", mResourceContent);
281 }
282
283 exit:
284
285 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
286 if ((error != OT_ERROR_NONE) && (notificationMessage != nullptr))
287 {
288 otMessageFree(notificationMessage);
289 }
290 #endif
291
292 return error;
293 }
294
295 /**
296 * @cli coap start
297 * @code
298 * coap start
299 * Done
300 * @endcode
301 * @par
302 * Starts the CoAP server. @moreinfo{@coap}.
303 * @sa otCoapStart
304 */
Process(Arg aArgs[])305 template <> otError Coap::Process<Cmd("start")>(Arg aArgs[])
306 {
307 OT_UNUSED_VARIABLE(aArgs);
308
309 return otCoapStart(GetInstancePtr(), OT_DEFAULT_COAP_PORT);
310 }
311
312 /**
313 * @cli coap stop
314 * @code
315 * coap stop
316 * Done
317 * @endcode
318 * @par api_copy
319 * #otCoapStop
320 */
Process(Arg aArgs[])321 template <> otError Coap::Process<Cmd("stop")>(Arg aArgs[])
322 {
323 OT_UNUSED_VARIABLE(aArgs);
324
325 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
326 otCoapRemoveBlockWiseResource(GetInstancePtr(), &mResource);
327 #else
328 otCoapRemoveResource(GetInstancePtr(), &mResource);
329 #endif
330
331 return otCoapStop(GetInstancePtr());
332 }
333
334 /**
335 * @cli coap parameters(get,set)
336 * @code
337 * coap parameters request
338 * Transmission parameters for request:
339 * ACK_TIMEOUT=1000 ms, ACK_RANDOM_FACTOR=255/254, MAX_RETRANSMIT=2
340 * Done
341 * @endcode
342 * @code
343 * coap parameters request default
344 * Transmission parameters for request:
345 * default
346 * Done
347 * @endcode
348 * @code
349 * coap parameters request 1000 255 254 2
350 * Transmission parameters for request:
351 * ACK_TIMEOUT=1000 ms, ACK_RANDOM_FACTOR=255/254, MAX_RETRANSMIT=2
352 * Done
353 * @endcode
354 * @cparam coap parameters @ca{type} [@ca{default} | <!--
355 * -->@ca{ack_timeout ack_random_factor_numerator <!--
356 * -->ack_random_factor_denominator max_retransmit}]
357 * * `type`: `request` for CoAP requests, or `response` for CoAP responses.
358 If no more parameters are given, the command prints the current configuration.
359 * * `default`: Sets the transmission parameters to
360 the following default values:
361 * * `ack_timeout`: 2000 milliseconds
362 * * `ack_random_factor_numerator`: 3
363 * * `ack_random_factor_denominator`: 2
364 * * `max_retransmit`: 4
365 * * `ack_timeout`: The `ACK_TIMEOUT` (0-UINT32_MAX) in milliseconds.
366 Refer to RFC7252.
367 * * `ack_random_factor_numerator`:
368 The `ACK_RANDOM_FACTOR` numerator, with possible values
369 of 0-255. Refer to RFC7252.
370 * * `ack_random_factor_denominator`:
371 * The `ACK_RANDOM_FACTOR` denominator, with possible values
372 * of 0-255. Refer to RFC7252.
373 * * `max_retransmit`: The `MAX_RETRANSMIT` (0-255). Refer to RFC7252.
374 * @par
375 * Gets current CoAP parameter values if the command is run with no optional
376 * parameters.
377 * @par
378 * Sets the CoAP parameters either to their default values or to the values
379 * you specify, depending on the syntax chosen.
380 */
Process(Arg aArgs[])381 template <> otError Coap::Process<Cmd("parameters")>(Arg aArgs[])
382 {
383 otError error = OT_ERROR_NONE;
384 bool *defaultTxParameters;
385 otCoapTxParameters *txParameters;
386
387 if (aArgs[0] == "request")
388 {
389 txParameters = &mRequestTxParameters;
390 defaultTxParameters = &mUseDefaultRequestTxParameters;
391 }
392 else if (aArgs[0] == "response")
393 {
394 txParameters = &mResponseTxParameters;
395 defaultTxParameters = &mUseDefaultResponseTxParameters;
396 }
397 else
398 {
399 ExitNow(error = OT_ERROR_INVALID_ARGS);
400 }
401
402 if (!aArgs[1].IsEmpty())
403 {
404 if (aArgs[1] == "default")
405 {
406 *defaultTxParameters = true;
407 }
408 else
409 {
410 SuccessOrExit(error = aArgs[1].ParseAsUint32(txParameters->mAckTimeout));
411 SuccessOrExit(error = aArgs[2].ParseAsUint8(txParameters->mAckRandomFactorNumerator));
412 SuccessOrExit(error = aArgs[3].ParseAsUint8(txParameters->mAckRandomFactorDenominator));
413 SuccessOrExit(error = aArgs[4].ParseAsUint8(txParameters->mMaxRetransmit));
414
415 VerifyOrExit(txParameters->mAckRandomFactorNumerator > txParameters->mAckRandomFactorDenominator,
416 error = OT_ERROR_INVALID_ARGS);
417
418 *defaultTxParameters = false;
419 }
420 }
421
422 OutputLine("Transmission parameters for %s:", aArgs[0].GetCString());
423
424 if (*defaultTxParameters)
425 {
426 OutputLine("default");
427 }
428 else
429 {
430 OutputLine("ACK_TIMEOUT=%lu ms, ACK_RANDOM_FACTOR=%u/%u, MAX_RETRANSMIT=%u", ToUlong(txParameters->mAckTimeout),
431 txParameters->mAckRandomFactorNumerator, txParameters->mAckRandomFactorDenominator,
432 txParameters->mMaxRetransmit);
433 }
434
435 exit:
436 return error;
437 }
438
439 /**
440 * @cli coap get
441 * @code
442 * coap get fdde:ad00:beef:0:2780:9423:166c:1aac test-resource
443 * Done
444 * @endcode
445 * @code
446 * coap get fdde:ad00:beef:0:2780:9423:166c:1aac test-resource block-1024
447 * Done
448 * @endcode
449 * @cparam coap get @ca{address} @ca{uri-path} [@ca{type}]
450 * * `address`: IPv6 address of the CoAP server.
451 * * `uri-path`: URI path of the resource.
452 * * `type`:
453 * * `con`: Confirmable
454 * * `non-con`: Non-confirmable (default)
455 * * `block-`: Use this option, followed by the block-wise value,
456 * if the response should be transferred block-wise. Valid
457 * values are: `block-16`, `block-32`, `block-64`, `block-128`,
458 * `block-256`, `block-512`, or `block-1024`.
459 * @par
460 * Gets information about the specified CoAP resource on the CoAP server.
461 */
Process(Arg aArgs[])462 template <> otError Coap::Process<Cmd("get")>(Arg aArgs[]) { return ProcessRequest(aArgs, OT_COAP_CODE_GET); }
463
464 /**
465 * @cli coap post
466 * @code
467 * coap post fdde:ad00:beef:0:2780:9423:166c:1aac test-resource con hellothere
468 * Done
469 * @endcode
470 * @code
471 * coap post fdde:ad00:beef:0:2780:9423:166c:1aac test-resource block-1024 10
472 * Done
473 * @endcode
474 * @cparam coap post @ca{address} @ca{uri-path} [@ca{type}] [@ca{payload}]
475 * * `address`: IPv6 address of the CoAP server.
476 * * `uri-path`: URI path of the resource.
477 * * `type`:
478 * * `con`: Confirmable
479 * * `non-con`: Non-confirmable (default)
480 * * `block-`: Use this option, followed by the block-wise value,
481 * to send blocks with a randomly generated number of bytes
482 * for the payload. Valid values are:
483 * `block-16`, `block-32`, `block-64`, `block-128`,
484 * `block-256`, `block-512`, or `block-1024`.
485 * * `payload`: CoAP payload request, which if used is either a string or an
486 * integer, depending on the `type`. If the `type` is `con` or `non-con`,
487 * the `payload` parameter is optional. If you leave out the
488 * `payload` parameter, an empty payload is sent. However, If you use the
489 * `payload` parameter, its value must be a string, such as
490 * `hellothere`. If the `type` is `block-`,
491 * the value of the`payload` parameter must be an integer that specifies
492 * the number of blocks to send. The `block-` type requires
493 * `OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE` to be set.
494 * @par
495 * Creates the specified CoAP resource. @moreinfo{@coap}.
496 */
Process(Arg aArgs[])497 template <> otError Coap::Process<Cmd("post")>(Arg aArgs[]) { return ProcessRequest(aArgs, OT_COAP_CODE_POST); }
498
499 /**
500 * @cli coap put
501 * @code
502 * coap put fdde:ad00:beef:0:2780:9423:166c:1aac test-resource con hellothere
503 * Done
504 * @endcode
505 * @code
506 * coap put fdde:ad00:beef:0:2780:9423:166c:1aac test-resource block-1024 10
507 * Done
508 * @endcode
509 * @cparam coap put @ca{address} @ca{uri-path} [@ca{type}] [@ca{payload}]
510 * * `address`: IPv6 address of the CoAP server.
511 * * `uri-path`: URI path of the resource.
512 * * `type`:
513 * * `con`: Confirmable
514 * * `non-con`: Non-confirmable (default)
515 * * `block-`: Use this option, followed by the block-wise value,
516 * to send blocks with a randomly generated number of bytes
517 * for the payload. Valid values are:
518 * `block-16`, `block-32`, `block-64`, `block-128`,
519 * `block-256`, `block-512`, or `block-1024`.
520 * * `payload`: CoAP payload request, which if used is either a string or an
521 * integer, depending on the `type`. If the `type` is `con` or `non-con`,
522 * the `payload` parameter is optional. If you leave out the
523 * `payload` parameter, an empty payload is sent. However, If you use the
524 * `payload` parameter, its value must be a string, such as
525 * `hellothere`. If the `type` is `block-`,
526 * the value of the`payload` parameter must be an integer that specifies
527 * the number of blocks to send. The `block-` type requires
528 * `OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE` to be set.
529 * @par
530 * Modifies the specified CoAP resource. @moreinfo{@coap}.
531 */
Process(Arg aArgs[])532 template <> otError Coap::Process<Cmd("put")>(Arg aArgs[]) { return ProcessRequest(aArgs, OT_COAP_CODE_PUT); }
533
534 /**
535 * @cli coap delete
536 * @code
537 * coap delete fdde:ad00:beef:0:2780:9423:166c:1aac test-resource con hellothere
538 * Done
539 * @endcode
540 * @cparam coap delete @ca{address} @ca{uri-path} [@ca{type}] [@ca{payload}]
541 * * `address`: IPv6 address of the CoAP server.
542 * * `uri-path`: URI path of the resource.
543 * * `type`:
544 * * `con`: Confirmable
545 * * `non-con`: Non-confirmable (default)
546 * * `payload`: The CoAP payload string. For example, `hellothere`.
547 * @par
548 * Deletes the specified CoAP resource.
549 */
Process(Arg aArgs[])550 template <> otError Coap::Process<Cmd("delete")>(Arg aArgs[]) { return ProcessRequest(aArgs, OT_COAP_CODE_DELETE); }
551
552 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
553 /**
554 * @cli coap observe
555 * @code
556 * coap observe fdde:ad00:beef:0:2780:9423:166c:1aac test-resource
557 * Done
558 * @endcode
559 * @cparam coap observe @ca{address} @ca{uri-path} [@ca{type}]
560 * * `address`: IPv6 address of the CoAP server.
561 * * `uri-path`: URI path of the resource.
562 * * `type`:
563 * * `con`: Confirmable
564 * * `non-con`: Non-confirmable (default).
565 * @par
566 * Triggers a subscription request which allows the CoAP client to
567 * observe the specified resource on the CoAP server for possible changes
568 * in its state.
569 * @note This command is available only when `OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE` is set.
570 */
Process(Arg aArgs[])571 template <> otError Coap::Process<Cmd("observe")>(Arg aArgs[])
572 {
573 return ProcessRequest(aArgs, OT_COAP_CODE_GET, /* aCoapObserve */ true);
574 }
575 #endif
576
577 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
ProcessRequest(Arg aArgs[],otCoapCode aCoapCode,bool aCoapObserve)578 otError Coap::ProcessRequest(Arg aArgs[], otCoapCode aCoapCode, bool aCoapObserve)
579 #else
580 otError Coap::ProcessRequest(Arg aArgs[], otCoapCode aCoapCode)
581 #endif
582 {
583 otError error = OT_ERROR_NONE;
584 otMessage *message = nullptr;
585 otMessageInfo messageInfo;
586 uint16_t payloadLength = 0;
587 char *uriQueryStartPtr = nullptr;
588
589 // Default parameters
590 char coapUri[kMaxUriLength] = "test";
591 otCoapType coapType = OT_COAP_TYPE_NON_CONFIRMABLE;
592 otIp6Address coapDestinationIp;
593 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
594 bool coapBlock = false;
595 otCoapBlockSzx coapBlockSize = OT_COAP_OPTION_BLOCK_SZX_16;
596 BlockType coapBlockType = (aCoapCode == OT_COAP_CODE_GET) ? kBlockType2 : kBlockType1;
597 #endif
598
599 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE && OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
600 if (aCoapObserve)
601 {
602 coapBlockType = kBlockType1;
603 }
604 #endif
605
606 SuccessOrExit(error = aArgs[0].ParseAsIp6Address(coapDestinationIp));
607
608 VerifyOrExit(!aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
609 VerifyOrExit(aArgs[1].GetLength() < sizeof(coapUri), error = OT_ERROR_INVALID_ARGS);
610 strcpy(coapUri, aArgs[1].GetCString());
611
612 // CoAP-Type
613 if (!aArgs[2].IsEmpty())
614 {
615 if (aArgs[2] == "con")
616 {
617 coapType = OT_COAP_TYPE_CONFIRMABLE;
618 }
619 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
620 else if (aArgs[2] == "block-16")
621 {
622 coapType = OT_COAP_TYPE_CONFIRMABLE;
623 coapBlock = true;
624 coapBlockSize = OT_COAP_OPTION_BLOCK_SZX_16;
625 }
626 else if (aArgs[2] == "block-32")
627 {
628 coapType = OT_COAP_TYPE_CONFIRMABLE;
629 coapBlock = true;
630 coapBlockSize = OT_COAP_OPTION_BLOCK_SZX_32;
631 }
632 else if (aArgs[2] == "block-64")
633 {
634 coapType = OT_COAP_TYPE_CONFIRMABLE;
635 coapBlock = true;
636 coapBlockSize = OT_COAP_OPTION_BLOCK_SZX_64;
637 }
638 else if (aArgs[2] == "block-128")
639 {
640 coapType = OT_COAP_TYPE_CONFIRMABLE;
641 coapBlock = true;
642 coapBlockSize = OT_COAP_OPTION_BLOCK_SZX_128;
643 }
644 else if (aArgs[2] == "block-256")
645 {
646 coapType = OT_COAP_TYPE_CONFIRMABLE;
647 coapBlock = true;
648 coapBlockSize = OT_COAP_OPTION_BLOCK_SZX_256;
649 }
650 else if (aArgs[2] == "block-512")
651 {
652 coapType = OT_COAP_TYPE_CONFIRMABLE;
653 coapBlock = true;
654 coapBlockSize = OT_COAP_OPTION_BLOCK_SZX_512;
655 }
656 else if (aArgs[2] == "block-1024")
657 {
658 coapType = OT_COAP_TYPE_CONFIRMABLE;
659 coapBlock = true;
660 coapBlockSize = OT_COAP_OPTION_BLOCK_SZX_1024;
661 }
662 #endif // OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
663 }
664
665 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
666 if (aCoapObserve && mRequestTokenLength)
667 {
668 // New observe request, cancel any existing observation
669 SuccessOrExit(error = CancelResourceSubscription());
670 }
671 #endif
672
673 message = otCoapNewMessage(GetInstancePtr(), nullptr);
674 VerifyOrExit(message != nullptr, error = OT_ERROR_NO_BUFS);
675
676 otCoapMessageInit(message, coapType, aCoapCode);
677 otCoapMessageGenerateToken(message, OT_COAP_DEFAULT_TOKEN_LENGTH);
678
679 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
680 if (aCoapObserve)
681 {
682 SuccessOrExit(error = otCoapMessageAppendObserveOption(message, 0));
683 }
684 #endif
685
686 uriQueryStartPtr = const_cast<char *>(StringFind(coapUri, '?'));
687
688 if (uriQueryStartPtr == nullptr)
689 {
690 // "?" doesn't present in URI --> contains only URI path parts
691 SuccessOrExit(error = otCoapMessageAppendUriPathOptions(message, coapUri));
692 }
693 else
694 {
695 // "?" presents in URI --> contains URI path AND URI query parts
696 *uriQueryStartPtr++ = '\0';
697 SuccessOrExit(error = otCoapMessageAppendUriPathOptions(message, coapUri));
698 SuccessOrExit(error = otCoapMessageAppendUriQueryOptions(message, uriQueryStartPtr));
699 }
700
701 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
702 if (coapBlock)
703 {
704 if (coapBlockType == kBlockType1)
705 {
706 SuccessOrExit(error = otCoapMessageAppendBlock1Option(message, 0, true, coapBlockSize));
707 }
708 else
709 {
710 SuccessOrExit(error = otCoapMessageAppendBlock2Option(message, 0, false, coapBlockSize));
711 }
712 }
713 #endif
714
715 if (!aArgs[3].IsEmpty())
716 {
717 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
718 if (coapBlock)
719 {
720 SuccessOrExit(error = aArgs[3].ParseAsUint32(mBlockCount));
721 }
722 else
723 {
724 #endif
725 payloadLength = aArgs[3].GetLength();
726
727 if (payloadLength > 0)
728 {
729 SuccessOrExit(error = otCoapMessageSetPayloadMarker(message));
730 }
731 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
732 }
733 #endif
734 }
735
736 // Embed content into message if given
737 if (payloadLength > 0)
738 {
739 SuccessOrExit(error = otMessageAppend(message, aArgs[3].GetCString(), payloadLength));
740 }
741
742 ClearAllBytes(messageInfo);
743 messageInfo.mPeerAddr = coapDestinationIp;
744 messageInfo.mPeerPort = OT_DEFAULT_COAP_PORT;
745
746 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
747 if (aCoapObserve)
748 {
749 // Make a note of the message details for later so we can cancel it later.
750 memcpy(&mRequestAddr, &coapDestinationIp, sizeof(mRequestAddr));
751 mRequestTokenLength = otCoapMessageGetTokenLength(message);
752 memcpy(mRequestToken, otCoapMessageGetToken(message), mRequestTokenLength);
753 // Use `memcpy` instead of `strncpy` here because GCC will give warnings for `strncpy` when the dest's length is
754 // not bigger than the src's length.
755 memcpy(mRequestUri, coapUri, sizeof(mRequestUri) - 1);
756 }
757 #endif
758
759 if ((coapType == OT_COAP_TYPE_CONFIRMABLE) || (aCoapCode == OT_COAP_CODE_GET))
760 {
761 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
762 if (coapBlock)
763 {
764 if (aCoapCode == OT_COAP_CODE_PUT || aCoapCode == OT_COAP_CODE_POST)
765 {
766 SuccessOrExit(error = otCoapMessageSetPayloadMarker(message));
767 }
768 error = otCoapSendRequestBlockWiseWithParameters(GetInstancePtr(), message, &messageInfo,
769 &Coap::HandleResponse, this, GetRequestTxParameters(),
770 Coap::BlockwiseTransmitHook, Coap::BlockwiseReceiveHook);
771 }
772 else
773 {
774 #endif
775 error = otCoapSendRequestWithParameters(GetInstancePtr(), message, &messageInfo, &Coap::HandleResponse,
776 this, GetRequestTxParameters());
777 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
778 }
779 #endif
780 }
781 else
782 {
783 error = otCoapSendRequestWithParameters(GetInstancePtr(), message, &messageInfo, nullptr, nullptr,
784 GetResponseTxParameters());
785 }
786
787 exit:
788
789 if ((error != OT_ERROR_NONE) && (message != nullptr))
790 {
791 otMessageFree(message);
792 }
793
794 return error;
795 }
796
Process(Arg aArgs[])797 otError Coap::Process(Arg aArgs[])
798 {
799 #define CmdEntry(aCommandString) \
800 { \
801 aCommandString, &Coap::Process<Cmd(aCommandString)> \
802 }
803
804 static constexpr Command kCommands[] = {
805 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
806 CmdEntry("cancel"),
807 #endif
808 CmdEntry("delete"),
809 CmdEntry("get"),
810 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
811 CmdEntry("observe"),
812 #endif
813 CmdEntry("parameters"),
814 CmdEntry("post"),
815 CmdEntry("put"),
816 CmdEntry("resource"),
817 CmdEntry("set"),
818 CmdEntry("start"),
819 CmdEntry("stop"),
820 };
821
822 #undef CmdEntry
823
824 static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted");
825
826 otError error = OT_ERROR_INVALID_COMMAND;
827 const Command *command;
828
829 if (aArgs[0].IsEmpty() || (aArgs[0] == "help"))
830 {
831 OutputCommandTable(kCommands);
832 ExitNow(error = aArgs[0].IsEmpty() ? OT_ERROR_INVALID_ARGS : OT_ERROR_NONE);
833 }
834
835 command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
836 VerifyOrExit(command != nullptr);
837
838 error = (this->*command->mHandler)(aArgs + 1);
839
840 exit:
841 return error;
842 }
843
HandleRequest(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo)844 void Coap::HandleRequest(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
845 {
846 static_cast<Coap *>(aContext)->HandleRequest(aMessage, aMessageInfo);
847 }
848
HandleRequest(otMessage * aMessage,const otMessageInfo * aMessageInfo)849 void Coap::HandleRequest(otMessage *aMessage, const otMessageInfo *aMessageInfo)
850 {
851 otError error = OT_ERROR_NONE;
852 otMessage *responseMessage = nullptr;
853 otCoapCode responseCode = OT_COAP_CODE_EMPTY;
854 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
855 uint64_t observe = 0;
856 bool observePresent = false;
857 #endif
858 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
859 uint64_t blockValue = 0;
860 bool blockPresent = false;
861 #endif
862 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE || OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
863 otCoapOptionIterator iterator;
864 #endif
865
866 OutputFormat("coap request from ");
867 OutputIp6Address(aMessageInfo->mPeerAddr);
868 OutputFormat(" ");
869
870 switch (otCoapMessageGetCode(aMessage))
871 {
872 case OT_COAP_CODE_GET:
873 OutputFormat("GET");
874 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE || OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
875 SuccessOrExit(error = otCoapOptionIteratorInit(&iterator, aMessage));
876 #endif
877 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
878 if (otCoapOptionIteratorGetFirstOptionMatching(&iterator, OT_COAP_OPTION_OBSERVE) != nullptr)
879 {
880 SuccessOrExit(error = otCoapOptionIteratorGetOptionUintValue(&iterator, &observe));
881 observePresent = true;
882
883 OutputFormat(" OBS=");
884 OutputUint64(observe);
885 }
886 #endif
887 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
888 if (otCoapOptionIteratorGetFirstOptionMatching(&iterator, OT_COAP_OPTION_BLOCK2) != nullptr)
889 {
890 SuccessOrExit(error = otCoapOptionIteratorGetOptionUintValue(&iterator, &blockValue));
891 blockPresent = true;
892 }
893 #endif
894 break;
895
896 case OT_COAP_CODE_DELETE:
897 OutputFormat("DELETE");
898 break;
899
900 case OT_COAP_CODE_PUT:
901 OutputFormat("PUT");
902 break;
903
904 case OT_COAP_CODE_POST:
905 OutputFormat("POST");
906 break;
907
908 default:
909 OutputLine("Undefined");
910 ExitNow(error = OT_ERROR_PARSE);
911 }
912
913 PrintPayload(aMessage);
914
915 if (otCoapMessageGetType(aMessage) == OT_COAP_TYPE_CONFIRMABLE ||
916 otCoapMessageGetCode(aMessage) == OT_COAP_CODE_GET)
917 {
918 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
919 if (observePresent && (mSubscriberTokenLength > 0) && (observe == 0))
920 {
921 // There is already a subscriber
922 responseCode = OT_COAP_CODE_SERVICE_UNAVAILABLE;
923 }
924 else
925 #endif
926 if (otCoapMessageGetCode(aMessage) == OT_COAP_CODE_GET)
927 {
928 responseCode = OT_COAP_CODE_CONTENT;
929 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
930 if (observePresent)
931 {
932 if (observe == 0)
933 {
934 // New subscriber
935 OutputLine("Subscribing client");
936 mSubscriberSock.mAddress = aMessageInfo->mPeerAddr;
937 mSubscriberSock.mPort = aMessageInfo->mPeerPort;
938 mSubscriberTokenLength = otCoapMessageGetTokenLength(aMessage);
939 memcpy(mSubscriberToken, otCoapMessageGetToken(aMessage), mSubscriberTokenLength);
940
941 /*
942 * Implementer note.
943 *
944 * Here, we try to match a confirmable GET request with confirmable
945 * notifications, however this is not a requirement of RFC7641:
946 * the server can send notifications of either type regardless of
947 * what the client used to subscribe initially.
948 */
949 mSubscriberConfirmableNotifications = (otCoapMessageGetType(aMessage) == OT_COAP_TYPE_CONFIRMABLE);
950 }
951 else if (observe == 1)
952 {
953 // See if it matches our subscriber token
954 if ((otCoapMessageGetTokenLength(aMessage) == mSubscriberTokenLength) &&
955 (memcmp(otCoapMessageGetToken(aMessage), mSubscriberToken, mSubscriberTokenLength) == 0))
956 {
957 // Unsubscribe request
958 CancelSubscriber();
959 }
960 }
961 }
962 #endif // OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
963 }
964 else
965 {
966 responseCode = OT_COAP_CODE_CHANGED;
967 }
968
969 responseMessage = otCoapNewMessage(GetInstancePtr(), nullptr);
970 VerifyOrExit(responseMessage != nullptr, error = OT_ERROR_NO_BUFS);
971
972 SuccessOrExit(
973 error = otCoapMessageInitResponse(responseMessage, aMessage, OT_COAP_TYPE_ACKNOWLEDGMENT, responseCode));
974
975 if (responseCode == OT_COAP_CODE_CONTENT)
976 {
977 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
978 if (observePresent && (observe == 0))
979 {
980 SuccessOrExit(error = otCoapMessageAppendObserveOption(responseMessage, mObserveSerial++));
981 }
982 #endif
983 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
984 if (blockPresent)
985 {
986 SuccessOrExit(error = otCoapMessageAppendBlock2Option(responseMessage,
987 static_cast<uint32_t>(blockValue >> 4), true,
988 static_cast<otCoapBlockSzx>(blockValue & 0x7)));
989 SuccessOrExit(error = otCoapMessageSetPayloadMarker(responseMessage));
990 }
991 else
992 {
993 #endif
994 SuccessOrExit(error = otCoapMessageSetPayloadMarker(responseMessage));
995 SuccessOrExit(error = otMessageAppend(responseMessage, mResourceContent,
996 static_cast<uint16_t>(strlen(mResourceContent))));
997 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
998 }
999 #endif
1000 }
1001
1002 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
1003 if (blockPresent)
1004 {
1005 SuccessOrExit(error = otCoapSendResponseBlockWiseWithParameters(GetInstancePtr(), responseMessage,
1006 aMessageInfo, GetResponseTxParameters(),
1007 this, mResource.mTransmitHook));
1008 }
1009 else
1010 {
1011 #endif
1012 SuccessOrExit(error = otCoapSendResponseWithParameters(GetInstancePtr(), responseMessage, aMessageInfo,
1013 GetResponseTxParameters()));
1014 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
1015 }
1016 #endif
1017 }
1018
1019 exit:
1020
1021 if (error != OT_ERROR_NONE)
1022 {
1023 if (responseMessage != nullptr)
1024 {
1025 OutputLine("coap send response error %d: %s", error, otThreadErrorToString(error));
1026 otMessageFree(responseMessage);
1027 }
1028 }
1029 else if (responseCode >= OT_COAP_CODE_RESPONSE_MIN)
1030 {
1031 OutputLine("coap response sent");
1032 }
1033 }
1034
1035 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
HandleNotificationResponse(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo,otError aError)1036 void Coap::HandleNotificationResponse(void *aContext,
1037 otMessage *aMessage,
1038 const otMessageInfo *aMessageInfo,
1039 otError aError)
1040 {
1041 static_cast<Coap *>(aContext)->HandleNotificationResponse(aMessage, aMessageInfo, aError);
1042 }
1043
HandleNotificationResponse(otMessage * aMessage,const otMessageInfo * aMessageInfo,otError aError)1044 void Coap::HandleNotificationResponse(otMessage *aMessage, const otMessageInfo *aMessageInfo, otError aError)
1045 {
1046 OT_UNUSED_VARIABLE(aMessage);
1047
1048 switch (aError)
1049 {
1050 case OT_ERROR_NONE:
1051 if (aMessageInfo != nullptr)
1052 {
1053 OutputFormat("Received ACK in reply to notification from ");
1054 OutputIp6AddressLine(aMessageInfo->mPeerAddr);
1055 }
1056 break;
1057
1058 default:
1059 OutputLine("coap receive notification response error %d: %s", aError, otThreadErrorToString(aError));
1060 CancelSubscriber();
1061 break;
1062 }
1063 }
1064 #endif // OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
1065
HandleResponse(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo,otError aError)1066 void Coap::HandleResponse(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo, otError aError)
1067 {
1068 static_cast<Coap *>(aContext)->HandleResponse(aMessage, aMessageInfo, aError);
1069 }
1070
HandleResponse(otMessage * aMessage,const otMessageInfo * aMessageInfo,otError aError)1071 void Coap::HandleResponse(otMessage *aMessage, const otMessageInfo *aMessageInfo, otError aError)
1072 {
1073 if (aError != OT_ERROR_NONE)
1074 {
1075 OutputLine("coap receive response error %d: %s", aError, otThreadErrorToString(aError));
1076 }
1077 else if ((aMessageInfo != nullptr) && (aMessage != nullptr))
1078 {
1079 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
1080 otCoapOptionIterator iterator;
1081 #endif
1082
1083 OutputFormat("coap response from ");
1084 OutputIp6Address(aMessageInfo->mPeerAddr);
1085
1086 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
1087 if (otCoapOptionIteratorInit(&iterator, aMessage) == OT_ERROR_NONE)
1088 {
1089 const otCoapOption *observeOpt =
1090 otCoapOptionIteratorGetFirstOptionMatching(&iterator, OT_COAP_OPTION_OBSERVE);
1091
1092 if (observeOpt != nullptr)
1093 {
1094 uint64_t observeVal = 0;
1095 otError error = otCoapOptionIteratorGetOptionUintValue(&iterator, &observeVal);
1096
1097 if (error == OT_ERROR_NONE)
1098 {
1099 OutputFormat(" OBS=");
1100 OutputUint64(observeVal);
1101 }
1102 }
1103 }
1104 #endif
1105 PrintPayload(aMessage);
1106 }
1107 }
1108
1109 #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)1110 otError Coap::BlockwiseReceiveHook(void *aContext,
1111 const uint8_t *aBlock,
1112 uint32_t aPosition,
1113 uint16_t aBlockLength,
1114 bool aMore,
1115 uint32_t aTotalLength)
1116 {
1117 return static_cast<Coap *>(aContext)->BlockwiseReceiveHook(aBlock, aPosition, aBlockLength, aMore, aTotalLength);
1118 }
1119
BlockwiseReceiveHook(const uint8_t * aBlock,uint32_t aPosition,uint16_t aBlockLength,bool aMore,uint32_t aTotalLength)1120 otError Coap::BlockwiseReceiveHook(const uint8_t *aBlock,
1121 uint32_t aPosition,
1122 uint16_t aBlockLength,
1123 bool aMore,
1124 uint32_t aTotalLength)
1125 {
1126 OT_UNUSED_VARIABLE(aMore);
1127 OT_UNUSED_VARIABLE(aTotalLength);
1128
1129 OutputLine("received block: Num %i Len %i", aPosition / aBlockLength, aBlockLength);
1130
1131 for (uint16_t i = 0; i < aBlockLength / 16; i++)
1132 {
1133 OutputBytesLine(&aBlock[i * 16], 16);
1134 }
1135
1136 return OT_ERROR_NONE;
1137 }
1138
BlockwiseTransmitHook(void * aContext,uint8_t * aBlock,uint32_t aPosition,uint16_t * aBlockLength,bool * aMore)1139 otError Coap::BlockwiseTransmitHook(void *aContext,
1140 uint8_t *aBlock,
1141 uint32_t aPosition,
1142 uint16_t *aBlockLength,
1143 bool *aMore)
1144 {
1145 return static_cast<Coap *>(aContext)->BlockwiseTransmitHook(aBlock, aPosition, aBlockLength, aMore);
1146 }
1147
BlockwiseTransmitHook(uint8_t * aBlock,uint32_t aPosition,uint16_t * aBlockLength,bool * aMore)1148 otError Coap::BlockwiseTransmitHook(uint8_t *aBlock, uint32_t aPosition, uint16_t *aBlockLength, bool *aMore)
1149 {
1150 static uint32_t blockCount = 0;
1151 OT_UNUSED_VARIABLE(aPosition);
1152
1153 // Send a random payload
1154 otRandomNonCryptoFillBuffer(aBlock, *aBlockLength);
1155
1156 OutputLine("send block: Num %i Len %i", blockCount, *aBlockLength);
1157
1158 for (uint16_t i = 0; i < *aBlockLength / 16; i++)
1159 {
1160 OutputBytesLine(&aBlock[i * 16], 16);
1161 }
1162
1163 if (blockCount == mBlockCount - 1)
1164 {
1165 blockCount = 0;
1166 *aMore = false;
1167 }
1168 else
1169 {
1170 *aMore = true;
1171 blockCount++;
1172 }
1173
1174 return OT_ERROR_NONE;
1175 }
1176 #endif // OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
1177
1178 } // namespace Cli
1179 } // namespace ot
1180
1181 #endif // OPENTHREAD_CONFIG_COAP_API_ENABLE
1182