/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 |