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