/* * Copyright (c) 2018, The OpenThread Authors. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * @file * This file implements a simple CLI for the CoAP Secure service. */ #include "cli_coap_secure.hpp" #if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE #include #include #include "cli/cli.hpp" // header for place your x509 certificate and private key #include "x509_cert_key.hpp" namespace ot { namespace Cli { CoapSecure::CoapSecure(otInstance *aInstance, OutputImplementer &aOutputImplementer) : Utils(aInstance, aOutputImplementer) , mShutdownFlag(false) , mUseCertificate(false) , mPskLength(0) , mPskIdLength(0) #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE , mBlockCount(1) #endif { ClearAllBytes(mResource); ClearAllBytes(mPsk); ClearAllBytes(mPskId); ClearAllBytes(mUriPath); strncpy(mResourceContent, "0", sizeof(mResourceContent)); mResourceContent[sizeof(mResourceContent) - 1] = '\0'; } void CoapSecure::PrintPayload(otMessage *aMessage) { uint8_t buf[kMaxBufferSize]; uint16_t bytesToPrint; uint16_t bytesPrinted = 0; uint16_t length = otMessageGetLength(aMessage) - otMessageGetOffset(aMessage); if (length > 0) { OutputFormat(" with payload: "); while (length > 0) { bytesToPrint = Min(length, static_cast(sizeof(buf))); otMessageRead(aMessage, otMessageGetOffset(aMessage) + bytesPrinted, buf, bytesToPrint); OutputBytes(buf, static_cast(bytesToPrint)); length -= bytesToPrint; bytesPrinted += bytesToPrint; } } OutputNewLine(); } /** * @cli coaps resource (get,set) * @code * coaps resource test-resource * Done * @endcode * @code * coaps resource * test-resource * Done * @endcode * @cparam coaps resource [@ca{uri-path}] * @par * Gets or sets the URI path of the CoAPS server resource. @moreinfo{@coaps}. * @sa otCoapSecureAddBlockWiseResource */ template <> otError CoapSecure::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; if (!aArgs[0].IsEmpty()) { VerifyOrExit(aArgs[0].GetLength() < kMaxUriLength, error = OT_ERROR_INVALID_ARGS); mResource.mUriPath = mUriPath; mResource.mContext = this; mResource.mHandler = &CoapSecure::HandleRequest; #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE mResource.mReceiveHook = &CoapSecure::BlockwiseReceiveHook; mResource.mTransmitHook = &CoapSecure::BlockwiseTransmitHook; if (!aArgs[1].IsEmpty()) { SuccessOrExit(error = aArgs[1].ParseAsUint32(mBlockCount)); } #endif strncpy(mUriPath, aArgs[0].GetCString(), sizeof(mUriPath) - 1); #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE otCoapSecureAddBlockWiseResource(GetInstancePtr(), &mResource); #else otCoapSecureAddResource(GetInstancePtr(), &mResource); #endif } else { OutputLine("%s", mResource.mUriPath != nullptr ? mResource.mUriPath : ""); } exit: return error; } /** * @cli coaps set * @code * coaps set Testing123 * Done * @endcode * @cparam coaps set @ca{new-content} * @par * Sets the content sent by the resource on the CoAPS server. @moreinfo{@coaps}. */ template <> otError CoapSecure::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; if (!aArgs[0].IsEmpty()) { VerifyOrExit(aArgs[0].GetLength() < sizeof(mResourceContent), error = OT_ERROR_INVALID_ARGS); strncpy(mResourceContent, aArgs[0].GetCString(), sizeof(mResourceContent)); mResourceContent[sizeof(mResourceContent) - 1] = '\0'; } else { OutputLine("%s", mResourceContent); } exit: return error; } /** * @cli coaps start * @code * coaps start * Done * @endcode * @code * coaps start false * Done * @endcode * @code * coaps start 8 * Done * @endcode * @cparam coaps start [@ca{check-peer-cert} | @ca{max-conn-attempts}] * The `check-peer-cert` parameter determines if the peer-certificate check is * enabled (default) or disabled. * The `max-conn-attempts` parameter sets the maximum number of allowed * attempts, successful or failed, to connect to the CoAP Secure server. * The default value of this parameter is `0`, which means that there is * no limit to the number of attempts. * The `check-peer-cert` and `max-conn-attempts` parameters work * together in the following combinations, even though you can only specify * one argument: * * No argument specified: Defaults are used. * * Setting `check-peer-cert` to `true`: * Has the same effect as omitting the argument, which is that the * `check-peer-cert` value is `true`, and the `max-conn-attempts` value is 0. * * Setting `check-peer-cert` to `false`: * `check-peer-cert` value is `false`, and the `max-conn-attempts` value is 0. * * Specifying a number: * `check-peer-cert` is `true`, and the `max-conn-attempts` value is the * number specified in the argument. * @par * Starts the CoAP Secure service. @moreinfo{@coaps}. * @sa otCoapSecureStart * @sa otCoapSecureSetSslAuthMode * @sa otCoapSecureSetClientConnectEventCallback */ template <> otError CoapSecure::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; bool verifyPeerCert = true; uint16_t maxConnAttempts = 0; if (!aArgs[0].IsEmpty()) { if (aArgs[0] == "false") { verifyPeerCert = false; } else if (aArgs[0] == "true") { verifyPeerCert = true; } else { SuccessOrExit(error = aArgs[0].ParseAsUint16(maxConnAttempts)); } } otCoapSecureSetSslAuthMode(GetInstancePtr(), verifyPeerCert); otCoapSecureSetClientConnectEventCallback(GetInstancePtr(), &CoapSecure::HandleConnectEvent, this); #if CLI_COAP_SECURE_USE_COAP_DEFAULT_HANDLER otCoapSecureSetDefaultHandler(GetInstancePtr(), &CoapSecure::DefaultHandler, this); #endif error = otCoapSecureStartWithMaxConnAttempts(GetInstancePtr(), OT_DEFAULT_COAP_SECURE_PORT, maxConnAttempts, nullptr, nullptr); exit: return error; } /** * @cli coaps stop * @code * coaps stop * Done * @endcode * @par * Stops the CoAP Secure service. @moreinfo{@coaps}. * @sa otCoapSecureStop */ template <> otError CoapSecure::Process(Arg aArgs[]) { OT_UNUSED_VARIABLE(aArgs); #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE otCoapRemoveBlockWiseResource(GetInstancePtr(), &mResource); #else otCoapRemoveResource(GetInstancePtr(), &mResource); #endif if (otCoapSecureIsConnectionActive(GetInstancePtr())) { otCoapSecureDisconnect(GetInstancePtr()); mShutdownFlag = true; } else { Stop(); } return OT_ERROR_NONE; } /** * @cli coaps isclosed * @code * coaps isclosed * no * Done * @endcode * @par * Indicates if the CoAP Secure service is closed. @moreinfo{@coaps}. * @sa otCoapSecureIsClosed */ template <> otError CoapSecure::Process(Arg aArgs[]) { return ProcessIsRequest(aArgs, otCoapSecureIsClosed); } /** * @cli coaps isconnected * @code * coaps isconnected * yes * Done * @endcode * @par * Indicates if the CoAP Secure service is connected. @moreinfo{@coaps}. * @sa otCoapSecureIsConnected */ template <> otError CoapSecure::Process(Arg aArgs[]) { return ProcessIsRequest(aArgs, otCoapSecureIsConnected); } /** * @cli coaps isconnactive * @code * coaps isconnactive * yes * Done * @endcode * @par * Indicates if the CoAP Secure service connection is active * (either already connected or in the process of establishing a connection). * @moreinfo{@coaps}. * @sa otCoapSecureIsConnectionActive */ template <> otError CoapSecure::Process(Arg aArgs[]) { return ProcessIsRequest(aArgs, otCoapSecureIsConnectionActive); } otError CoapSecure::ProcessIsRequest(Arg aArgs[], bool (*IsChecker)(otInstance *)) { otError error = OT_ERROR_NONE; VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS); OutputLine("%s", IsChecker(GetInstancePtr()) ? "yes" : "no"); exit: return error; } /** * @cli coaps get * @code * coaps get test-resource * Done * @endcode * @code * coaps get test-resource block-1024 * Done * @endcode * @cparam coaps get @ca{uri-path} [@ca{type}] * * `uri-path`: URI path of the resource. * * `type`: * * `con`: Confirmable * * `non-con`: Non-confirmable (default) * * `block-`: Use this option, followed by the block-wise value, * if the response should be transferred block-wise. * Valid values are: `block-16`, `block-32`, `block-64`, `block-128`, * `block-256`, `block-512`, or `block-1024`. * @par * Gets information about the specified CoAPS resource on the CoAPS server. * @moreinfo{@coaps}. */ template <> otError CoapSecure::Process(Arg aArgs[]) { return ProcessRequest(aArgs, OT_COAP_CODE_GET); } /** * @cli coaps post * @code * coaps post test-resource con hellothere * Done * @endcode * @code * coaps post test-resource block-1024 10 * Done * @endcode * @cparam @ca{uri-path} [@ca{type}] [@ca{payload}] * * `uri-path`: URI path of the resource. * * `type`: * * `con`: Confirmable * * `non-con`: Non-confirmable (default) * * `block-`: Use this option, followed by the block-wise value, * to send blocks with a randomly generated number of bytes for the payload. * Valid values are: `block-16`, `block-32`, `block-64`, `block-128`, * `block-256`, `block-512`, or `block-1024`. * * `payload`: CoAPS payload request, which if used is either a string * or an integer, depending on the `type`. If the `type` is `con` or `non-con`, * the payload parameter is optional. If you leave out the payload * parameter, an empty payload is sent. However, If you use the payload * parameter, its value must be a string, such as `hellothere`. If the * `type` is `block-`, the value of the payload parameter must be an * integer that specifies the number of blocks to send. The `block-` type * requires `OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE` to be set. * @par * Creates the specified CoAPS resource. @moreinfo{@coaps}. */ template <> otError CoapSecure::Process(Arg aArgs[]) { return ProcessRequest(aArgs, OT_COAP_CODE_POST); } /** * @cli coaps put * @code * coaps put test-resource con hellothere * Done * @endcode * @code * coaps put test-resource block-1024 10 * Done * @endcode * @cparam @ca{uri-path} [@ca{type}] [@ca{payload}] * * `uri-path`: URI path of the resource. * * `type`: * * `con`: Confirmable * * `non-con`: Non-confirmable (default) * * `block-`: Use this option, followed by the block-wise value, * to send blocks with a randomly generated number of bytes for the payload. * Valid values are: `block-16`, `block-32`, `block-64`, `block-128`, * `block-256`, `block-512`, or `block-1024`. * * `payload`: CoAPS payload request, which if used is either a string * or an integer, depending on the `type`. If the `type` is `con` or `non-con`, * the payload parameter is optional. If you leave out the payload * parameter, an empty payload is sent. However, If you use the payload * parameter, its value must be a string, such as `hellothere`. If the * `type` is `block-`, the value of the payload parameter must be an * integer that specifies the number of blocks to send. The `block-` type * requires `OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE` to be set. * @par * Modifies the specified CoAPS resource. @moreinfo{@coaps}. */ template <> otError CoapSecure::Process(Arg aArgs[]) { return ProcessRequest(aArgs, OT_COAP_CODE_PUT); } /** * @cli coaps delete * @code * coaps delete test-resource con hellothere * Done * @endcode * @cparam coaps delete @ca{uri-path} [@ca{type}] [@ca{payload}] * * `uri-path`: URI path of the resource. * * `type`: * * `con`: Confirmable * * `non-con`: Non-confirmable (default) * * `payload`: CoAPS payload request. * @par * The CoAPS payload string to delete. */ template <> otError CoapSecure::Process(Arg aArgs[]) { return ProcessRequest(aArgs, OT_COAP_CODE_DELETE); } otError CoapSecure::ProcessRequest(Arg aArgs[], otCoapCode aCoapCode) { otError error = OT_ERROR_NONE; otMessage *message = nullptr; uint16_t payloadLength = 0; // Default parameters char coapUri[kMaxUriLength]; otCoapType coapType = OT_COAP_TYPE_NON_CONFIRMABLE; #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE bool coapBlock = false; otCoapBlockSzx coapBlockSize = OT_COAP_OPTION_BLOCK_SZX_16; BlockType coapBlockType = (aCoapCode == OT_COAP_CODE_GET) ? kBlockType2 : kBlockType1; #endif VerifyOrExit(!aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS); VerifyOrExit(aArgs[0].GetLength() < sizeof(coapUri), error = OT_ERROR_INVALID_ARGS); strcpy(coapUri, aArgs[0].GetCString()); if (!aArgs[1].IsEmpty()) { if (aArgs[1] == "con") { coapType = OT_COAP_TYPE_CONFIRMABLE; } #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE else if (aArgs[1] == "block-16") { coapType = OT_COAP_TYPE_CONFIRMABLE; coapBlock = true; coapBlockSize = OT_COAP_OPTION_BLOCK_SZX_16; } else if (aArgs[1] == "block-32") { coapType = OT_COAP_TYPE_CONFIRMABLE; coapBlock = true; coapBlockSize = OT_COAP_OPTION_BLOCK_SZX_32; } else if (aArgs[1] == "block-64") { coapType = OT_COAP_TYPE_CONFIRMABLE; coapBlock = true; coapBlockSize = OT_COAP_OPTION_BLOCK_SZX_64; } else if (aArgs[1] == "block-128") { coapType = OT_COAP_TYPE_CONFIRMABLE; coapBlock = true; coapBlockSize = OT_COAP_OPTION_BLOCK_SZX_128; } else if (aArgs[1] == "block-256") { coapType = OT_COAP_TYPE_CONFIRMABLE; coapBlock = true; coapBlockSize = OT_COAP_OPTION_BLOCK_SZX_256; } else if (aArgs[1] == "block-512") { coapType = OT_COAP_TYPE_CONFIRMABLE; coapBlock = true; coapBlockSize = OT_COAP_OPTION_BLOCK_SZX_512; } else if (aArgs[1] == "block-1024") { coapType = OT_COAP_TYPE_CONFIRMABLE; coapBlock = true; coapBlockSize = OT_COAP_OPTION_BLOCK_SZX_1024; } #endif // OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE } message = otCoapNewMessage(GetInstancePtr(), nullptr); VerifyOrExit(message != nullptr, error = OT_ERROR_NO_BUFS); otCoapMessageInit(message, coapType, aCoapCode); otCoapMessageGenerateToken(message, OT_COAP_DEFAULT_TOKEN_LENGTH); SuccessOrExit(error = otCoapMessageAppendUriPathOptions(message, coapUri)); #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE if (coapBlock) { if (coapBlockType == kBlockType1) { SuccessOrExit(error = otCoapMessageAppendBlock1Option(message, 0, true, coapBlockSize)); } else { SuccessOrExit(error = otCoapMessageAppendBlock2Option(message, 0, false, coapBlockSize)); } } #endif if (!aArgs[2].IsEmpty()) { #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE if (coapBlock) { SuccessOrExit(error = aArgs[2].ParseAsUint32(mBlockCount)); } else { #endif payloadLength = aArgs[2].GetLength(); if (payloadLength > 0) { SuccessOrExit(error = otCoapMessageSetPayloadMarker(message)); } #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE } #endif } if (payloadLength > 0) { SuccessOrExit(error = otMessageAppend(message, aArgs[2].GetCString(), payloadLength)); } if ((coapType == OT_COAP_TYPE_CONFIRMABLE) || (aCoapCode == OT_COAP_CODE_GET)) { #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE if (coapBlock) { error = otCoapSecureSendRequestBlockWise(GetInstancePtr(), message, &CoapSecure::HandleResponse, this, &CoapSecure::BlockwiseTransmitHook, &CoapSecure::BlockwiseReceiveHook); } else { #endif error = otCoapSecureSendRequest(GetInstancePtr(), message, &CoapSecure::HandleResponse, this); #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE } #endif } else { error = otCoapSecureSendRequest(GetInstancePtr(), message, nullptr, nullptr); } exit: if ((error != OT_ERROR_NONE) && (message != nullptr)) { otMessageFree(message); } return error; } /** * @cli coaps connect * @code * coaps connect fdde:ad00:beef:0:9903:14b:27e0:5744 * Done * coaps connected * @endcode * @cparam coaps connect @ca{address} * The `address` parameter is the IPv6 address of the peer. * @par * Initializes a Datagram Transport Layer Security (DTLS) session with a peer. * @moreinfo{@coaps}. * @sa otCoapSecureConnect */ template <> otError CoapSecure::Process(Arg aArgs[]) { otError error; otSockAddr sockaddr; ClearAllBytes(sockaddr); SuccessOrExit(error = aArgs[0].ParseAsIp6Address(sockaddr.mAddress)); sockaddr.mPort = OT_DEFAULT_COAP_SECURE_PORT; if (!aArgs[1].IsEmpty()) { SuccessOrExit(error = aArgs[1].ParseAsUint16(sockaddr.mPort)); } SuccessOrExit(error = otCoapSecureConnect(GetInstancePtr(), &sockaddr, &CoapSecure::HandleConnectEvent, this)); exit: return error; } /** * @cli coaps disconnect * @code * coaps disconnect * coaps disconnected * Done * @endcode * @par * Stops the DTLS session. * @sa otCoapSecureDisconnect */ template <> otError CoapSecure::Process(Arg aArgs[]) { OT_UNUSED_VARIABLE(aArgs); otCoapSecureDisconnect(GetInstancePtr()); return OT_ERROR_NONE; } /** * * @cli coaps psk * @code * coaps psk 1234 key1 * Done * @endcode * @cparam coaps psk @ca{psk-value} @ca{psk-id} * * `psk-value`: The pre-shared key * * `psk-id`: The pre-shared key identifier. * @par * Sets the pre-shared key (PSK) and cipher suite DTLS_PSK_WITH_AES_128_CCM_8. * @note This command requires the build-time feature * `MBEDTLS_KEY_EXCHANGE_PSK_ENABLED` to be enabled. * @sa #otCoapSecureSetPsk */ #ifdef MBEDTLS_KEY_EXCHANGE_PSK_ENABLED template <> otError CoapSecure::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; uint16_t length; VerifyOrExit(!aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS); length = aArgs[0].GetLength(); VerifyOrExit(length <= sizeof(mPsk), error = OT_ERROR_INVALID_ARGS); mPskLength = static_cast(length); memcpy(mPsk, aArgs[0].GetCString(), mPskLength); length = aArgs[1].GetLength(); VerifyOrExit(length <= sizeof(mPskId), error = OT_ERROR_INVALID_ARGS); mPskIdLength = static_cast(length); memcpy(mPskId, aArgs[1].GetCString(), mPskIdLength); otCoapSecureSetPsk(GetInstancePtr(), mPsk, mPskLength, mPskId, mPskIdLength); mUseCertificate = false; exit: return error; } #endif // MBEDTLS_KEY_EXCHANGE_PSK_ENABLED /** * * @cli coaps x509 * @code * coaps x509 * Done * @endcode * @par * Sets the X509 certificate of the local device with the corresponding private key for * the DTLS session with `DTLS_ECDHE_ECDSA_WITH_AES_128_CCM_8`. * @note This command requires `MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED=1` * to be enabled. * The X.509 certificate is stored in the location: `src/cli/x509_cert_key.hpp`. * @sa otCoapSecureSetCertificate * @sa otCoapSecureSetCaCertificateChain */ #ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED template <> otError CoapSecure::Process(Arg aArgs[]) { OT_UNUSED_VARIABLE(aArgs); otCoapSecureSetCertificate(GetInstancePtr(), reinterpret_cast(OT_CLI_COAPS_X509_CERT), sizeof(OT_CLI_COAPS_X509_CERT), reinterpret_cast(OT_CLI_COAPS_PRIV_KEY), sizeof(OT_CLI_COAPS_PRIV_KEY)); otCoapSecureSetCaCertificateChain(GetInstancePtr(), reinterpret_cast(OT_CLI_COAPS_TRUSTED_ROOT_CERTIFICATE), sizeof(OT_CLI_COAPS_TRUSTED_ROOT_CERTIFICATE)); mUseCertificate = true; return OT_ERROR_NONE; } #endif otError CoapSecure::Process(Arg aArgs[]) { #define CmdEntry(aCommandString) \ { \ aCommandString, &CoapSecure::Process \ } static constexpr Command kCommands[] = { CmdEntry("connect"), CmdEntry("delete"), CmdEntry("disconnect"), CmdEntry("get"), CmdEntry("isclosed"), CmdEntry("isconnactive"), CmdEntry("isconnected"), CmdEntry("post"), #ifdef MBEDTLS_KEY_EXCHANGE_PSK_ENABLED CmdEntry("psk"), #endif CmdEntry("put"), CmdEntry("resource"), CmdEntry("set"), CmdEntry("start"), CmdEntry("stop"), #ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED CmdEntry("x509"), #endif }; #undef CmdEntry static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted"); otError error = OT_ERROR_INVALID_COMMAND; const Command *command; if (aArgs[0].IsEmpty() || (aArgs[0] == "help")) { OutputCommandTable(kCommands); ExitNow(error = aArgs[0].IsEmpty() ? OT_ERROR_INVALID_ARGS : OT_ERROR_NONE); } command = BinarySearch::Find(aArgs[0].GetCString(), kCommands); VerifyOrExit(command != nullptr); error = (this->*command->mHandler)(aArgs + 1); exit: return error; } void CoapSecure::Stop(void) { #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE otCoapRemoveBlockWiseResource(GetInstancePtr(), &mResource); #else otCoapRemoveResource(GetInstancePtr(), &mResource); #endif otCoapSecureStop(GetInstancePtr()); } void CoapSecure::HandleConnectEvent(otCoapSecureConnectEvent aEvent, void *aContext) { static_cast(aContext)->HandleConnectEvent(aEvent); } void CoapSecure::HandleConnectEvent(otCoapSecureConnectEvent aEvent) { if (aEvent == OT_COAP_SECURE_CONNECTED) { OutputLine("coaps connected"); } else { OutputLine("coaps disconnected"); if (mShutdownFlag) { Stop(); mShutdownFlag = false; } } } void CoapSecure::HandleRequest(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) { static_cast(aContext)->HandleRequest(aMessage, aMessageInfo); } void CoapSecure::HandleRequest(otMessage *aMessage, const otMessageInfo *aMessageInfo) { otError error = OT_ERROR_NONE; otMessage *responseMessage = nullptr; otCoapCode responseCode = OT_COAP_CODE_EMPTY; #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE uint64_t blockValue = 0; bool blockPresent = false; otCoapOptionIterator iterator; #endif OutputFormat("coaps request from "); OutputIp6Address(aMessageInfo->mPeerAddr); OutputFormat(" "); switch (otCoapMessageGetCode(aMessage)) { case OT_COAP_CODE_GET: OutputFormat("GET"); #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE SuccessOrExit(error = otCoapOptionIteratorInit(&iterator, aMessage)); if (otCoapOptionIteratorGetFirstOptionMatching(&iterator, OT_COAP_OPTION_BLOCK2) != nullptr) { SuccessOrExit(error = otCoapOptionIteratorGetOptionUintValue(&iterator, &blockValue)); blockPresent = true; } #endif break; case OT_COAP_CODE_DELETE: OutputFormat("DELETE"); break; case OT_COAP_CODE_PUT: OutputFormat("PUT"); break; case OT_COAP_CODE_POST: OutputFormat("POST"); break; default: OutputLine("Undefined"); return; } PrintPayload(aMessage); if ((otCoapMessageGetType(aMessage) == OT_COAP_TYPE_CONFIRMABLE) || (otCoapMessageGetCode(aMessage) == OT_COAP_CODE_GET)) { if (otCoapMessageGetCode(aMessage) == OT_COAP_CODE_GET) { responseCode = OT_COAP_CODE_CONTENT; } else { responseCode = OT_COAP_CODE_VALID; } responseMessage = otCoapNewMessage(GetInstancePtr(), nullptr); VerifyOrExit(responseMessage != nullptr, error = OT_ERROR_NO_BUFS); SuccessOrExit( error = otCoapMessageInitResponse(responseMessage, aMessage, OT_COAP_TYPE_ACKNOWLEDGMENT, responseCode)); if (otCoapMessageGetCode(aMessage) == OT_COAP_CODE_GET) { #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE if (blockPresent) { SuccessOrExit(error = otCoapMessageAppendBlock2Option(responseMessage, static_cast(blockValue >> 4), true, static_cast(blockValue & 0x7))); } #endif SuccessOrExit(error = otCoapMessageSetPayloadMarker(responseMessage)); #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE if (!blockPresent) { #endif SuccessOrExit(error = otMessageAppend(responseMessage, &mResourceContent, static_cast(strlen(mResourceContent)))); #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE } #endif } #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE if (blockPresent) { SuccessOrExit(error = otCoapSecureSendResponseBlockWise(GetInstancePtr(), responseMessage, aMessageInfo, this, mResource.mTransmitHook)); } else { #endif SuccessOrExit(error = otCoapSecureSendResponse(GetInstancePtr(), responseMessage, aMessageInfo)); #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE } #endif } exit: if (error != OT_ERROR_NONE) { if (responseMessage != nullptr) { OutputLine("coaps send response error %d: %s", error, otThreadErrorToString(error)); otMessageFree(responseMessage); } } else if (responseCode >= OT_COAP_CODE_RESPONSE_MIN) { OutputLine("coaps response sent"); } } void CoapSecure::HandleResponse(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo, otError aError) { static_cast(aContext)->HandleResponse(aMessage, aMessageInfo, aError); } void CoapSecure::HandleResponse(otMessage *aMessage, const otMessageInfo *aMessageInfo, otError aError) { OT_UNUSED_VARIABLE(aMessageInfo); if (aError != OT_ERROR_NONE) { OutputLine("coaps receive response error %d: %s", aError, otThreadErrorToString(aError)); } else { OutputFormat("coaps response from "); OutputIp6Address(aMessageInfo->mPeerAddr); PrintPayload(aMessage); } } #if CLI_COAP_SECURE_USE_COAP_DEFAULT_HANDLER void CoapSecure::DefaultHandler(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) { static_cast(aContext)->DefaultHandler(aMessage, aMessageInfo); } void CoapSecure::DefaultHandler(otMessage *aMessage, const otMessageInfo *aMessageInfo) { otError error = OT_ERROR_NONE; otMessage *responseMessage = nullptr; if ((otCoapMessageGetType(aMessage) == OT_COAP_TYPE_CONFIRMABLE) || (otCoapMessageGetCode(aMessage) == OT_COAP_CODE_GET)) { responseMessage = otCoapNewMessage(GetInstancePtr(), nullptr); VerifyOrExit(responseMessage != nullptr, error = OT_ERROR_NO_BUFS); SuccessOrExit(error = otCoapMessageInitResponse(responseMessage, aMessage, OT_COAP_TYPE_NON_CONFIRMABLE, OT_COAP_CODE_NOT_FOUND)); SuccessOrExit(error = otCoapSecureSendResponse(GetInstancePtr(), responseMessage, aMessageInfo)); } exit: if (error != OT_ERROR_NONE && responseMessage != nullptr) { otMessageFree(responseMessage); } } #endif // CLI_COAP_SECURE_USE_COAP_DEFAULT_HANDLER #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE otError CoapSecure::BlockwiseReceiveHook(void *aContext, const uint8_t *aBlock, uint32_t aPosition, uint16_t aBlockLength, bool aMore, uint32_t aTotalLength) { return static_cast(aContext)->BlockwiseReceiveHook(aBlock, aPosition, aBlockLength, aMore, aTotalLength); } otError CoapSecure::BlockwiseReceiveHook(const uint8_t *aBlock, uint32_t aPosition, uint16_t aBlockLength, bool aMore, uint32_t aTotalLength) { OT_UNUSED_VARIABLE(aMore); OT_UNUSED_VARIABLE(aTotalLength); OutputLine("received block: Num %i Len %i", aPosition / aBlockLength, aBlockLength); for (uint16_t i = 0; i < aBlockLength / 16; i++) { OutputBytesLine(&aBlock[i * 16], 16); } return OT_ERROR_NONE; } otError CoapSecure::BlockwiseTransmitHook(void *aContext, uint8_t *aBlock, uint32_t aPosition, uint16_t *aBlockLength, bool *aMore) { return static_cast(aContext)->BlockwiseTransmitHook(aBlock, aPosition, aBlockLength, aMore); } otError CoapSecure::BlockwiseTransmitHook(uint8_t *aBlock, uint32_t aPosition, uint16_t *aBlockLength, bool *aMore) { static uint32_t blockCount = 0; OT_UNUSED_VARIABLE(aPosition); // Send a random payload otRandomNonCryptoFillBuffer(aBlock, *aBlockLength); OutputLine("send block: Num %i Len %i", blockCount, *aBlockLength); for (uint16_t i = 0; i < *aBlockLength / 16; i++) { OutputBytesLine(&aBlock[i * 16], 16); } if (blockCount == mBlockCount - 1) { blockCount = 0; *aMore = false; } else { *aMore = true; blockCount++; } return OT_ERROR_NONE; } #endif // OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE } // namespace Cli } // namespace ot #endif // OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE