Coverage Report

Created: 2025-10-28 06:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openthread/src/cli/cli_coap.cpp
Line
Count
Source
1
/*
2
 *  Copyright (c) 2017, The OpenThread Authors.
3
 *  All rights reserved.
4
 *
5
 *  Redistribution and use in source and binary forms, with or without
6
 *  modification, are permitted provided that the following conditions are met:
7
 *  1. Redistributions of source code must retain the above copyright
8
 *     notice, this list of conditions and the following disclaimer.
9
 *  2. Redistributions in binary form must reproduce the above copyright
10
 *     notice, this list of conditions and the following disclaimer in the
11
 *     documentation and/or other materials provided with the distribution.
12
 *  3. Neither the name of the copyright holder nor the
13
 *     names of its contributors may be used to endorse or promote products
14
 *     derived from this software without specific prior written permission.
15
 *
16
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19
 *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20
 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23
 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24
 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26
 *  POSSIBILITY OF SUCH DAMAGE.
27
 */
28
29
/**
30
 * @file
31
 *   This file implements a simple CLI for the CoAP service.
32
 */
33
34
#include "cli_coap.hpp"
35
36
#if OPENTHREAD_CONFIG_COAP_API_ENABLE
37
38
#include <openthread/random_noncrypto.h>
39
40
#include <ctype.h>
41
42
#include "cli/cli.hpp"
43
44
namespace ot {
45
namespace Cli {
46
47
Coap::Coap(otInstance *aInstance, OutputImplementer &aOutputImplementer)
48
5.52k
    : Utils(aInstance, aOutputImplementer)
49
5.52k
    , mUseDefaultRequestTxParameters(true)
50
5.52k
    , 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
5.52k
{
61
5.52k
    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
5.52k
    ClearAllBytes(mUriPath);
70
5.52k
    strncpy(mResourceContent, "0", sizeof(mResourceContent));
71
5.52k
    mResourceContent[sizeof(mResourceContent) - 1] = '\0';
72
5.52k
}
73
74
#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
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
111
void Coap::CancelSubscriber(void)
112
{
113
    ClearAllBytes(mSubscriberSock);
114
    mSubscriberTokenLength = 0;
115
}
116
#endif // OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
117
118
void Coap::PrintPayload(otMessage *aMessage)
119
0
{
120
0
    uint8_t  buf[kMaxBufferSize];
121
0
    uint16_t bytesToPrint;
122
0
    uint16_t bytesPrinted = 0;
123
0
    uint16_t length       = otMessageGetLength(aMessage) - otMessageGetOffset(aMessage);
124
125
0
    if (length > 0)
126
0
    {
127
0
        OutputFormat(" with payload: ");
128
129
0
        while (length > 0)
130
0
        {
131
0
            bytesToPrint = Min(length, static_cast<uint16_t>(sizeof(buf)));
132
0
            otMessageRead(aMessage, otMessageGetOffset(aMessage) + bytesPrinted, buf, bytesToPrint);
133
134
0
            OutputBytes(buf, static_cast<uint8_t>(bytesToPrint));
135
136
0
            length -= bytesToPrint;
137
0
            bytesPrinted += bytesToPrint;
138
0
        }
139
0
    }
140
141
0
    OutputNewLine();
142
0
}
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
 */
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
 */
181
template <> otError Coap::Process<Cmd("resource")>(Arg aArgs[])
182
7
{
183
7
    otError error = OT_ERROR_NONE;
184
185
7
    if (!aArgs[0].IsEmpty())
186
6
    {
187
6
        VerifyOrExit(aArgs[0].GetLength() < kMaxUriLength, error = OT_ERROR_INVALID_ARGS);
188
189
4
        mResource.mUriPath = mUriPath;
190
4
        mResource.mContext = this;
191
4
        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
4
        strncpy(mUriPath, aArgs[0].GetCString(), sizeof(mUriPath) - 1);
204
205
#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
206
        otCoapAddBlockWiseResource(GetInstancePtr(), &mResource);
207
#else
208
4
        otCoapAddResource(GetInstancePtr(), &mResource);
209
4
#endif
210
4
    }
211
1
    else
212
1
    {
213
1
        OutputLine("%s", mResource.mUriPath != nullptr ? mResource.mUriPath : "");
214
1
    }
215
216
7
exit:
217
7
    return error;
218
7
}
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
 */
234
template <> otError Coap::Process<Cmd("set")>(Arg aArgs[])
235
4
{
236
#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
237
    otMessage    *notificationMessage = nullptr;
238
    otMessageInfo messageInfo;
239
#endif
240
4
    otError error = OT_ERROR_NONE;
241
242
4
    if (!aArgs[0].IsEmpty())
243
3
    {
244
3
        VerifyOrExit(aArgs[0].GetLength() < sizeof(mResourceContent), error = OT_ERROR_INVALID_ARGS);
245
2
        strncpy(mResourceContent, aArgs[0].GetCString(), sizeof(mResourceContent));
246
2
        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
2
    }
278
1
    else
279
1
    {
280
1
        OutputLine("%s", mResourceContent);
281
1
    }
282
283
4
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
4
    return error;
293
4
}
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
 */
305
template <> otError Coap::Process<Cmd("start")>(Arg aArgs[])
306
1
{
307
1
    OT_UNUSED_VARIABLE(aArgs);
308
309
1
    return otCoapStart(GetInstancePtr(), OT_DEFAULT_COAP_PORT);
310
1
}
311
312
/**
313
 * @cli coap stop
314
 * @code
315
 * coap stop
316
 * Done
317
 * @endcode
318
 * @par api_copy
319
 * #otCoapStop
320
 */
321
template <> otError Coap::Process<Cmd("stop")>(Arg aArgs[])
322
1
{
323
1
    OT_UNUSED_VARIABLE(aArgs);
324
325
#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
326
    otCoapRemoveBlockWiseResource(GetInstancePtr(), &mResource);
327
#else
328
1
    otCoapRemoveResource(GetInstancePtr(), &mResource);
329
1
#endif
330
331
1
    return otCoapStop(GetInstancePtr());
332
1
}
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
 */
381
template <> otError Coap::Process<Cmd("parameters")>(Arg aArgs[])
382
0
{
383
0
    otError             error = OT_ERROR_NONE;
384
0
    bool               *defaultTxParameters;
385
0
    otCoapTxParameters *txParameters;
386
387
0
    if (aArgs[0] == "request")
388
0
    {
389
0
        txParameters        = &mRequestTxParameters;
390
0
        defaultTxParameters = &mUseDefaultRequestTxParameters;
391
0
    }
392
0
    else if (aArgs[0] == "response")
393
0
    {
394
0
        txParameters        = &mResponseTxParameters;
395
0
        defaultTxParameters = &mUseDefaultResponseTxParameters;
396
0
    }
397
0
    else
398
0
    {
399
0
        ExitNow(error = OT_ERROR_INVALID_ARGS);
400
0
    }
401
402
0
    if (!aArgs[1].IsEmpty())
403
0
    {
404
0
        if (aArgs[1] == "default")
405
0
        {
406
0
            *defaultTxParameters = true;
407
0
        }
408
0
        else
409
0
        {
410
0
            SuccessOrExit(error = aArgs[1].ParseAsUint32(txParameters->mAckTimeout));
411
0
            SuccessOrExit(error = aArgs[2].ParseAsUint8(txParameters->mAckRandomFactorNumerator));
412
0
            SuccessOrExit(error = aArgs[3].ParseAsUint8(txParameters->mAckRandomFactorDenominator));
413
0
            SuccessOrExit(error = aArgs[4].ParseAsUint8(txParameters->mMaxRetransmit));
414
415
0
            VerifyOrExit(txParameters->mAckRandomFactorNumerator > txParameters->mAckRandomFactorDenominator,
416
0
                         error = OT_ERROR_INVALID_ARGS);
417
418
0
            *defaultTxParameters = false;
419
0
        }
420
0
    }
421
422
0
    OutputLine("Transmission parameters for %s:", aArgs[0].GetCString());
423
424
0
    if (*defaultTxParameters)
425
0
    {
426
0
        OutputLine("default");
427
0
    }
428
0
    else
429
0
    {
430
0
        OutputLine("ACK_TIMEOUT=%lu ms, ACK_RANDOM_FACTOR=%u/%u, MAX_RETRANSMIT=%u", ToUlong(txParameters->mAckTimeout),
431
0
                   txParameters->mAckRandomFactorNumerator, txParameters->mAckRandomFactorDenominator,
432
0
                   txParameters->mMaxRetransmit);
433
0
    }
434
435
0
exit:
436
0
    return error;
437
0
}
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
 */
462
42
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
 */
497
1
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
 */
532
2
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
 */
550
1
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
 */
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
578
otError Coap::ProcessRequest(Arg aArgs[], otCoapCode aCoapCode, bool aCoapObserve)
579
#else
580
otError Coap::ProcessRequest(Arg aArgs[], otCoapCode aCoapCode)
581
#endif
582
46
{
583
46
    otError       error   = OT_ERROR_NONE;
584
46
    otMessage    *message = nullptr;
585
46
    otMessageInfo messageInfo;
586
46
    uint16_t      payloadLength    = 0;
587
46
    char         *uriQueryStartPtr = nullptr;
588
589
    // Default parameters
590
46
    char         coapUri[kMaxUriLength] = "test";
591
46
    otCoapType   coapType               = OT_COAP_TYPE_NON_CONFIRMABLE;
592
46
    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
46
    SuccessOrExit(error = aArgs[0].ParseAsIp6Address(coapDestinationIp));
607
608
39
    VerifyOrExit(!aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
609
38
    VerifyOrExit(aArgs[1].GetLength() < sizeof(coapUri), error = OT_ERROR_INVALID_ARGS);
610
37
    strcpy(coapUri, aArgs[1].GetCString());
611
612
    // CoAP-Type
613
37
    if (!aArgs[2].IsEmpty())
614
20
    {
615
20
        if (aArgs[2] == "con")
616
1
        {
617
1
            coapType = OT_COAP_TYPE_CONFIRMABLE;
618
1
        }
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
20
    }
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
37
    message = otCoapNewMessage(GetInstancePtr(), nullptr);
674
37
    VerifyOrExit(message != nullptr, error = OT_ERROR_NO_BUFS);
675
676
37
    otCoapMessageInit(message, coapType, aCoapCode);
677
37
    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
37
    uriQueryStartPtr = const_cast<char *>(StringFind(coapUri, '?'));
687
688
37
    if (uriQueryStartPtr == nullptr)
689
28
    {
690
        // "?" doesn't present in URI --> contains only URI path parts
691
28
        SuccessOrExit(error = otCoapMessageAppendUriPathOptions(message, coapUri));
692
28
    }
693
9
    else
694
9
    {
695
        // "?" presents in URI --> contains URI path AND URI query parts
696
9
        *uriQueryStartPtr++ = '\0';
697
9
        SuccessOrExit(error = otCoapMessageAppendUriPathOptions(message, coapUri));
698
9
        SuccessOrExit(error = otCoapMessageAppendUriQueryOptions(message, uriQueryStartPtr));
699
9
    }
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
37
    if (!aArgs[3].IsEmpty())
716
18
    {
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
18
            payloadLength = aArgs[3].GetLength();
726
727
18
            if (payloadLength > 0)
728
18
            {
729
18
                SuccessOrExit(error = otCoapMessageSetPayloadMarker(message));
730
18
            }
731
#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
732
        }
733
#endif
734
18
    }
735
736
    // Embed content into message if given
737
37
    if (payloadLength > 0)
738
18
    {
739
18
        SuccessOrExit(error = otMessageAppend(message, aArgs[3].GetCString(), payloadLength));
740
18
    }
741
742
37
    ClearAllBytes(messageInfo);
743
37
    messageInfo.mPeerAddr = coapDestinationIp;
744
37
    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
37
    if ((coapType == OT_COAP_TYPE_CONFIRMABLE) || (aCoapCode == OT_COAP_CODE_GET))
760
36
    {
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
36
            error = otCoapSendRequestWithParameters(GetInstancePtr(), message, &messageInfo, &Coap::HandleResponse,
776
36
                                                    this, GetRequestTxParameters());
777
#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
778
        }
779
#endif
780
36
    }
781
1
    else
782
1
    {
783
1
        error = otCoapSendRequestWithParameters(GetInstancePtr(), message, &messageInfo, nullptr, nullptr,
784
1
                                                GetResponseTxParameters());
785
1
    }
786
787
46
exit:
788
789
46
    if ((error != OT_ERROR_NONE) && (message != nullptr))
790
37
    {
791
37
        otMessageFree(message);
792
37
    }
793
794
46
    return error;
795
37
}
796
797
otError Coap::Process(Arg aArgs[])
798
62
{
799
558
#define CmdEntry(aCommandString) {aCommandString, &Coap::Process<Cmd(aCommandString)>}
800
801
62
    static constexpr Command kCommands[] = {
802
#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
803
        CmdEntry("cancel"),
804
#endif
805
62
        CmdEntry("delete"),     CmdEntry("get"),
806
#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
807
        CmdEntry("observe"),
808
#endif
809
62
        CmdEntry("parameters"), CmdEntry("post"),  CmdEntry("put"),  CmdEntry("resource"),
810
62
        CmdEntry("set"),        CmdEntry("start"), CmdEntry("stop"),
811
62
    };
812
813
62
#undef CmdEntry
814
815
62
    static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted");
816
817
62
    otError        error = OT_ERROR_INVALID_COMMAND;
818
62
    const Command *command;
819
820
62
    if (aArgs[0].IsEmpty() || (aArgs[0] == "help"))
821
1
    {
822
1
        OutputCommandTable(kCommands);
823
1
        ExitNow(error = aArgs[0].IsEmpty() ? OT_ERROR_INVALID_ARGS : OT_ERROR_NONE);
824
1
    }
825
826
61
    command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
827
61
    VerifyOrExit(command != nullptr);
828
829
59
    error = (this->*command->mHandler)(aArgs + 1);
830
831
62
exit:
832
62
    return error;
833
59
}
834
835
void Coap::HandleRequest(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
836
0
{
837
0
    static_cast<Coap *>(aContext)->HandleRequest(aMessage, aMessageInfo);
838
0
}
839
840
void Coap::HandleRequest(otMessage *aMessage, const otMessageInfo *aMessageInfo)
841
0
{
842
0
    otError    error           = OT_ERROR_NONE;
843
0
    otMessage *responseMessage = nullptr;
844
0
    otCoapCode responseCode    = OT_COAP_CODE_EMPTY;
845
#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
846
    uint64_t observe        = 0;
847
    bool     observePresent = false;
848
#endif
849
#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
850
    uint64_t blockValue   = 0;
851
    bool     blockPresent = false;
852
#endif
853
#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE || OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
854
    otCoapOptionIterator iterator;
855
#endif
856
857
0
    OutputFormat("coap request from ");
858
0
    OutputIp6Address(aMessageInfo->mPeerAddr);
859
0
    OutputFormat(" ");
860
861
0
    switch (otCoapMessageGetCode(aMessage))
862
0
    {
863
0
    case OT_COAP_CODE_GET:
864
0
        OutputFormat("GET");
865
#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE || OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
866
        SuccessOrExit(error = otCoapOptionIteratorInit(&iterator, aMessage));
867
#endif
868
#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
869
        if (otCoapOptionIteratorGetFirstOptionMatching(&iterator, OT_COAP_OPTION_OBSERVE) != nullptr)
870
        {
871
            SuccessOrExit(error = otCoapOptionIteratorGetOptionUintValue(&iterator, &observe));
872
            observePresent = true;
873
874
            OutputFormat(" OBS=");
875
            OutputUint64(observe);
876
        }
877
#endif
878
#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
879
        if (otCoapOptionIteratorGetFirstOptionMatching(&iterator, OT_COAP_OPTION_BLOCK2) != nullptr)
880
        {
881
            SuccessOrExit(error = otCoapOptionIteratorGetOptionUintValue(&iterator, &blockValue));
882
            blockPresent = true;
883
        }
884
#endif
885
0
        break;
886
887
0
    case OT_COAP_CODE_DELETE:
888
0
        OutputFormat("DELETE");
889
0
        break;
890
891
0
    case OT_COAP_CODE_PUT:
892
0
        OutputFormat("PUT");
893
0
        break;
894
895
0
    case OT_COAP_CODE_POST:
896
0
        OutputFormat("POST");
897
0
        break;
898
899
0
    default:
900
0
        OutputLine("Undefined");
901
0
        ExitNow(error = OT_ERROR_PARSE);
902
0
    }
903
904
0
    PrintPayload(aMessage);
905
906
0
    if (otCoapMessageGetType(aMessage) == OT_COAP_TYPE_CONFIRMABLE ||
907
0
        otCoapMessageGetCode(aMessage) == OT_COAP_CODE_GET)
908
0
    {
909
#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
910
        if (observePresent && (mSubscriberTokenLength > 0) && (observe == 0))
911
        {
912
            // There is already a subscriber
913
            responseCode = OT_COAP_CODE_SERVICE_UNAVAILABLE;
914
        }
915
        else
916
#endif
917
0
            if (otCoapMessageGetCode(aMessage) == OT_COAP_CODE_GET)
918
0
        {
919
0
            responseCode = OT_COAP_CODE_CONTENT;
920
#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
921
            if (observePresent)
922
            {
923
                if (observe == 0)
924
                {
925
                    // New subscriber
926
                    OutputLine("Subscribing client");
927
                    mSubscriberSock.mAddress = aMessageInfo->mPeerAddr;
928
                    mSubscriberSock.mPort    = aMessageInfo->mPeerPort;
929
                    mSubscriberTokenLength   = otCoapMessageGetTokenLength(aMessage);
930
                    memcpy(mSubscriberToken, otCoapMessageGetToken(aMessage), mSubscriberTokenLength);
931
932
                    /*
933
                     * Implementer note.
934
                     *
935
                     * Here, we try to match a confirmable GET request with confirmable
936
                     * notifications, however this is not a requirement of RFC7641:
937
                     * the server can send notifications of either type regardless of
938
                     * what the client used to subscribe initially.
939
                     */
940
                    mSubscriberConfirmableNotifications = (otCoapMessageGetType(aMessage) == OT_COAP_TYPE_CONFIRMABLE);
941
                }
942
                else if (observe == 1)
943
                {
944
                    // See if it matches our subscriber token
945
                    if ((otCoapMessageGetTokenLength(aMessage) == mSubscriberTokenLength) &&
946
                        (memcmp(otCoapMessageGetToken(aMessage), mSubscriberToken, mSubscriberTokenLength) == 0))
947
                    {
948
                        // Unsubscribe request
949
                        CancelSubscriber();
950
                    }
951
                }
952
            }
953
#endif // OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
954
0
        }
955
0
        else
956
0
        {
957
0
            responseCode = OT_COAP_CODE_CHANGED;
958
0
        }
959
960
0
        responseMessage = otCoapNewMessage(GetInstancePtr(), nullptr);
961
0
        VerifyOrExit(responseMessage != nullptr, error = OT_ERROR_NO_BUFS);
962
963
0
        SuccessOrExit(
964
0
            error = otCoapMessageInitResponse(responseMessage, aMessage, OT_COAP_TYPE_ACKNOWLEDGMENT, responseCode));
965
966
0
        if (responseCode == OT_COAP_CODE_CONTENT)
967
0
        {
968
#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
969
            if (observePresent && (observe == 0))
970
            {
971
                SuccessOrExit(error = otCoapMessageAppendObserveOption(responseMessage, mObserveSerial++));
972
            }
973
#endif
974
#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
975
            if (blockPresent)
976
            {
977
                SuccessOrExit(error = otCoapMessageAppendBlock2Option(responseMessage,
978
                                                                      static_cast<uint32_t>(blockValue >> 4), true,
979
                                                                      static_cast<otCoapBlockSzx>(blockValue & 0x7)));
980
                SuccessOrExit(error = otCoapMessageSetPayloadMarker(responseMessage));
981
            }
982
            else
983
            {
984
#endif
985
0
                SuccessOrExit(error = otCoapMessageSetPayloadMarker(responseMessage));
986
0
                SuccessOrExit(error = otMessageAppend(responseMessage, mResourceContent,
987
0
                                                      static_cast<uint16_t>(strlen(mResourceContent))));
988
#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
989
            }
990
#endif
991
0
        }
992
993
#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
994
        if (blockPresent)
995
        {
996
            SuccessOrExit(error = otCoapSendResponseBlockWiseWithParameters(GetInstancePtr(), responseMessage,
997
                                                                            aMessageInfo, GetResponseTxParameters(),
998
                                                                            this, mResource.mTransmitHook));
999
        }
1000
        else
1001
        {
1002
#endif
1003
0
            SuccessOrExit(error = otCoapSendResponseWithParameters(GetInstancePtr(), responseMessage, aMessageInfo,
1004
0
                                                                   GetResponseTxParameters()));
1005
#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
1006
        }
1007
#endif
1008
0
    }
1009
1010
0
exit:
1011
1012
0
    if (error != OT_ERROR_NONE)
1013
0
    {
1014
0
        if (responseMessage != nullptr)
1015
0
        {
1016
0
            OutputLine("coap send response error %d: %s", error, otThreadErrorToString(error));
1017
0
            otMessageFree(responseMessage);
1018
0
        }
1019
0
    }
1020
0
    else if (responseCode >= OT_COAP_CODE_RESPONSE_MIN)
1021
0
    {
1022
0
        OutputLine("coap response sent");
1023
0
    }
1024
0
}
1025
1026
#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
1027
void Coap::HandleNotificationResponse(void                *aContext,
1028
                                      otMessage           *aMessage,
1029
                                      const otMessageInfo *aMessageInfo,
1030
                                      otError              aError)
1031
{
1032
    static_cast<Coap *>(aContext)->HandleNotificationResponse(aMessage, aMessageInfo, aError);
1033
}
1034
1035
void Coap::HandleNotificationResponse(otMessage *aMessage, const otMessageInfo *aMessageInfo, otError aError)
1036
{
1037
    OT_UNUSED_VARIABLE(aMessage);
1038
1039
    switch (aError)
1040
    {
1041
    case OT_ERROR_NONE:
1042
        if (aMessageInfo != nullptr)
1043
        {
1044
            OutputFormat("Received ACK in reply to notification from ");
1045
            OutputIp6AddressLine(aMessageInfo->mPeerAddr);
1046
        }
1047
        break;
1048
1049
    default:
1050
        OutputLine("coap receive notification response error %d: %s", aError, otThreadErrorToString(aError));
1051
        CancelSubscriber();
1052
        break;
1053
    }
1054
}
1055
#endif // OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
1056
1057
void Coap::HandleResponse(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo, otError aError)
1058
0
{
1059
0
    static_cast<Coap *>(aContext)->HandleResponse(aMessage, aMessageInfo, aError);
1060
0
}
1061
1062
void Coap::HandleResponse(otMessage *aMessage, const otMessageInfo *aMessageInfo, otError aError)
1063
0
{
1064
0
    if (aError != OT_ERROR_NONE)
1065
0
    {
1066
0
        OutputLine("coap receive response error %d: %s", aError, otThreadErrorToString(aError));
1067
0
    }
1068
0
    else if ((aMessageInfo != nullptr) && (aMessage != nullptr))
1069
0
    {
1070
#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
1071
        otCoapOptionIterator iterator;
1072
#endif
1073
1074
0
        OutputFormat("coap response from ");
1075
0
        OutputIp6Address(aMessageInfo->mPeerAddr);
1076
1077
#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
1078
        if (otCoapOptionIteratorInit(&iterator, aMessage) == OT_ERROR_NONE)
1079
        {
1080
            const otCoapOption *observeOpt =
1081
                otCoapOptionIteratorGetFirstOptionMatching(&iterator, OT_COAP_OPTION_OBSERVE);
1082
1083
            if (observeOpt != nullptr)
1084
            {
1085
                uint64_t observeVal = 0;
1086
                otError  error      = otCoapOptionIteratorGetOptionUintValue(&iterator, &observeVal);
1087
1088
                if (error == OT_ERROR_NONE)
1089
                {
1090
                    OutputFormat(" OBS=");
1091
                    OutputUint64(observeVal);
1092
                }
1093
            }
1094
        }
1095
#endif
1096
0
        PrintPayload(aMessage);
1097
0
    }
1098
0
}
1099
1100
#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
1101
otError Coap::BlockwiseReceiveHook(void          *aContext,
1102
                                   const uint8_t *aBlock,
1103
                                   uint32_t       aPosition,
1104
                                   uint16_t       aBlockLength,
1105
                                   bool           aMore,
1106
                                   uint32_t       aTotalLength)
1107
{
1108
    return static_cast<Coap *>(aContext)->BlockwiseReceiveHook(aBlock, aPosition, aBlockLength, aMore, aTotalLength);
1109
}
1110
1111
otError Coap::BlockwiseReceiveHook(const uint8_t *aBlock,
1112
                                   uint32_t       aPosition,
1113
                                   uint16_t       aBlockLength,
1114
                                   bool           aMore,
1115
                                   uint32_t       aTotalLength)
1116
{
1117
    OT_UNUSED_VARIABLE(aMore);
1118
    OT_UNUSED_VARIABLE(aTotalLength);
1119
1120
    OutputLine("received block: Num %lu Len %u", ToUlong(aPosition / aBlockLength), aBlockLength);
1121
1122
    for (uint16_t i = 0; i < aBlockLength / 16; i++)
1123
    {
1124
        OutputBytesLine(&aBlock[i * 16], 16);
1125
    }
1126
1127
    return OT_ERROR_NONE;
1128
}
1129
1130
otError Coap::BlockwiseTransmitHook(void     *aContext,
1131
                                    uint8_t  *aBlock,
1132
                                    uint32_t  aPosition,
1133
                                    uint16_t *aBlockLength,
1134
                                    bool     *aMore)
1135
{
1136
    return static_cast<Coap *>(aContext)->BlockwiseTransmitHook(aBlock, aPosition, aBlockLength, aMore);
1137
}
1138
1139
otError Coap::BlockwiseTransmitHook(uint8_t *aBlock, uint32_t aPosition, uint16_t *aBlockLength, bool *aMore)
1140
{
1141
    static uint32_t blockCount = 0;
1142
    OT_UNUSED_VARIABLE(aPosition);
1143
1144
    // Send a random payload
1145
    otRandomNonCryptoFillBuffer(aBlock, *aBlockLength);
1146
1147
    OutputLine("send block: Num %lu Len %u", ToUlong(blockCount), *aBlockLength);
1148
1149
    for (uint16_t i = 0; i < *aBlockLength / 16; i++)
1150
    {
1151
        OutputBytesLine(&aBlock[i * 16], 16);
1152
    }
1153
1154
    if (blockCount == mBlockCount - 1)
1155
    {
1156
        blockCount = 0;
1157
        *aMore     = false;
1158
    }
1159
    else
1160
    {
1161
        *aMore = true;
1162
        blockCount++;
1163
    }
1164
1165
    return OT_ERROR_NONE;
1166
}
1167
#endif // OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
1168
1169
} // namespace Cli
1170
} // namespace ot
1171
1172
#endif // OPENTHREAD_CONFIG_COAP_API_ENABLE