blob: 332fa9dbe2bd1dcb7b3b999e104219cf26e8fbdd [file]
/*
* Copyright (c) 2020, 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 SRP Client.
*/
#include "cli_srp_client.hpp"
#if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE
#include <string.h>
#include "cli/cli.hpp"
#include "utils/parse_cmdline.hpp"
using ot::Utils::CmdLineParser::ParseAsBool;
using ot::Utils::CmdLineParser::ParseAsHexString;
using ot::Utils::CmdLineParser::ParseAsIp6Address;
using ot::Utils::CmdLineParser::ParseAsUint16;
using ot::Utils::CmdLineParser::ParseAsUint32;
namespace ot {
namespace Cli {
constexpr SrpClient::Command SrpClient::sCommands[];
template <uint16_t kDestSize> static otError CopyString(char (&aDestination)[kDestSize], const char *aSource)
{
// Copies a string from `aSource` to `aDestination` (char array),
// verifying that the string fits in the destination array.
otError error = OT_ERROR_NONE;
size_t len = strlen(aSource);
VerifyOrExit(len + 1 <= kDestSize, error = OT_ERROR_INVALID_ARGS);
memcpy(aDestination, aSource, len + 1);
exit:
return error;
}
SrpClient::SrpClient(Interpreter &aInterpreter)
: mInterpreter(aInterpreter)
, mCallbackEnabled(false)
{
for (Service &service : mServicePool)
{
service.MarkAsNotInUse();
}
memset(mHostAddresses, 0, sizeof(mHostAddresses));
otSrpClientSetCallback(mInterpreter.mInstance, SrpClient::HandleCallback, this);
}
otError SrpClient::Process(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_INVALID_COMMAND;
const Command *command;
VerifyOrExit(aArgsLength != 0, IgnoreError(ProcessHelp(0, nullptr)));
command = Utils::LookupTable::Find(aArgs[0], sCommands);
VerifyOrExit(command != nullptr);
error = (this->*command->mHandler)(aArgsLength - 1, aArgs + 1);
exit:
return error;
}
otError SrpClient::ProcessCallback(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
if (aArgsLength == 0)
{
mInterpreter.OutputLine(mCallbackEnabled ? "Enabled" : "Disabled");
ExitNow();
}
VerifyOrExit(aArgsLength == 1, error = OT_ERROR_INVALID_ARGS);
if (strcmp(aArgs[0], "enable") == 0)
{
mCallbackEnabled = true;
}
else if (strcmp(aArgs[0], "disable") == 0)
{
mCallbackEnabled = false;
}
else
{
error = OT_ERROR_INVALID_ARGS;
}
exit:
return error;
}
otError SrpClient::ProcessHelp(uint8_t aArgsLength, char *aArgs[])
{
OT_UNUSED_VARIABLE(aArgsLength);
OT_UNUSED_VARIABLE(aArgs);
for (const Command &command : sCommands)
{
mInterpreter.OutputLine(command.mName);
}
return OT_ERROR_NONE;
}
otError SrpClient::ProcessHost(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
if (aArgsLength == 0)
{
OutputHostInfo(0, *otSrpClientGetHostInfo(mInterpreter.mInstance));
ExitNow();
}
if (strcmp(aArgs[0], "name") == 0)
{
if (aArgsLength == 1)
{
const char *name = otSrpClientGetHostInfo(mInterpreter.mInstance)->mName;
mInterpreter.OutputLine("%s", (name != nullptr) ? name : "(null)");
}
else
{
VerifyOrExit(aArgsLength == 2, error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = CopyString(mHostName, aArgs[1]));
error = otSrpClientSetHostName(mInterpreter.mInstance, mHostName);
}
}
else if (strcmp(aArgs[0], "state") == 0)
{
VerifyOrExit(aArgsLength == 1, error = OT_ERROR_INVALID_ARGS);
mInterpreter.OutputLine("%s",
otSrpClientItemStateToString(otSrpClientGetHostInfo(mInterpreter.mInstance)->mState));
}
else if (strcmp(aArgs[0], "address") == 0)
{
if (aArgsLength == 1)
{
const otSrpClientHostInfo *hostInfo = otSrpClientGetHostInfo(mInterpreter.mInstance);
for (uint8_t index = 0; index < hostInfo->mNumAddresses; index++)
{
mInterpreter.OutputIp6Address(hostInfo->mAddresses[index]);
mInterpreter.OutputLine("");
}
}
else
{
uint8_t numAddresses;
otIp6Address addresses[kMaxHostAddresses];
numAddresses = 0;
memset(addresses, 0, sizeof(addresses));
while (aArgsLength > 1)
{
VerifyOrExit(numAddresses < kMaxHostAddresses, error = OT_ERROR_NO_BUFS);
aArgsLength--;
aArgs++;
SuccessOrExit(error = ParseAsIp6Address(aArgs[0], addresses[numAddresses++]));
}
VerifyOrExit(numAddresses > 0, error = OT_ERROR_INVALID_ARGS);
memcpy(mHostAddresses, addresses, sizeof(addresses));
error = otSrpClientSetHostAddresses(mInterpreter.mInstance, mHostAddresses, numAddresses);
}
}
else if (strcmp(aArgs[0], "remove") == 0)
{
bool removeKeyLease = false;
if (aArgsLength > 1)
{
VerifyOrExit(aArgsLength == 2, error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = ParseAsBool(aArgs[1], removeKeyLease));
}
error = otSrpClientRemoveHostAndServices(mInterpreter.mInstance, removeKeyLease);
}
else if (strcmp(aArgs[0], "clear") == 0)
{
VerifyOrExit(aArgsLength == 1, error = OT_ERROR_INVALID_ARGS);
otSrpClientClearHostAndServices(mInterpreter.mInstance);
for (Service &poolEntry : mServicePool)
{
poolEntry.MarkAsNotInUse();
}
}
else
{
error = OT_ERROR_INVALID_COMMAND;
}
exit:
return error;
}
otError SrpClient::ProcessLeaseInterval(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
uint32_t interval;
if (aArgsLength == 0)
{
mInterpreter.OutputLine("%u", otSrpClientGetLeaseInterval(mInterpreter.mInstance));
ExitNow();
}
VerifyOrExit(aArgsLength == 1, error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = ParseAsUint32(aArgs[0], interval));
otSrpClientSetLeaseInterval(mInterpreter.mInstance, interval);
exit:
return error;
}
otError SrpClient::ProcessKeyLeaseInterval(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
uint32_t interval;
if (aArgsLength == 0)
{
mInterpreter.OutputLine("%u", otSrpClientGetKeyLeaseInterval(mInterpreter.mInstance));
ExitNow();
}
VerifyOrExit(aArgsLength == 1, error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = ParseAsUint32(aArgs[0], interval));
otSrpClientSetKeyLeaseInterval(mInterpreter.mInstance, interval);
exit:
return error;
}
otError SrpClient::ProcessServer(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
const otSockAddr *serverSockAddr = otSrpClientGetServerAddress(mInterpreter.mInstance);
if (aArgsLength == 0)
{
mInterpreter.OutputFormat("[");
mInterpreter.OutputIp6Address(serverSockAddr->mAddress);
mInterpreter.OutputLine("]:%u", serverSockAddr->mPort);
ExitNow();
}
VerifyOrExit(aArgsLength == 1, error = OT_ERROR_INVALID_ARGS);
if (strcmp(aArgs[0], "address") == 0)
{
mInterpreter.OutputIp6Address(serverSockAddr->mAddress);
mInterpreter.OutputLine("");
}
else if (strcmp(aArgs[0], "port") == 0)
{
mInterpreter.OutputLine("%u", serverSockAddr->mPort);
}
else
{
error = OT_ERROR_INVALID_COMMAND;
}
exit:
return error;
}
otError SrpClient::ProcessService(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
if (aArgsLength == 0)
{
OutputServiceList(0, otSrpClientGetServices(mInterpreter.mInstance));
ExitNow();
}
if (strcmp(aArgs[0], "add") == 0)
{
// `add` <instance-name> <service-name> <port> [priority] [weight] [txt]
Service *entry = nullptr;
VerifyOrExit(4 <= aArgsLength && aArgsLength <= 7, error = OT_ERROR_INVALID_ARGS);
for (Service &poolEntry : mServicePool)
{
if (!poolEntry.IsInUse())
{
entry = &poolEntry;
break;
}
}
VerifyOrExit(entry != nullptr, error = OT_ERROR_NO_BUFS);
memset(&entry->mService, 0, sizeof(entry->mService));
SuccessOrExit(error = CopyString(entry->mInstanceName, aArgs[1]));
entry->mService.mInstanceName = entry->mInstanceName;
SuccessOrExit(error = CopyString(entry->mServiceName, aArgs[2]));
entry->mService.mName = entry->mServiceName;
SuccessOrExit(error = ParseAsUint16(aArgs[3], entry->mService.mPort));
if (aArgsLength >= 5)
{
SuccessOrExit(error = ParseAsUint16(aArgs[4], entry->mService.mPriority));
}
if (aArgsLength >= 6)
{
SuccessOrExit(error = ParseAsUint16(aArgs[5], entry->mService.mWeight));
}
if (aArgsLength >= 7)
{
entry->mService.mNumTxtEntries = 1;
entry->mService.mTxtEntries = &entry->mTxtEntry;
entry->mTxtEntry.mKey = nullptr; // Treat`mValue` as an already encoded TXT-DATA
entry->mTxtEntry.mKeyLength = 0;
entry->mTxtEntry.mValue = entry->mTxtBuffer;
entry->mTxtEntry.mValueLength = sizeof(entry->mTxtBuffer);
SuccessOrExit(error = ParseAsHexString(aArgs[6], entry->mTxtEntry.mValueLength, entry->mTxtBuffer));
}
error = otSrpClientAddService(mInterpreter.mInstance, &entry->mService);
if (error != OT_ERROR_NONE)
{
entry->MarkAsNotInUse();
}
}
else if (strcmp(aArgs[0], "remove") == 0)
{
// `remove` <instance-name> <service-name>
Service *entry = nullptr;
VerifyOrExit(aArgsLength == 3, error = OT_ERROR_INVALID_ARGS);
for (Service &poolEntry : mServicePool)
{
if (poolEntry.IsInUse() && (strcmp(aArgs[1], poolEntry.mInstanceName) == 0) &&
(strcmp(aArgs[2], poolEntry.mServiceName) == 0))
{
entry = &poolEntry;
break;
}
}
VerifyOrExit(entry != nullptr, error = OT_ERROR_NOT_FOUND);
error = otSrpClientRemoveService(mInterpreter.mInstance, &entry->mService);
}
else
{
error = OT_ERROR_INVALID_COMMAND;
}
exit:
return error;
}
void SrpClient::OutputHostInfo(uint8_t aIndentSize, const otSrpClientHostInfo &aHostInfo)
{
mInterpreter.OutputFormat(aIndentSize, "name:");
if (aHostInfo.mName != nullptr)
{
mInterpreter.OutputFormat("\"%s\"", aHostInfo.mName);
}
else
{
mInterpreter.OutputFormat("(null)");
}
mInterpreter.OutputFormat(", state:%s, addrs:[", otSrpClientItemStateToString(aHostInfo.mState));
for (uint8_t index = 0; index < aHostInfo.mNumAddresses; index++)
{
if (index > 0)
{
mInterpreter.OutputFormat(", ");
}
mInterpreter.OutputIp6Address(aHostInfo.mAddresses[index]);
}
mInterpreter.OutputLine("]");
}
void SrpClient::OutputServiceList(uint8_t aIndentSize, const otSrpClientService *aServices)
{
while (aServices != nullptr)
{
OutputService(aIndentSize, *aServices);
aServices = aServices->mNext;
}
}
void SrpClient::OutputService(uint8_t aIndentSize, const otSrpClientService &aService)
{
mInterpreter.OutputLine(aIndentSize, "instance:\"%s\", name:\"%s\", state:%s, port:%d, priority:%d, weight:%d",
aService.mInstanceName, aService.mName, otSrpClientItemStateToString(aService.mState),
aService.mPort, aService.mPriority, aService.mWeight);
}
otError SrpClient::ProcessStart(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
otSockAddr serverSockAddr;
VerifyOrExit(aArgsLength == 2, error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = ParseAsIp6Address(aArgs[0], serverSockAddr.mAddress));
SuccessOrExit(error = ParseAsUint16(aArgs[1], serverSockAddr.mPort));
error = otSrpClientStart(mInterpreter.mInstance, &serverSockAddr);
exit:
return error;
}
otError SrpClient::ProcessStop(uint8_t aArgsLength, char *aArgs[])
{
OT_UNUSED_VARIABLE(aArgs);
otError error = OT_ERROR_NONE;
VerifyOrExit(aArgsLength == 0, error = OT_ERROR_INVALID_ARGS);
otSrpClientStop(mInterpreter.mInstance);
exit:
return error;
}
void SrpClient::HandleCallback(otError aError,
const otSrpClientHostInfo *aHostInfo,
const otSrpClientService * aServices,
const otSrpClientService * aRemovedServices,
void * aContext)
{
static_cast<SrpClient *>(aContext)->HandleCallback(aError, aHostInfo, aServices, aRemovedServices);
}
void SrpClient::HandleCallback(otError aError,
const otSrpClientHostInfo *aHostInfo,
const otSrpClientService * aServices,
const otSrpClientService * aRemovedServices)
{
otSrpClientService *next;
if (mCallbackEnabled)
{
mInterpreter.OutputLine("SRP client callback - error:%s", otThreadErrorToString(aError));
mInterpreter.OutputLine("Host info:");
OutputHostInfo(kIndentSize, *aHostInfo);
mInterpreter.OutputLine("Service list:");
OutputServiceList(kIndentSize, aServices);
if (aRemovedServices != nullptr)
{
mInterpreter.OutputLine("Removed service list:");
OutputServiceList(kIndentSize, aRemovedServices);
}
}
// Go through removed services and mark the corresponding entry in
// `mServicePool` as "not in use".
for (const otSrpClientService *service = aRemovedServices; service != nullptr; service = next)
{
next = service->mNext;
for (Service &poolEntry : mServicePool)
{
if (service == &poolEntry.mService)
{
poolEntry.MarkAsNotInUse();
break;
}
}
}
}
} // namespace Cli
} // namespace ot
#endif // OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE