Coverage Report

Created: 2025-05-12 06:47

/src/openthread/src/cli/cli_coap.cpp
Line
Count
Source (jump to first uncovered line)
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
7.46k
    : Utils(aInstance, aOutputImplementer)
49
7.46k
    , mUseDefaultRequestTxParameters(true)
50
7.46k
    , mUseDefaultResponseTxParameters(true)
51
#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
52
7.46k
    , mObserveSerial(0)
53
7.46k
    , mRequestTokenLength(0)
54
7.46k
    , mSubscriberTokenLength(0)
55
7.46k
    , mSubscriberConfirmableNotifications(false)
56
#endif
57
#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
58
7.46k
    , mBlockCount(1)
59
#endif
60
7.46k
{
61
7.46k
    ClearAllBytes(mResource);
62
7.46k
#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
63
7.46k
    ClearAllBytes(mRequestAddr);
64
7.46k
    ClearAllBytes(mSubscriberSock);
65
7.46k
    ClearAllBytes(mRequestToken);
66
7.46k
    ClearAllBytes(mSubscriberToken);
67
7.46k
    ClearAllBytes(mRequestUri);
68
7.46k
#endif
69
7.46k
    ClearAllBytes(mUriPath);
70
7.46k
    strncpy(mResourceContent, "0", sizeof(mResourceContent));
71
7.46k
    mResourceContent[sizeof(mResourceContent) - 1] = '\0';
72
7.46k
}
73
74
#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
75
otError Coap::CancelResourceSubscription(void)
76
1
{
77
1
    otError       error   = OT_ERROR_NONE;
78
1
    otMessage    *message = nullptr;
79
1
    otMessageInfo messageInfo;
80
81
1
    ClearAllBytes(messageInfo);
82
1
    messageInfo.mPeerAddr = mRequestAddr;
83
1
    messageInfo.mPeerPort = OT_DEFAULT_COAP_PORT;
84
85
1
    VerifyOrExit(mRequestTokenLength != 0, error = OT_ERROR_INVALID_STATE);
86
87
0
    message = otCoapNewMessage(GetInstancePtr(), nullptr);
88
0
    VerifyOrExit(message != nullptr, error = OT_ERROR_NO_BUFS);
89
90
0
    otCoapMessageInit(message, OT_COAP_TYPE_CONFIRMABLE, OT_COAP_CODE_GET);
91
92
0
    SuccessOrExit(error = otCoapMessageSetToken(message, mRequestToken, mRequestTokenLength));
93
0
    SuccessOrExit(error = otCoapMessageAppendObserveOption(message, 1));
94
0
    SuccessOrExit(error = otCoapMessageAppendUriPathOptions(message, mRequestUri));
95
0
    SuccessOrExit(error = otCoapSendRequest(GetInstancePtr(), message, &messageInfo, &Coap::HandleResponse, this));
96
97
0
    ClearAllBytes(mRequestAddr);
98
0
    ClearAllBytes(mRequestUri);
99
0
    mRequestTokenLength = 0;
100
101
1
exit:
102
103
1
    if ((error != OT_ERROR_NONE) && (message != nullptr))
104
0
    {
105
0
        otMessageFree(message);
106
0
    }
107
108
1
    return error;
109
0
}
110
111
void Coap::CancelSubscriber(void)
112
0
{
113
0
    ClearAllBytes(mSubscriberSock);
114
0
    mSubscriberTokenLength = 0;
115
0
}
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
1
{
158
1
    OT_UNUSED_VARIABLE(aArgs);
159
160
1
    return CancelResourceSubscription();
161
1
}
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
5
{
183
5
    otError error = OT_ERROR_NONE;
184
185
5
    if (!aArgs[0].IsEmpty())
186
4
    {
187
4
        VerifyOrExit(aArgs[0].GetLength() < kMaxUriLength, error = OT_ERROR_INVALID_ARGS);
188
189
3
        mResource.mUriPath = mUriPath;
190
3
        mResource.mContext = this;
191
3
        mResource.mHandler = &Coap::HandleRequest;
192
193
3
#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
194
3
        mResource.mReceiveHook  = &Coap::BlockwiseReceiveHook;
195
3
        mResource.mTransmitHook = &Coap::BlockwiseTransmitHook;
196
197
3
        if (!aArgs[1].IsEmpty())
198
2
        {
199
2
            SuccessOrExit(error = aArgs[1].ParseAsUint32(mBlockCount));
200
2
        }
201
2
#endif
202
203
2
        strncpy(mUriPath, aArgs[0].GetCString(), sizeof(mUriPath) - 1);
204
205
2
#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
206
2
        otCoapAddBlockWiseResource(GetInstancePtr(), &mResource);
207
#else
208
        otCoapAddResource(GetInstancePtr(), &mResource);
209
#endif
210
2
    }
211
1
    else
212
1
    {
213
1
        OutputLine("%s", mResource.mUriPath != nullptr ? mResource.mUriPath : "");
214
1
    }
215
216
5
exit:
217
5
    return error;
218
5
}
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
7
{
236
7
#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
237
7
    otMessage    *notificationMessage = nullptr;
238
7
    otMessageInfo messageInfo;
239
7
#endif
240
7
    otError error = OT_ERROR_NONE;
241
242
7
    if (!aArgs[0].IsEmpty())
243
6
    {
244
6
        VerifyOrExit(aArgs[0].GetLength() < sizeof(mResourceContent), error = OT_ERROR_INVALID_ARGS);
245
5
        strncpy(mResourceContent, aArgs[0].GetCString(), sizeof(mResourceContent));
246
5
        mResourceContent[sizeof(mResourceContent) - 1] = '\0';
247
248
5
#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
249
5
        if (mSubscriberTokenLength > 0)
250
0
        {
251
            // Notify the subscriber
252
0
            ClearAllBytes(messageInfo);
253
0
            messageInfo.mPeerAddr = mSubscriberSock.mAddress;
254
0
            messageInfo.mPeerPort = mSubscriberSock.mPort;
255
256
0
            OutputFormat("sending coap notification to ");
257
0
            OutputIp6AddressLine(mSubscriberSock.mAddress);
258
259
0
            notificationMessage = otCoapNewMessage(GetInstancePtr(), nullptr);
260
0
            VerifyOrExit(notificationMessage != nullptr, error = OT_ERROR_NO_BUFS);
261
262
0
            otCoapMessageInit(
263
0
                notificationMessage,
264
0
                ((mSubscriberConfirmableNotifications) ? OT_COAP_TYPE_CONFIRMABLE : OT_COAP_TYPE_NON_CONFIRMABLE),
265
0
                OT_COAP_CODE_CONTENT);
266
267
0
            SuccessOrExit(error = otCoapMessageSetToken(notificationMessage, mSubscriberToken, mSubscriberTokenLength));
268
0
            SuccessOrExit(error = otCoapMessageAppendObserveOption(notificationMessage, mObserveSerial++));
269
0
            SuccessOrExit(error = otCoapMessageSetPayloadMarker(notificationMessage));
270
0
            SuccessOrExit(error = otMessageAppend(notificationMessage, mResourceContent,
271
0
                                                  static_cast<uint16_t>(strlen(mResourceContent))));
272
273
0
            SuccessOrExit(error = otCoapSendRequest(GetInstancePtr(), notificationMessage, &messageInfo,
274
0
                                                    &Coap::HandleNotificationResponse, this));
275
0
        }
276
5
#endif // OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
277
5
    }
278
1
    else
279
1
    {
280
1
        OutputLine("%s", mResourceContent);
281
1
    }
282
283
7
exit:
284
285
7
#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
286
7
    if ((error != OT_ERROR_NONE) && (notificationMessage != nullptr))
287
0
    {
288
0
        otMessageFree(notificationMessage);
289
0
    }
290
7
#endif
291
292
7
    return error;
293
7
}
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
1
#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
326
1
    otCoapRemoveBlockWiseResource(GetInstancePtr(), &mResource);
327
#else
328
    otCoapRemoveResource(GetInstancePtr(), &mResource);
329
#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
10
{
383
10
    otError             error = OT_ERROR_NONE;
384
10
    bool               *defaultTxParameters;
385
10
    otCoapTxParameters *txParameters;
386
387
10
    if (aArgs[0] == "request")
388
8
    {
389
8
        txParameters        = &mRequestTxParameters;
390
8
        defaultTxParameters = &mUseDefaultRequestTxParameters;
391
8
    }
392
2
    else if (aArgs[0] == "response")
393
1
    {
394
1
        txParameters        = &mResponseTxParameters;
395
1
        defaultTxParameters = &mUseDefaultResponseTxParameters;
396
1
    }
397
1
    else
398
1
    {
399
1
        ExitNow(error = OT_ERROR_INVALID_ARGS);
400
1
    }
401
402
9
    if (!aArgs[1].IsEmpty())
403
7
    {
404
7
        if (aArgs[1] == "default")
405
1
        {
406
1
            *defaultTxParameters = true;
407
1
        }
408
6
        else
409
6
        {
410
6
            SuccessOrExit(error = aArgs[1].ParseAsUint32(txParameters->mAckTimeout));
411
5
            SuccessOrExit(error = aArgs[2].ParseAsUint8(txParameters->mAckRandomFactorNumerator));
412
4
            SuccessOrExit(error = aArgs[3].ParseAsUint8(txParameters->mAckRandomFactorDenominator));
413
3
            SuccessOrExit(error = aArgs[4].ParseAsUint8(txParameters->mMaxRetransmit));
414
415
2
            VerifyOrExit(txParameters->mAckRandomFactorNumerator > txParameters->mAckRandomFactorDenominator,
416
2
                         error = OT_ERROR_INVALID_ARGS);
417
418
1
            *defaultTxParameters = false;
419
1
        }
420
7
    }
421
422
4
    OutputLine("Transmission parameters for %s:", aArgs[0].GetCString());
423
424
4
    if (*defaultTxParameters)
425
3
    {
426
3
        OutputLine("default");
427
3
    }
428
1
    else
429
1
    {
430
1
        OutputLine("ACK_TIMEOUT=%lu ms, ACK_RANDOM_FACTOR=%u/%u, MAX_RETRANSMIT=%u", ToUlong(txParameters->mAckTimeout),
431
1
                   txParameters->mAckRandomFactorNumerator, txParameters->mAckRandomFactorDenominator,
432
1
                   txParameters->mMaxRetransmit);
433
1
    }
434
435
10
exit:
436
10
    return error;
437
4
}
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
76
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
2
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
55
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
33
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
2
{
573
2
    return ProcessRequest(aArgs, OT_COAP_CODE_GET, /* aCoapObserve */ true);
574
2
}
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
168
{
583
168
    otError       error   = OT_ERROR_NONE;
584
168
    otMessage    *message = nullptr;
585
168
    otMessageInfo messageInfo;
586
168
    uint16_t      payloadLength    = 0;
587
168
    char         *uriQueryStartPtr = nullptr;
588
589
    // Default parameters
590
168
    char         coapUri[kMaxUriLength] = "test";
591
168
    otCoapType   coapType               = OT_COAP_TYPE_NON_CONFIRMABLE;
592
168
    otIp6Address coapDestinationIp;
593
168
#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
594
168
    bool           coapBlock     = false;
595
168
    otCoapBlockSzx coapBlockSize = OT_COAP_OPTION_BLOCK_SZX_16;
596
168
    BlockType      coapBlockType = (aCoapCode == OT_COAP_CODE_GET) ? kBlockType2 : kBlockType1;
597
168
#endif
598
599
168
#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE && OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
600
168
    if (aCoapObserve)
601
2
    {
602
2
        coapBlockType = kBlockType1;
603
2
    }
604
168
#endif
605
606
168
    SuccessOrExit(error = aArgs[0].ParseAsIp6Address(coapDestinationIp));
607
608
163
    VerifyOrExit(!aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
609
162
    VerifyOrExit(aArgs[1].GetLength() < sizeof(coapUri), error = OT_ERROR_INVALID_ARGS);
610
161
    strcpy(coapUri, aArgs[1].GetCString());
611
612
    // CoAP-Type
613
161
    if (!aArgs[2].IsEmpty())
614
126
    {
615
126
        if (aArgs[2] == "con")
616
14
        {
617
14
            coapType = OT_COAP_TYPE_CONFIRMABLE;
618
14
        }
619
112
#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
620
112
        else if (aArgs[2] == "block-16")
621
13
        {
622
13
            coapType      = OT_COAP_TYPE_CONFIRMABLE;
623
13
            coapBlock     = true;
624
13
            coapBlockSize = OT_COAP_OPTION_BLOCK_SZX_16;
625
13
        }
626
99
        else if (aArgs[2] == "block-32")
627
27
        {
628
27
            coapType      = OT_COAP_TYPE_CONFIRMABLE;
629
27
            coapBlock     = true;
630
27
            coapBlockSize = OT_COAP_OPTION_BLOCK_SZX_32;
631
27
        }
632
72
        else if (aArgs[2] == "block-64")
633
21
        {
634
21
            coapType      = OT_COAP_TYPE_CONFIRMABLE;
635
21
            coapBlock     = true;
636
21
            coapBlockSize = OT_COAP_OPTION_BLOCK_SZX_64;
637
21
        }
638
51
        else if (aArgs[2] == "block-128")
639
2
        {
640
2
            coapType      = OT_COAP_TYPE_CONFIRMABLE;
641
2
            coapBlock     = true;
642
2
            coapBlockSize = OT_COAP_OPTION_BLOCK_SZX_128;
643
2
        }
644
49
        else if (aArgs[2] == "block-256")
645
5
        {
646
5
            coapType      = OT_COAP_TYPE_CONFIRMABLE;
647
5
            coapBlock     = true;
648
5
            coapBlockSize = OT_COAP_OPTION_BLOCK_SZX_256;
649
5
        }
650
44
        else if (aArgs[2] == "block-512")
651
6
        {
652
6
            coapType      = OT_COAP_TYPE_CONFIRMABLE;
653
6
            coapBlock     = true;
654
6
            coapBlockSize = OT_COAP_OPTION_BLOCK_SZX_512;
655
6
        }
656
38
        else if (aArgs[2] == "block-1024")
657
16
        {
658
16
            coapType      = OT_COAP_TYPE_CONFIRMABLE;
659
16
            coapBlock     = true;
660
16
            coapBlockSize = OT_COAP_OPTION_BLOCK_SZX_1024;
661
16
        }
662
126
#endif // OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
663
126
    }
664
665
161
#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
666
161
    if (aCoapObserve && mRequestTokenLength)
667
0
    {
668
        // New observe request, cancel any existing observation
669
0
        SuccessOrExit(error = CancelResourceSubscription());
670
0
    }
671
161
#endif
672
673
161
    message = otCoapNewMessage(GetInstancePtr(), nullptr);
674
161
    VerifyOrExit(message != nullptr, error = OT_ERROR_NO_BUFS);
675
676
161
    otCoapMessageInit(message, coapType, aCoapCode);
677
161
    otCoapMessageGenerateToken(message, OT_COAP_DEFAULT_TOKEN_LENGTH);
678
679
161
#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
680
161
    if (aCoapObserve)
681
1
    {
682
1
        SuccessOrExit(error = otCoapMessageAppendObserveOption(message, 0));
683
1
    }
684
161
#endif
685
686
161
    uriQueryStartPtr = const_cast<char *>(StringFind(coapUri, '?'));
687
688
161
    if (uriQueryStartPtr == nullptr)
689
129
    {
690
        // "?" doesn't present in URI --> contains only URI path parts
691
129
        SuccessOrExit(error = otCoapMessageAppendUriPathOptions(message, coapUri));
692
129
    }
693
32
    else
694
32
    {
695
        // "?" presents in URI --> contains URI path AND URI query parts
696
32
        *uriQueryStartPtr++ = '\0';
697
32
        SuccessOrExit(error = otCoapMessageAppendUriPathOptions(message, coapUri));
698
32
        SuccessOrExit(error = otCoapMessageAppendUriQueryOptions(message, uriQueryStartPtr));
699
32
    }
700
701
161
#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
702
161
    if (coapBlock)
703
90
    {
704
90
        if (coapBlockType == kBlockType1)
705
82
        {
706
82
            SuccessOrExit(error = otCoapMessageAppendBlock1Option(message, 0, true, coapBlockSize));
707
82
        }
708
8
        else
709
8
        {
710
8
            SuccessOrExit(error = otCoapMessageAppendBlock2Option(message, 0, false, coapBlockSize));
711
8
        }
712
90
    }
713
161
#endif
714
715
161
    if (!aArgs[3].IsEmpty())
716
60
    {
717
60
#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
718
60
        if (coapBlock)
719
27
        {
720
27
            SuccessOrExit(error = aArgs[3].ParseAsUint32(mBlockCount));
721
27
        }
722
33
        else
723
33
        {
724
33
#endif
725
33
            payloadLength = aArgs[3].GetLength();
726
727
33
            if (payloadLength > 0)
728
33
            {
729
33
                SuccessOrExit(error = otCoapMessageSetPayloadMarker(message));
730
33
            }
731
33
#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
732
33
        }
733
60
#endif
734
60
    }
735
736
    // Embed content into message if given
737
160
    if (payloadLength > 0)
738
33
    {
739
33
        SuccessOrExit(error = otMessageAppend(message, aArgs[3].GetCString(), payloadLength));
740
33
    }
741
742
160
    ClearAllBytes(messageInfo);
743
160
    messageInfo.mPeerAddr = coapDestinationIp;
744
160
    messageInfo.mPeerPort = OT_DEFAULT_COAP_PORT;
745
746
160
#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
747
160
    if (aCoapObserve)
748
1
    {
749
        // Make a note of the message details for later so we can cancel it later.
750
1
        memcpy(&mRequestAddr, &coapDestinationIp, sizeof(mRequestAddr));
751
1
        mRequestTokenLength = otCoapMessageGetTokenLength(message);
752
1
        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
1
        memcpy(mRequestUri, coapUri, sizeof(mRequestUri) - 1);
756
1
    }
757
160
#endif
758
759
160
    if ((coapType == OT_COAP_TYPE_CONFIRMABLE) || (aCoapCode == OT_COAP_CODE_GET))
760
157
    {
761
157
#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
762
157
        if (coapBlock)
763
89
        {
764
89
            if (aCoapCode == OT_COAP_CODE_PUT || aCoapCode == OT_COAP_CODE_POST)
765
49
            {
766
49
                SuccessOrExit(error = otCoapMessageSetPayloadMarker(message));
767
49
            }
768
89
            error = otCoapSendRequestBlockWiseWithParameters(GetInstancePtr(), message, &messageInfo,
769
89
                                                             &Coap::HandleResponse, this, GetRequestTxParameters(),
770
89
                                                             Coap::BlockwiseTransmitHook, Coap::BlockwiseReceiveHook);
771
89
        }
772
68
        else
773
68
        {
774
68
#endif
775
68
            error = otCoapSendRequestWithParameters(GetInstancePtr(), message, &messageInfo, &Coap::HandleResponse,
776
68
                                                    this, GetRequestTxParameters());
777
68
#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
778
68
        }
779
157
#endif
780
157
    }
781
3
    else
782
3
    {
783
3
        error = otCoapSendRequestWithParameters(GetInstancePtr(), message, &messageInfo, nullptr, nullptr,
784
3
                                                GetResponseTxParameters());
785
3
    }
786
787
168
exit:
788
789
168
    if ((error != OT_ERROR_NONE) && (message != nullptr))
790
161
    {
791
161
        otMessageFree(message);
792
161
    }
793
794
168
    return error;
795
160
}
796
797
otError Coap::Process(Arg aArgs[])
798
199
{
799
199
#define CmdEntry(aCommandString)                            \
800
2.18k
    {                                                       \
801
2.18k
        aCommandString, &Coap::Process<Cmd(aCommandString)> \
802
2.18k
    }
803
804
199
    static constexpr Command kCommands[] = {
805
199
#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
806
199
        CmdEntry("cancel"),
807
199
#endif
808
199
        CmdEntry("delete"),
809
199
        CmdEntry("get"),
810
199
#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
811
199
        CmdEntry("observe"),
812
199
#endif
813
199
        CmdEntry("parameters"),
814
199
        CmdEntry("post"),
815
199
        CmdEntry("put"),
816
199
        CmdEntry("resource"),
817
199
        CmdEntry("set"),
818
199
        CmdEntry("start"),
819
199
        CmdEntry("stop"),
820
199
    };
821
822
199
#undef CmdEntry
823
824
199
    static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted");
825
826
199
    otError        error = OT_ERROR_INVALID_COMMAND;
827
199
    const Command *command;
828
829
199
    if (aArgs[0].IsEmpty() || (aArgs[0] == "help"))
830
2
    {
831
2
        OutputCommandTable(kCommands);
832
2
        ExitNow(error = aArgs[0].IsEmpty() ? OT_ERROR_INVALID_ARGS : OT_ERROR_NONE);
833
2
    }
834
835
197
    command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
836
197
    VerifyOrExit(command != nullptr);
837
838
193
    error = (this->*command->mHandler)(aArgs + 1);
839
840
199
exit:
841
199
    return error;
842
193
}
843
844
void Coap::HandleRequest(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
845
0
{
846
0
    static_cast<Coap *>(aContext)->HandleRequest(aMessage, aMessageInfo);
847
0
}
848
849
void Coap::HandleRequest(otMessage *aMessage, const otMessageInfo *aMessageInfo)
850
0
{
851
0
    otError    error           = OT_ERROR_NONE;
852
0
    otMessage *responseMessage = nullptr;
853
0
    otCoapCode responseCode    = OT_COAP_CODE_EMPTY;
854
0
#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
855
0
    uint64_t observe        = 0;
856
0
    bool     observePresent = false;
857
0
#endif
858
0
#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
859
0
    uint64_t blockValue   = 0;
860
0
    bool     blockPresent = false;
861
0
#endif
862
0
#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE || OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
863
0
    otCoapOptionIterator iterator;
864
0
#endif
865
866
0
    OutputFormat("coap request from ");
867
0
    OutputIp6Address(aMessageInfo->mPeerAddr);
868
0
    OutputFormat(" ");
869
870
0
    switch (otCoapMessageGetCode(aMessage))
871
0
    {
872
0
    case OT_COAP_CODE_GET:
873
0
        OutputFormat("GET");
874
0
#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE || OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
875
0
        SuccessOrExit(error = otCoapOptionIteratorInit(&iterator, aMessage));
876
0
#endif
877
0
#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
878
0
        if (otCoapOptionIteratorGetFirstOptionMatching(&iterator, OT_COAP_OPTION_OBSERVE) != nullptr)
879
0
        {
880
0
            SuccessOrExit(error = otCoapOptionIteratorGetOptionUintValue(&iterator, &observe));
881
0
            observePresent = true;
882
883
0
            OutputFormat(" OBS=");
884
0
            OutputUint64(observe);
885
0
        }
886
0
#endif
887
0
#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
888
0
        if (otCoapOptionIteratorGetFirstOptionMatching(&iterator, OT_COAP_OPTION_BLOCK2) != nullptr)
889
0
        {
890
0
            SuccessOrExit(error = otCoapOptionIteratorGetOptionUintValue(&iterator, &blockValue));
891
0
            blockPresent = true;
892
0
        }
893
0
#endif
894
0
        break;
895
896
0
    case OT_COAP_CODE_DELETE:
897
0
        OutputFormat("DELETE");
898
0
        break;
899
900
0
    case OT_COAP_CODE_PUT:
901
0
        OutputFormat("PUT");
902
0
        break;
903
904
0
    case OT_COAP_CODE_POST:
905
0
        OutputFormat("POST");
906
0
        break;
907
908
0
    default:
909
0
        OutputLine("Undefined");
910
0
        ExitNow(error = OT_ERROR_PARSE);
911
0
    }
912
913
0
    PrintPayload(aMessage);
914
915
0
    if (otCoapMessageGetType(aMessage) == OT_COAP_TYPE_CONFIRMABLE ||
916
0
        otCoapMessageGetCode(aMessage) == OT_COAP_CODE_GET)
917
0
    {
918
0
#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
919
0
        if (observePresent && (mSubscriberTokenLength > 0) && (observe == 0))
920
0
        {
921
            // There is already a subscriber
922
0
            responseCode = OT_COAP_CODE_SERVICE_UNAVAILABLE;
923
0
        }
924
0
        else
925
0
#endif
926
0
            if (otCoapMessageGetCode(aMessage) == OT_COAP_CODE_GET)
927
0
        {
928
0
            responseCode = OT_COAP_CODE_CONTENT;
929
0
#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
930
0
            if (observePresent)
931
0
            {
932
0
                if (observe == 0)
933
0
                {
934
                    // New subscriber
935
0
                    OutputLine("Subscribing client");
936
0
                    mSubscriberSock.mAddress = aMessageInfo->mPeerAddr;
937
0
                    mSubscriberSock.mPort    = aMessageInfo->mPeerPort;
938
0
                    mSubscriberTokenLength   = otCoapMessageGetTokenLength(aMessage);
939
0
                    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
0
                    mSubscriberConfirmableNotifications = (otCoapMessageGetType(aMessage) == OT_COAP_TYPE_CONFIRMABLE);
950
0
                }
951
0
                else if (observe == 1)
952
0
                {
953
                    // See if it matches our subscriber token
954
0
                    if ((otCoapMessageGetTokenLength(aMessage) == mSubscriberTokenLength) &&
955
0
                        (memcmp(otCoapMessageGetToken(aMessage), mSubscriberToken, mSubscriberTokenLength) == 0))
956
0
                    {
957
                        // Unsubscribe request
958
0
                        CancelSubscriber();
959
0
                    }
960
0
                }
961
0
            }
962
0
#endif // OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
963
0
        }
964
0
        else
965
0
        {
966
0
            responseCode = OT_COAP_CODE_CHANGED;
967
0
        }
968
969
0
        responseMessage = otCoapNewMessage(GetInstancePtr(), nullptr);
970
0
        VerifyOrExit(responseMessage != nullptr, error = OT_ERROR_NO_BUFS);
971
972
0
        SuccessOrExit(
973
0
            error = otCoapMessageInitResponse(responseMessage, aMessage, OT_COAP_TYPE_ACKNOWLEDGMENT, responseCode));
974
975
0
        if (responseCode == OT_COAP_CODE_CONTENT)
976
0
        {
977
0
#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
978
0
            if (observePresent && (observe == 0))
979
0
            {
980
0
                SuccessOrExit(error = otCoapMessageAppendObserveOption(responseMessage, mObserveSerial++));
981
0
            }
982
0
#endif
983
0
#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
984
0
            if (blockPresent)
985
0
            {
986
0
                SuccessOrExit(error = otCoapMessageAppendBlock2Option(responseMessage,
987
0
                                                                      static_cast<uint32_t>(blockValue >> 4), true,
988
0
                                                                      static_cast<otCoapBlockSzx>(blockValue & 0x7)));
989
0
                SuccessOrExit(error = otCoapMessageSetPayloadMarker(responseMessage));
990
0
            }
991
0
            else
992
0
            {
993
0
#endif
994
0
                SuccessOrExit(error = otCoapMessageSetPayloadMarker(responseMessage));
995
0
                SuccessOrExit(error = otMessageAppend(responseMessage, mResourceContent,
996
0
                                                      static_cast<uint16_t>(strlen(mResourceContent))));
997
0
#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
998
0
            }
999
0
#endif
1000
0
        }
1001
1002
0
#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
1003
0
        if (blockPresent)
1004
0
        {
1005
0
            SuccessOrExit(error = otCoapSendResponseBlockWiseWithParameters(GetInstancePtr(), responseMessage,
1006
0
                                                                            aMessageInfo, GetResponseTxParameters(),
1007
0
                                                                            this, mResource.mTransmitHook));
1008
0
        }
1009
0
        else
1010
0
        {
1011
0
#endif
1012
0
            SuccessOrExit(error = otCoapSendResponseWithParameters(GetInstancePtr(), responseMessage, aMessageInfo,
1013
0
                                                                   GetResponseTxParameters()));
1014
0
#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
1015
0
        }
1016
0
#endif
1017
0
    }
1018
1019
0
exit:
1020
1021
0
    if (error != OT_ERROR_NONE)
1022
0
    {
1023
0
        if (responseMessage != nullptr)
1024
0
        {
1025
0
            OutputLine("coap send response error %d: %s", error, otThreadErrorToString(error));
1026
0
            otMessageFree(responseMessage);
1027
0
        }
1028
0
    }
1029
0
    else if (responseCode >= OT_COAP_CODE_RESPONSE_MIN)
1030
0
    {
1031
0
        OutputLine("coap response sent");
1032
0
    }
1033
0
}
1034
1035
#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
1036
void Coap::HandleNotificationResponse(void                *aContext,
1037
                                      otMessage           *aMessage,
1038
                                      const otMessageInfo *aMessageInfo,
1039
                                      otError              aError)
1040
0
{
1041
0
    static_cast<Coap *>(aContext)->HandleNotificationResponse(aMessage, aMessageInfo, aError);
1042
0
}
1043
1044
void Coap::HandleNotificationResponse(otMessage *aMessage, const otMessageInfo *aMessageInfo, otError aError)
1045
0
{
1046
0
    OT_UNUSED_VARIABLE(aMessage);
1047
1048
0
    switch (aError)
1049
0
    {
1050
0
    case OT_ERROR_NONE:
1051
0
        if (aMessageInfo != nullptr)
1052
0
        {
1053
0
            OutputFormat("Received ACK in reply to notification from ");
1054
0
            OutputIp6AddressLine(aMessageInfo->mPeerAddr);
1055
0
        }
1056
0
        break;
1057
1058
0
    default:
1059
0
        OutputLine("coap receive notification response error %d: %s", aError, otThreadErrorToString(aError));
1060
0
        CancelSubscriber();
1061
0
        break;
1062
0
    }
1063
0
}
1064
#endif // OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
1065
1066
void Coap::HandleResponse(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo, otError aError)
1067
0
{
1068
0
    static_cast<Coap *>(aContext)->HandleResponse(aMessage, aMessageInfo, aError);
1069
0
}
1070
1071
void Coap::HandleResponse(otMessage *aMessage, const otMessageInfo *aMessageInfo, otError aError)
1072
0
{
1073
0
    if (aError != OT_ERROR_NONE)
1074
0
    {
1075
0
        OutputLine("coap receive response error %d: %s", aError, otThreadErrorToString(aError));
1076
0
    }
1077
0
    else if ((aMessageInfo != nullptr) && (aMessage != nullptr))
1078
0
    {
1079
0
#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
1080
0
        otCoapOptionIterator iterator;
1081
0
#endif
1082
1083
0
        OutputFormat("coap response from ");
1084
0
        OutputIp6Address(aMessageInfo->mPeerAddr);
1085
1086
0
#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
1087
0
        if (otCoapOptionIteratorInit(&iterator, aMessage) == OT_ERROR_NONE)
1088
0
        {
1089
0
            const otCoapOption *observeOpt =
1090
0
                otCoapOptionIteratorGetFirstOptionMatching(&iterator, OT_COAP_OPTION_OBSERVE);
1091
1092
0
            if (observeOpt != nullptr)
1093
0
            {
1094
0
                uint64_t observeVal = 0;
1095
0
                otError  error      = otCoapOptionIteratorGetOptionUintValue(&iterator, &observeVal);
1096
1097
0
                if (error == OT_ERROR_NONE)
1098
0
                {
1099
0
                    OutputFormat(" OBS=");
1100
0
                    OutputUint64(observeVal);
1101
0
                }
1102
0
            }
1103
0
        }
1104
0
#endif
1105
0
        PrintPayload(aMessage);
1106
0
    }
1107
0
}
1108
1109
#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
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
0
{
1117
0
    return static_cast<Coap *>(aContext)->BlockwiseReceiveHook(aBlock, aPosition, aBlockLength, aMore, aTotalLength);
1118
0
}
1119
1120
otError Coap::BlockwiseReceiveHook(const uint8_t *aBlock,
1121
                                   uint32_t       aPosition,
1122
                                   uint16_t       aBlockLength,
1123
                                   bool           aMore,
1124
                                   uint32_t       aTotalLength)
1125
0
{
1126
0
    OT_UNUSED_VARIABLE(aMore);
1127
0
    OT_UNUSED_VARIABLE(aTotalLength);
1128
1129
0
    OutputLine("received block: Num %i Len %i", aPosition / aBlockLength, aBlockLength);
1130
1131
0
    for (uint16_t i = 0; i < aBlockLength / 16; i++)
1132
0
    {
1133
0
        OutputBytesLine(&aBlock[i * 16], 16);
1134
0
    }
1135
1136
0
    return OT_ERROR_NONE;
1137
0
}
1138
1139
otError Coap::BlockwiseTransmitHook(void     *aContext,
1140
                                    uint8_t  *aBlock,
1141
                                    uint32_t  aPosition,
1142
                                    uint16_t *aBlockLength,
1143
                                    bool     *aMore)
1144
81
{
1145
81
    return static_cast<Coap *>(aContext)->BlockwiseTransmitHook(aBlock, aPosition, aBlockLength, aMore);
1146
81
}
1147
1148
otError Coap::BlockwiseTransmitHook(uint8_t *aBlock, uint32_t aPosition, uint16_t *aBlockLength, bool *aMore)
1149
81
{
1150
81
    static uint32_t blockCount = 0;
1151
81
    OT_UNUSED_VARIABLE(aPosition);
1152
1153
    // Send a random payload
1154
81
    otRandomNonCryptoFillBuffer(aBlock, *aBlockLength);
1155
1156
81
    OutputLine("send block: Num %i Len %i", blockCount, *aBlockLength);
1157
1158
1.52k
    for (uint16_t i = 0; i < *aBlockLength / 16; i++)
1159
1.44k
    {
1160
1.44k
        OutputBytesLine(&aBlock[i * 16], 16);
1161
1.44k
    }
1162
1163
81
    if (blockCount == mBlockCount - 1)
1164
10
    {
1165
10
        blockCount = 0;
1166
10
        *aMore     = false;
1167
10
    }
1168
71
    else
1169
71
    {
1170
71
        *aMore = true;
1171
71
        blockCount++;
1172
71
    }
1173
1174
81
    return OT_ERROR_NONE;
1175
81
}
1176
#endif // OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
1177
1178
} // namespace Cli
1179
} // namespace ot
1180
1181
#endif // OPENTHREAD_CONFIG_COAP_API_ENABLE