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 
588     // Default parameters
589     char         coapUri[kMaxUriLength] = "test";
590     otCoapType   coapType               = OT_COAP_TYPE_NON_CONFIRMABLE;
591     otIp6Address coapDestinationIp;
592 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
593     bool           coapBlock     = false;
594     otCoapBlockSzx coapBlockSize = OT_COAP_OPTION_BLOCK_SZX_16;
595     BlockType      coapBlockType = (aCoapCode == OT_COAP_CODE_GET) ? kBlockType2 : kBlockType1;
596 #endif
597 
598 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE && OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
599     if (aCoapObserve)
600     {
601         coapBlockType = kBlockType1;
602     }
603 #endif
604 
605     SuccessOrExit(error = aArgs[0].ParseAsIp6Address(coapDestinationIp));
606 
607     VerifyOrExit(!aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
608     VerifyOrExit(aArgs[1].GetLength() < sizeof(coapUri), error = OT_ERROR_INVALID_ARGS);
609     strcpy(coapUri, aArgs[1].GetCString());
610 
611     // CoAP-Type
612     if (!aArgs[2].IsEmpty())
613     {
614         if (aArgs[2] == "con")
615         {
616             coapType = OT_COAP_TYPE_CONFIRMABLE;
617         }
618 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
619         else if (aArgs[2] == "block-16")
620         {
621             coapType      = OT_COAP_TYPE_CONFIRMABLE;
622             coapBlock     = true;
623             coapBlockSize = OT_COAP_OPTION_BLOCK_SZX_16;
624         }
625         else if (aArgs[2] == "block-32")
626         {
627             coapType      = OT_COAP_TYPE_CONFIRMABLE;
628             coapBlock     = true;
629             coapBlockSize = OT_COAP_OPTION_BLOCK_SZX_32;
630         }
631         else if (aArgs[2] == "block-64")
632         {
633             coapType      = OT_COAP_TYPE_CONFIRMABLE;
634             coapBlock     = true;
635             coapBlockSize = OT_COAP_OPTION_BLOCK_SZX_64;
636         }
637         else if (aArgs[2] == "block-128")
638         {
639             coapType      = OT_COAP_TYPE_CONFIRMABLE;
640             coapBlock     = true;
641             coapBlockSize = OT_COAP_OPTION_BLOCK_SZX_128;
642         }
643         else if (aArgs[2] == "block-256")
644         {
645             coapType      = OT_COAP_TYPE_CONFIRMABLE;
646             coapBlock     = true;
647             coapBlockSize = OT_COAP_OPTION_BLOCK_SZX_256;
648         }
649         else if (aArgs[2] == "block-512")
650         {
651             coapType      = OT_COAP_TYPE_CONFIRMABLE;
652             coapBlock     = true;
653             coapBlockSize = OT_COAP_OPTION_BLOCK_SZX_512;
654         }
655         else if (aArgs[2] == "block-1024")
656         {
657             coapType      = OT_COAP_TYPE_CONFIRMABLE;
658             coapBlock     = true;
659             coapBlockSize = OT_COAP_OPTION_BLOCK_SZX_1024;
660         }
661 #endif // OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
662     }
663 
664 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
665     if (aCoapObserve && mRequestTokenLength)
666     {
667         // New observe request, cancel any existing observation
668         SuccessOrExit(error = CancelResourceSubscription());
669     }
670 #endif
671 
672     message = otCoapNewMessage(GetInstancePtr(), nullptr);
673     VerifyOrExit(message != nullptr, error = OT_ERROR_NO_BUFS);
674 
675     otCoapMessageInit(message, coapType, aCoapCode);
676     otCoapMessageGenerateToken(message, OT_COAP_DEFAULT_TOKEN_LENGTH);
677 
678 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
679     if (aCoapObserve)
680     {
681         SuccessOrExit(error = otCoapMessageAppendObserveOption(message, 0));
682     }
683 #endif
684 
685     SuccessOrExit(error = otCoapMessageAppendUriPathOptions(message, coapUri));
686 
687 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
688     if (coapBlock)
689     {
690         if (coapBlockType == kBlockType1)
691         {
692             SuccessOrExit(error = otCoapMessageAppendBlock1Option(message, 0, true, coapBlockSize));
693         }
694         else
695         {
696             SuccessOrExit(error = otCoapMessageAppendBlock2Option(message, 0, false, coapBlockSize));
697         }
698     }
699 #endif
700 
701     if (!aArgs[3].IsEmpty())
702     {
703 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
704         if (coapBlock)
705         {
706             SuccessOrExit(error = aArgs[3].ParseAsUint32(mBlockCount));
707         }
708         else
709         {
710 #endif
711             payloadLength = aArgs[3].GetLength();
712 
713             if (payloadLength > 0)
714             {
715                 SuccessOrExit(error = otCoapMessageSetPayloadMarker(message));
716             }
717 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
718         }
719 #endif
720     }
721 
722     // Embed content into message if given
723     if (payloadLength > 0)
724     {
725         SuccessOrExit(error = otMessageAppend(message, aArgs[3].GetCString(), payloadLength));
726     }
727 
728     ClearAllBytes(messageInfo);
729     messageInfo.mPeerAddr = coapDestinationIp;
730     messageInfo.mPeerPort = OT_DEFAULT_COAP_PORT;
731 
732 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
733     if (aCoapObserve)
734     {
735         // Make a note of the message details for later so we can cancel it later.
736         memcpy(&mRequestAddr, &coapDestinationIp, sizeof(mRequestAddr));
737         mRequestTokenLength = otCoapMessageGetTokenLength(message);
738         memcpy(mRequestToken, otCoapMessageGetToken(message), mRequestTokenLength);
739         // Use `memcpy` instead of `strncpy` here because GCC will give warnings for `strncpy` when the dest's length is
740         // not bigger than the src's length.
741         memcpy(mRequestUri, coapUri, sizeof(mRequestUri) - 1);
742     }
743 #endif
744 
745     if ((coapType == OT_COAP_TYPE_CONFIRMABLE) || (aCoapCode == OT_COAP_CODE_GET))
746     {
747 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
748         if (coapBlock)
749         {
750             if (aCoapCode == OT_COAP_CODE_PUT || aCoapCode == OT_COAP_CODE_POST)
751             {
752                 SuccessOrExit(error = otCoapMessageSetPayloadMarker(message));
753             }
754             error = otCoapSendRequestBlockWiseWithParameters(GetInstancePtr(), message, &messageInfo,
755                                                              &Coap::HandleResponse, this, GetRequestTxParameters(),
756                                                              Coap::BlockwiseTransmitHook, Coap::BlockwiseReceiveHook);
757         }
758         else
759         {
760 #endif
761             error = otCoapSendRequestWithParameters(GetInstancePtr(), message, &messageInfo, &Coap::HandleResponse,
762                                                     this, GetRequestTxParameters());
763 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
764         }
765 #endif
766     }
767     else
768     {
769         error = otCoapSendRequestWithParameters(GetInstancePtr(), message, &messageInfo, nullptr, nullptr,
770                                                 GetResponseTxParameters());
771     }
772 
773 exit:
774 
775     if ((error != OT_ERROR_NONE) && (message != nullptr))
776     {
777         otMessageFree(message);
778     }
779 
780     return error;
781 }
782 
Process(Arg aArgs[])783 otError Coap::Process(Arg aArgs[])
784 {
785 #define CmdEntry(aCommandString)                            \
786     {                                                       \
787         aCommandString, &Coap::Process<Cmd(aCommandString)> \
788     }
789 
790     static constexpr Command kCommands[] = {
791 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
792         CmdEntry("cancel"),
793 #endif
794         CmdEntry("delete"),
795         CmdEntry("get"),
796 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
797         CmdEntry("observe"),
798 #endif
799         CmdEntry("parameters"),
800         CmdEntry("post"),
801         CmdEntry("put"),
802         CmdEntry("resource"),
803         CmdEntry("set"),
804         CmdEntry("start"),
805         CmdEntry("stop"),
806     };
807 
808 #undef CmdEntry
809 
810     static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted");
811 
812     otError        error = OT_ERROR_INVALID_COMMAND;
813     const Command *command;
814 
815     if (aArgs[0].IsEmpty() || (aArgs[0] == "help"))
816     {
817         OutputCommandTable(kCommands);
818         ExitNow(error = aArgs[0].IsEmpty() ? OT_ERROR_INVALID_ARGS : OT_ERROR_NONE);
819     }
820 
821     command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
822     VerifyOrExit(command != nullptr);
823 
824     error = (this->*command->mHandler)(aArgs + 1);
825 
826 exit:
827     return error;
828 }
829 
HandleRequest(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo)830 void Coap::HandleRequest(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
831 {
832     static_cast<Coap *>(aContext)->HandleRequest(aMessage, aMessageInfo);
833 }
834 
HandleRequest(otMessage * aMessage,const otMessageInfo * aMessageInfo)835 void Coap::HandleRequest(otMessage *aMessage, const otMessageInfo *aMessageInfo)
836 {
837     otError    error           = OT_ERROR_NONE;
838     otMessage *responseMessage = nullptr;
839     otCoapCode responseCode    = OT_COAP_CODE_EMPTY;
840 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
841     uint64_t observe        = 0;
842     bool     observePresent = false;
843 #endif
844 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
845     uint64_t blockValue   = 0;
846     bool     blockPresent = false;
847 #endif
848 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE || OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
849     otCoapOptionIterator iterator;
850 #endif
851 
852     OutputFormat("coap request from ");
853     OutputIp6Address(aMessageInfo->mPeerAddr);
854     OutputFormat(" ");
855 
856     switch (otCoapMessageGetCode(aMessage))
857     {
858     case OT_COAP_CODE_GET:
859         OutputFormat("GET");
860 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE || OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
861         SuccessOrExit(error = otCoapOptionIteratorInit(&iterator, aMessage));
862 #endif
863 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
864         if (otCoapOptionIteratorGetFirstOptionMatching(&iterator, OT_COAP_OPTION_OBSERVE) != nullptr)
865         {
866             SuccessOrExit(error = otCoapOptionIteratorGetOptionUintValue(&iterator, &observe));
867             observePresent = true;
868 
869             OutputFormat(" OBS=");
870             OutputUint64(observe);
871         }
872 #endif
873 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
874         if (otCoapOptionIteratorGetFirstOptionMatching(&iterator, OT_COAP_OPTION_BLOCK2) != nullptr)
875         {
876             SuccessOrExit(error = otCoapOptionIteratorGetOptionUintValue(&iterator, &blockValue));
877             blockPresent = true;
878         }
879 #endif
880         break;
881 
882     case OT_COAP_CODE_DELETE:
883         OutputFormat("DELETE");
884         break;
885 
886     case OT_COAP_CODE_PUT:
887         OutputFormat("PUT");
888         break;
889 
890     case OT_COAP_CODE_POST:
891         OutputFormat("POST");
892         break;
893 
894     default:
895         OutputLine("Undefined");
896         ExitNow(error = OT_ERROR_PARSE);
897     }
898 
899     PrintPayload(aMessage);
900 
901     if (otCoapMessageGetType(aMessage) == OT_COAP_TYPE_CONFIRMABLE ||
902         otCoapMessageGetCode(aMessage) == OT_COAP_CODE_GET)
903     {
904 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
905         if (observePresent && (mSubscriberTokenLength > 0) && (observe == 0))
906         {
907             // There is already a subscriber
908             responseCode = OT_COAP_CODE_SERVICE_UNAVAILABLE;
909         }
910         else
911 #endif
912             if (otCoapMessageGetCode(aMessage) == OT_COAP_CODE_GET)
913         {
914             responseCode = OT_COAP_CODE_CONTENT;
915 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
916             if (observePresent)
917             {
918                 if (observe == 0)
919                 {
920                     // New subscriber
921                     OutputLine("Subscribing client");
922                     mSubscriberSock.mAddress = aMessageInfo->mPeerAddr;
923                     mSubscriberSock.mPort    = aMessageInfo->mPeerPort;
924                     mSubscriberTokenLength   = otCoapMessageGetTokenLength(aMessage);
925                     memcpy(mSubscriberToken, otCoapMessageGetToken(aMessage), mSubscriberTokenLength);
926 
927                     /*
928                      * Implementer note.
929                      *
930                      * Here, we try to match a confirmable GET request with confirmable
931                      * notifications, however this is not a requirement of RFC7641:
932                      * the server can send notifications of either type regardless of
933                      * what the client used to subscribe initially.
934                      */
935                     mSubscriberConfirmableNotifications = (otCoapMessageGetType(aMessage) == OT_COAP_TYPE_CONFIRMABLE);
936                 }
937                 else if (observe == 1)
938                 {
939                     // See if it matches our subscriber token
940                     if ((otCoapMessageGetTokenLength(aMessage) == mSubscriberTokenLength) &&
941                         (memcmp(otCoapMessageGetToken(aMessage), mSubscriberToken, mSubscriberTokenLength) == 0))
942                     {
943                         // Unsubscribe request
944                         CancelSubscriber();
945                     }
946                 }
947             }
948 #endif // OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
949         }
950         else
951         {
952             responseCode = OT_COAP_CODE_CHANGED;
953         }
954 
955         responseMessage = otCoapNewMessage(GetInstancePtr(), nullptr);
956         VerifyOrExit(responseMessage != nullptr, error = OT_ERROR_NO_BUFS);
957 
958         SuccessOrExit(
959             error = otCoapMessageInitResponse(responseMessage, aMessage, OT_COAP_TYPE_ACKNOWLEDGMENT, responseCode));
960 
961         if (responseCode == OT_COAP_CODE_CONTENT)
962         {
963 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
964             if (observePresent && (observe == 0))
965             {
966                 SuccessOrExit(error = otCoapMessageAppendObserveOption(responseMessage, mObserveSerial++));
967             }
968 #endif
969 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
970             if (blockPresent)
971             {
972                 SuccessOrExit(error = otCoapMessageAppendBlock2Option(responseMessage,
973                                                                       static_cast<uint32_t>(blockValue >> 4), true,
974                                                                       static_cast<otCoapBlockSzx>(blockValue & 0x7)));
975                 SuccessOrExit(error = otCoapMessageSetPayloadMarker(responseMessage));
976             }
977             else
978             {
979 #endif
980                 SuccessOrExit(error = otCoapMessageSetPayloadMarker(responseMessage));
981                 SuccessOrExit(error = otMessageAppend(responseMessage, mResourceContent,
982                                                       static_cast<uint16_t>(strlen(mResourceContent))));
983 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
984             }
985 #endif
986         }
987 
988 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
989         if (blockPresent)
990         {
991             SuccessOrExit(error = otCoapSendResponseBlockWiseWithParameters(GetInstancePtr(), responseMessage,
992                                                                             aMessageInfo, GetResponseTxParameters(),
993                                                                             this, mResource.mTransmitHook));
994         }
995         else
996         {
997 #endif
998             SuccessOrExit(error = otCoapSendResponseWithParameters(GetInstancePtr(), responseMessage, aMessageInfo,
999                                                                    GetResponseTxParameters()));
1000 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
1001         }
1002 #endif
1003     }
1004 
1005 exit:
1006 
1007     if (error != OT_ERROR_NONE)
1008     {
1009         if (responseMessage != nullptr)
1010         {
1011             OutputLine("coap send response error %d: %s", error, otThreadErrorToString(error));
1012             otMessageFree(responseMessage);
1013         }
1014     }
1015     else if (responseCode >= OT_COAP_CODE_RESPONSE_MIN)
1016     {
1017         OutputLine("coap response sent");
1018     }
1019 }
1020 
1021 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
HandleNotificationResponse(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo,otError aError)1022 void Coap::HandleNotificationResponse(void                *aContext,
1023                                       otMessage           *aMessage,
1024                                       const otMessageInfo *aMessageInfo,
1025                                       otError              aError)
1026 {
1027     static_cast<Coap *>(aContext)->HandleNotificationResponse(aMessage, aMessageInfo, aError);
1028 }
1029 
HandleNotificationResponse(otMessage * aMessage,const otMessageInfo * aMessageInfo,otError aError)1030 void Coap::HandleNotificationResponse(otMessage *aMessage, const otMessageInfo *aMessageInfo, otError aError)
1031 {
1032     OT_UNUSED_VARIABLE(aMessage);
1033 
1034     switch (aError)
1035     {
1036     case OT_ERROR_NONE:
1037         if (aMessageInfo != nullptr)
1038         {
1039             OutputFormat("Received ACK in reply to notification from ");
1040             OutputIp6AddressLine(aMessageInfo->mPeerAddr);
1041         }
1042         break;
1043 
1044     default:
1045         OutputLine("coap receive notification response error %d: %s", aError, otThreadErrorToString(aError));
1046         CancelSubscriber();
1047         break;
1048     }
1049 }
1050 #endif // OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
1051 
HandleResponse(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo,otError aError)1052 void Coap::HandleResponse(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo, otError aError)
1053 {
1054     static_cast<Coap *>(aContext)->HandleResponse(aMessage, aMessageInfo, aError);
1055 }
1056 
HandleResponse(otMessage * aMessage,const otMessageInfo * aMessageInfo,otError aError)1057 void Coap::HandleResponse(otMessage *aMessage, const otMessageInfo *aMessageInfo, otError aError)
1058 {
1059     if (aError != OT_ERROR_NONE)
1060     {
1061         OutputLine("coap receive response error %d: %s", aError, otThreadErrorToString(aError));
1062     }
1063     else if ((aMessageInfo != nullptr) && (aMessage != nullptr))
1064     {
1065 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
1066         otCoapOptionIterator iterator;
1067 #endif
1068 
1069         OutputFormat("coap response from ");
1070         OutputIp6Address(aMessageInfo->mPeerAddr);
1071 
1072 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
1073         if (otCoapOptionIteratorInit(&iterator, aMessage) == OT_ERROR_NONE)
1074         {
1075             const otCoapOption *observeOpt =
1076                 otCoapOptionIteratorGetFirstOptionMatching(&iterator, OT_COAP_OPTION_OBSERVE);
1077 
1078             if (observeOpt != nullptr)
1079             {
1080                 uint64_t observeVal = 0;
1081                 otError  error      = otCoapOptionIteratorGetOptionUintValue(&iterator, &observeVal);
1082 
1083                 if (error == OT_ERROR_NONE)
1084                 {
1085                     OutputFormat(" OBS=");
1086                     OutputUint64(observeVal);
1087                 }
1088             }
1089         }
1090 #endif
1091         PrintPayload(aMessage);
1092     }
1093 }
1094 
1095 #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)1096 otError Coap::BlockwiseReceiveHook(void          *aContext,
1097                                    const uint8_t *aBlock,
1098                                    uint32_t       aPosition,
1099                                    uint16_t       aBlockLength,
1100                                    bool           aMore,
1101                                    uint32_t       aTotalLength)
1102 {
1103     return static_cast<Coap *>(aContext)->BlockwiseReceiveHook(aBlock, aPosition, aBlockLength, aMore, aTotalLength);
1104 }
1105 
BlockwiseReceiveHook(const uint8_t * aBlock,uint32_t aPosition,uint16_t aBlockLength,bool aMore,uint32_t aTotalLength)1106 otError Coap::BlockwiseReceiveHook(const uint8_t *aBlock,
1107                                    uint32_t       aPosition,
1108                                    uint16_t       aBlockLength,
1109                                    bool           aMore,
1110                                    uint32_t       aTotalLength)
1111 {
1112     OT_UNUSED_VARIABLE(aMore);
1113     OT_UNUSED_VARIABLE(aTotalLength);
1114 
1115     OutputLine("received block: Num %i Len %i", aPosition / aBlockLength, aBlockLength);
1116 
1117     for (uint16_t i = 0; i < aBlockLength / 16; i++)
1118     {
1119         OutputBytesLine(&aBlock[i * 16], 16);
1120     }
1121 
1122     return OT_ERROR_NONE;
1123 }
1124 
BlockwiseTransmitHook(void * aContext,uint8_t * aBlock,uint32_t aPosition,uint16_t * aBlockLength,bool * aMore)1125 otError Coap::BlockwiseTransmitHook(void     *aContext,
1126                                     uint8_t  *aBlock,
1127                                     uint32_t  aPosition,
1128                                     uint16_t *aBlockLength,
1129                                     bool     *aMore)
1130 {
1131     return static_cast<Coap *>(aContext)->BlockwiseTransmitHook(aBlock, aPosition, aBlockLength, aMore);
1132 }
1133 
BlockwiseTransmitHook(uint8_t * aBlock,uint32_t aPosition,uint16_t * aBlockLength,bool * aMore)1134 otError Coap::BlockwiseTransmitHook(uint8_t *aBlock, uint32_t aPosition, uint16_t *aBlockLength, bool *aMore)
1135 {
1136     static uint32_t blockCount = 0;
1137     OT_UNUSED_VARIABLE(aPosition);
1138 
1139     // Send a random payload
1140     otRandomNonCryptoFillBuffer(aBlock, *aBlockLength);
1141 
1142     OutputLine("send block: Num %i Len %i", blockCount, *aBlockLength);
1143 
1144     for (uint16_t i = 0; i < *aBlockLength / 16; i++)
1145     {
1146         OutputBytesLine(&aBlock[i * 16], 16);
1147     }
1148 
1149     if (blockCount == mBlockCount - 1)
1150     {
1151         blockCount = 0;
1152         *aMore     = false;
1153     }
1154     else
1155     {
1156         *aMore = true;
1157         blockCount++;
1158     }
1159 
1160     return OT_ERROR_NONE;
1161 }
1162 #endif // OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
1163 
1164 } // namespace Cli
1165 } // namespace ot
1166 
1167 #endif // OPENTHREAD_CONFIG_COAP_API_ENABLE
1168