| /* |
| * 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" |
| |
| namespace ot { |
| namespace Cli { |
| |
| static otError CopyString(char *aDest, uint16_t aDestSize, 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 <= aDestSize, error = OT_ERROR_INVALID_ARGS); |
| memcpy(aDest, aSource, len + 1); |
| |
| exit: |
| return error; |
| } |
| |
| SrpClient::SrpClient(otInstance *aInstance, OutputImplementer &aOutputImplementer) |
| : Utils(aInstance, aOutputImplementer) |
| , mCallbackEnabled(false) |
| { |
| otSrpClientSetCallback(GetInstancePtr(), SrpClient::HandleCallback, this); |
| } |
| |
| #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE |
| |
| template <> otError SrpClient::Process<Cmd("autostart")>(Arg aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| bool enable; |
| |
| /** |
| * @cli srp client autostart (get) |
| * @code |
| * srp client autostart |
| * Disabled |
| * Done |
| * @endcode |
| * @par |
| * Indicates the current state of auto-start mode (enabled or disabled). |
| * @moreinfo{@srp}. |
| * @sa otSrpClientIsAutoStartModeEnabled |
| */ |
| if (aArgs[0].IsEmpty()) |
| { |
| OutputEnabledDisabledStatus(otSrpClientIsAutoStartModeEnabled(GetInstancePtr())); |
| ExitNow(); |
| } |
| |
| SuccessOrExit(error = Interpreter::ParseEnableOrDisable(aArgs[0], enable)); |
| |
| /** |
| * @cli srp client autostart enable |
| * @code |
| * srp client autostart enable |
| * Done |
| * @endcode |
| * @par |
| * Enables auto-start mode. |
| * @par |
| * When auto-start is enabled, the SRP client monitors Thread |
| * network data to discover SRP servers, to select the preferred |
| * server, and to automatically start and stop the client when |
| * an SRP server is detected. |
| * @par |
| * Three categories of network data entries indicate the presence of an SRP sever, |
| * and are preferred in the following order: |
| * -# Unicast entries in which the server address is included in the service |
| * data. If there are multiple options, the option with the lowest numerical |
| * IPv6 address is preferred. |
| * -# Anycast entries that each have a sequence number. The largest sequence |
| * number as specified by Serial Number Arithmetic Logic |
| * in RFC-1982 is preferred. |
| * -# Unicast entries in which the server address information is included |
| * with the server data. If there are multiple options, the option with the |
| * lowest numerical IPv6 address is preferred. |
| * @sa otSrpClientEnableAutoStartMode |
| */ |
| if (enable) |
| { |
| otSrpClientEnableAutoStartMode(GetInstancePtr(), /* aCallback */ nullptr, /* aContext */ nullptr); |
| } |
| /** |
| * @cli srp client autostart disable |
| * @code |
| * srp client autostart disable |
| * Done |
| * @endcode |
| * @par |
| * Disables the auto-start mode. |
| * @par |
| * Disabling auto-start mode does not stop a running client. |
| * However, the SRP client stops monitoring Thread network data. |
| * @sa otSrpClientDisableAutoStartMode |
| */ |
| else |
| { |
| otSrpClientDisableAutoStartMode(GetInstancePtr()); |
| } |
| |
| exit: |
| return error; |
| } |
| |
| #endif // OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE |
| |
| /** |
| * @cli srp client callback (get,enable,disable) |
| * @code |
| * srp client callback enable |
| * Done |
| * @endcode |
| * @code |
| * srp client callback |
| * Enabled |
| * Done |
| * @endcode |
| * @cparam srp client callback [@ca{enable}|@ca{disable}] |
| * @par |
| * Gets or enables/disables printing callback events from the SRP client. |
| * @moreinfo{@srp}. |
| * @sa otSrpClientSetCallback |
| */ |
| template <> otError SrpClient::Process<Cmd("callback")>(Arg aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| if (aArgs[0].IsEmpty()) |
| { |
| OutputEnabledDisabledStatus(mCallbackEnabled); |
| ExitNow(); |
| } |
| |
| error = Interpreter::ParseEnableOrDisable(aArgs[0], mCallbackEnabled); |
| |
| exit: |
| return error; |
| } |
| |
| template <> otError SrpClient::Process<Cmd("host")>(Arg aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| /** |
| * @cli srp client host |
| * @code |
| * srp client host |
| * name:"dev4312", state:Registered, addrs:[fd00:0:0:0:0:0:0:1234, fd00:0:0:0:0:0:0:beef] |
| * Done |
| * @endcode |
| * @par api_copy |
| * #otSrpClientGetHostInfo |
| */ |
| if (aArgs[0].IsEmpty()) |
| { |
| OutputHostInfo(0, *otSrpClientGetHostInfo(GetInstancePtr())); |
| } |
| /** |
| * @cli srp client host name (get,set) |
| * @code |
| * srp client host name dev4312 |
| * Done |
| * @endcode |
| * @code |
| * srp client host name |
| * dev4312 |
| * Done |
| * @endcode |
| * @cparam srp client host name [@ca{name}] |
| * To set the client host name when the host has either been removed or not yet |
| * registered with the server, use the `name` parameter. |
| * @par |
| * Gets or sets the host name of the SRP client. @moreinfo{@srp}. |
| * @sa otSrpClientSetHostName |
| */ |
| else if (aArgs[0] == "name") |
| { |
| if (aArgs[1].IsEmpty()) |
| { |
| const char *name = otSrpClientGetHostInfo(GetInstancePtr())->mName; |
| OutputLine("%s", (name != nullptr) ? name : "(null)"); |
| } |
| else |
| { |
| uint16_t len; |
| uint16_t size; |
| char *hostName; |
| |
| VerifyOrExit(aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS); |
| hostName = otSrpClientBuffersGetHostNameString(GetInstancePtr(), &size); |
| |
| len = aArgs[1].GetLength(); |
| VerifyOrExit(len + 1 <= size, error = OT_ERROR_INVALID_ARGS); |
| |
| // We first make sure we can set the name, and if so |
| // we copy it to the persisted string buffer and set |
| // the host name again now with the persisted buffer. |
| // This ensures that we do not overwrite a previous |
| // buffer with a host name that cannot be set. |
| |
| SuccessOrExit(error = otSrpClientSetHostName(GetInstancePtr(), aArgs[1].GetCString())); |
| memcpy(hostName, aArgs[1].GetCString(), len + 1); |
| |
| IgnoreError(otSrpClientSetHostName(GetInstancePtr(), hostName)); |
| } |
| } |
| /** |
| * @cli srp client host state |
| * @code |
| * srp client host state |
| * Registered |
| * Done |
| * @endcode |
| * @par |
| * Returns the state of the SRP client host. Possible states: |
| * * `ToAdd`: Item to be added/registered. |
| * * `Adding`: Item is being added/registered. |
| * * `ToRefresh`: Item to be refreshed for lease renewal. |
| * * `Refreshing`: Item is beig refreshed. |
| * * `ToRemove`: Item to be removed. |
| * * `Removing`: Item is being removed. |
| * * `Registered`: Item is registered with server. |
| * * `Removed`: Item has been removed. |
| */ |
| else if (aArgs[0] == "state") |
| { |
| VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS); |
| OutputLine("%s", otSrpClientItemStateToString(otSrpClientGetHostInfo(GetInstancePtr())->mState)); |
| } |
| else if (aArgs[0] == "address") |
| { |
| /** |
| * @cli srp client host address (get) |
| * @code |
| * srp client host address |
| * auto |
| * Done |
| * @endcode |
| * @code |
| * srp client host address |
| * fd00:0:0:0:0:0:0:1234 |
| * fd00:0:0:0:0:0:0:beef |
| * Done |
| * @endcode |
| * @par |
| * Indicates whether auto address mode is enabled. If auto address mode is not |
| * enabled, then the list of SRP client host addresses is returned. |
| * @moreinfo{@srp}. |
| * @sa otSrpClientGetHostInfo |
| */ |
| if (aArgs[1].IsEmpty()) |
| { |
| const otSrpClientHostInfo *hostInfo = otSrpClientGetHostInfo(GetInstancePtr()); |
| |
| if (hostInfo->mAutoAddress) |
| { |
| OutputLine("auto"); |
| } |
| else |
| { |
| for (uint8_t index = 0; index < hostInfo->mNumAddresses; index++) |
| { |
| OutputIp6AddressLine(hostInfo->mAddresses[index]); |
| } |
| } |
| } |
| /** |
| * @cli srp client host address (set) |
| * @code |
| * srp client host address auto |
| * Done |
| * @endcode |
| * @code |
| * srp client host address fd00::cafe |
| * Done |
| * @endcode |
| * @cparam srp client host address [auto|@ca{address...}] |
| * * Use the `auto` parameter to enable auto host address mode. |
| * When enabled, the client automatically uses all preferred Thread |
| * `netif` unicast addresses except for link-local and mesh-local |
| * addresses. If there is no valid address, the mesh local |
| * EID address gets added. The SRP client automatically |
| * re-registers if addresses on the Thread `netif` are |
| * added or removed or marked as non-preferred. |
| * * Explicitly specify the list of host addresses, separating |
| * each address by a space. You can set this list while the client is |
| * running. This will also disable auto host address mode. |
| * @par |
| * Enable auto host address mode or explicitly set the list of host |
| * addresses. @moreinfo{@srp}. |
| * @sa otSrpClientEnableAutoHostAddress |
| * @sa otSrpClientSetHostAddresses |
| */ |
| else if (aArgs[1] == "auto") |
| { |
| error = otSrpClientEnableAutoHostAddress(GetInstancePtr()); |
| } |
| else |
| { |
| uint8_t numAddresses = 0; |
| otIp6Address addresses[kMaxHostAddresses]; |
| uint8_t arrayLength; |
| otIp6Address *hostAddressArray; |
| |
| hostAddressArray = otSrpClientBuffersGetHostAddressesArray(GetInstancePtr(), &arrayLength); |
| |
| // We first make sure we can set the addresses, and if so |
| // we copy the address list into the persisted address array |
| // and set it again. This ensures that we do not overwrite |
| // a previous list before we know it is safe to set/change |
| // the address list. |
| |
| if (arrayLength > kMaxHostAddresses) |
| { |
| arrayLength = kMaxHostAddresses; |
| } |
| |
| for (Arg *arg = &aArgs[1]; !arg->IsEmpty(); arg++) |
| { |
| VerifyOrExit(numAddresses < arrayLength, error = OT_ERROR_NO_BUFS); |
| SuccessOrExit(error = arg->ParseAsIp6Address(addresses[numAddresses])); |
| numAddresses++; |
| } |
| |
| SuccessOrExit(error = otSrpClientSetHostAddresses(GetInstancePtr(), addresses, numAddresses)); |
| |
| memcpy(hostAddressArray, addresses, numAddresses * sizeof(hostAddressArray[0])); |
| IgnoreError(otSrpClientSetHostAddresses(GetInstancePtr(), hostAddressArray, numAddresses)); |
| } |
| } |
| /** |
| * @cli srp client host remove |
| * @code |
| * srp client host remove 1 |
| * Done |
| * @endcode |
| * @cparam srp client host remove [@ca{removekeylease}] [@ca{sendunregtoserver}] |
| * * The parameter `removekeylease` is an optional boolean value that indicates |
| * whether the host key lease should also be removed (default is `false`). |
| * * The parameter `sendunregtoserver` is an optional boolean value that indicates |
| * whether the client host should send an "update" message to the server |
| * even when the client host information has not yet been registered with the |
| * server (default is `false`). This parameter can be specified only if the |
| * `removekeylease` parameter is specified first in the command. |
| * @par |
| * Removes SRP client host information and all services from the SRP server. |
| * @moreinfo{@srp}. |
| * @sa otSrpClientRemoveHostAndServices |
| * @sa otSrpClientSetHostName |
| */ |
| else if (aArgs[0] == "remove") |
| { |
| bool removeKeyLease = false; |
| bool sendUnregToServer = false; |
| |
| if (!aArgs[1].IsEmpty()) |
| { |
| SuccessOrExit(error = aArgs[1].ParseAsBool(removeKeyLease)); |
| |
| if (!aArgs[2].IsEmpty()) |
| { |
| SuccessOrExit(error = aArgs[2].ParseAsBool(sendUnregToServer)); |
| VerifyOrExit(aArgs[3].IsEmpty(), error = OT_ERROR_INVALID_ARGS); |
| } |
| } |
| |
| error = otSrpClientRemoveHostAndServices(GetInstancePtr(), removeKeyLease, sendUnregToServer); |
| } |
| /** |
| * @cli srp client host clear |
| * @code |
| * srp client host clear |
| * Done |
| * @endcode |
| * @par |
| * Clears all host information and all services. |
| * @sa otSrpClientBuffersFreeAllServices |
| * @sa otSrpClientClearHostAndServices |
| */ |
| else if (aArgs[0] == "clear") |
| { |
| VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS); |
| otSrpClientClearHostAndServices(GetInstancePtr()); |
| otSrpClientBuffersFreeAllServices(GetInstancePtr()); |
| } |
| else |
| { |
| error = OT_ERROR_INVALID_COMMAND; |
| } |
| |
| exit: |
| return error; |
| } |
| |
| /** |
| * @cli srp client leaseinterval (get,set) |
| * @code |
| * srp client leaseinterval 3600 |
| * Done |
| * @endcode |
| * @code |
| * srp client leaseinterval |
| * 3600 |
| * Done |
| * @endcode |
| * @cparam srp client leaseinterval [@ca{interval}] |
| * @par |
| * Gets or sets the lease interval in seconds. |
| * @sa otSrpClientGetLeaseInterval |
| * @sa otSrpClientSetLeaseInterval |
| */ |
| template <> otError SrpClient::Process<Cmd("leaseinterval")>(Arg aArgs[]) |
| { |
| return ProcessGetSet(aArgs, otSrpClientGetLeaseInterval, otSrpClientSetLeaseInterval); |
| } |
| |
| /** |
| * @cli srp client keyleaseinterval (get,set) |
| * @code |
| * srp client keyleaseinterval 864000 |
| * Done |
| * @endcode |
| * @code |
| * srp client keyleaseinterval |
| * 864000 |
| * Done |
| * @endcode |
| * @cparam srp client keyleaseinterval [@ca{interval}] |
| * @par |
| * Gets or sets the key lease interval in seconds. |
| * @sa otSrpClientGetKeyLeaseInterval |
| * @sa otSrpClientSetKeyLeaseInterval |
| */ |
| template <> otError SrpClient::Process<Cmd("keyleaseinterval")>(Arg aArgs[]) |
| { |
| return ProcessGetSet(aArgs, otSrpClientGetKeyLeaseInterval, otSrpClientSetKeyLeaseInterval); |
| } |
| |
| template <> otError SrpClient::Process<Cmd("server")>(Arg aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| const otSockAddr *serverSockAddr = otSrpClientGetServerAddress(GetInstancePtr()); |
| |
| /** |
| * @cli srp client server |
| * @code |
| * srp client server |
| * [fd00:0:0:0:d88a:618b:384d:e760]:4724 |
| * Done |
| * @endcode |
| * @par |
| * Gets the socket address (IPv6 address and port number) of the SRP server |
| * that is being used by the SRP client. If the client is not running, the address |
| * is unspecified (all zeros) with a port number of 0. @moreinfo{@srp}. |
| * @sa otSrpClientGetServerAddress |
| */ |
| if (aArgs[0].IsEmpty()) |
| { |
| OutputSockAddrLine(*serverSockAddr); |
| ExitNow(); |
| } |
| |
| VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS); |
| |
| /** |
| * @cli srp client server address |
| * @code |
| * srp client server address |
| * fd00:0:0:0:d88a:618b:384d:e760 |
| * Done |
| * @endcode |
| * @par |
| * Returns the server's IPv6 address. |
| */ |
| if (aArgs[0] == "address") |
| { |
| OutputIp6AddressLine(serverSockAddr->mAddress); |
| } |
| /** |
| * @cli srp client server port |
| * @code |
| * srp client server port |
| * 4724 |
| * Done |
| * @endcode |
| * @par |
| * Returns the server's port number. |
| */ |
| else if (aArgs[0] == "port") |
| { |
| OutputLine("%u", serverSockAddr->mPort); |
| } |
| else |
| { |
| error = OT_ERROR_INVALID_COMMAND; |
| } |
| |
| exit: |
| return error; |
| } |
| |
| template <> otError SrpClient::Process<Cmd("service")>(Arg aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| bool isRemove; |
| |
| /** |
| * @cli srp client service |
| * @code |
| * srp client service |
| * instance:"ins2", name:"_test2._udp,_sub1,_sub2", state:Registered, port:111, priority:1, weight:1 |
| * instance:"ins1", name:"_test1._udp", state:Registered, port:777, priority:0, weight:0 |
| * Done |
| * @endcode |
| * @par api_copy |
| * #otSrpClientGetServices |
| */ |
| if (aArgs[0].IsEmpty()) |
| { |
| OutputServiceList(0, otSrpClientGetServices(GetInstancePtr())); |
| } |
| /** |
| * @cli srp client service add |
| * @code |
| * srp client service add ins1 _test1._udp 777 |
| * Done |
| * @endcode |
| * @code |
| * srp client service add ins2 _test2._udp,_sub1,_sub2 111 1 1 |
| * Done |
| * @endcode |
| * @cparam srp client service add @ca{instancename} @ca{servicename} <!-- |
| * * --> @ca{port} [@ca{priority}] <!-- |
| * * --> [@ca{weight}] [@ca{txt}] |
| * The `servicename` parameter can optionally include a list of service subtype labels that are |
| * separated by commas. The examples here use generic naming. The `priority` and `weight` (both are `uint16_t` |
| * values) parameters are optional, and if not provided zero is used. The optional `txt` parameter sets the TXT |
| * data associated with the service. The `txt` value must be in hex-string format and is treated as an already |
| * encoded TXT data byte sequence. |
| * @par |
| * Adds a service with a given instance name, service name, and port number. |
| * @moreinfo{@srp}. |
| * @sa otSrpClientAddService |
| */ |
| else if (aArgs[0] == "add") |
| { |
| error = ProcessServiceAdd(aArgs); |
| } |
| /** |
| * @cli srp client service remove |
| * @code |
| * srp client service remove ins2 _test2._udp |
| * Done |
| * @endcode |
| * @cparam srp client service remove @ca{instancename} @ca{servicename} |
| * @par |
| * Requests a service to be unregistered with the SRP server. |
| * @sa otSrpClientRemoveService |
| */ |
| /** |
| * @cli srp client service name clear |
| * @code |
| * srp client service clear ins2 _test2._udp |
| * Done |
| * @endcode |
| * @cparam srp client service clear @ca{instancename} @ca{servicename} |
| * @par |
| * Clears a service, immediately removing it from the client service list, |
| * with no interaction with the SRP server. |
| * @sa otSrpClientClearService |
| */ |
| else if ((isRemove = (aArgs[0] == "remove")) || (aArgs[0] == "clear")) |
| { |
| // `remove`|`clear` <instance-name> <service-name> |
| |
| const otSrpClientService *service; |
| |
| VerifyOrExit(!aArgs[2].IsEmpty() && aArgs[3].IsEmpty(), error = OT_ERROR_INVALID_ARGS); |
| |
| for (service = otSrpClientGetServices(GetInstancePtr()); service != nullptr; service = service->mNext) |
| { |
| if ((aArgs[1] == service->mInstanceName) && (aArgs[2] == service->mName)) |
| { |
| break; |
| } |
| } |
| |
| VerifyOrExit(service != nullptr, error = OT_ERROR_NOT_FOUND); |
| |
| if (isRemove) |
| { |
| error = otSrpClientRemoveService(GetInstancePtr(), const_cast<otSrpClientService *>(service)); |
| } |
| else |
| { |
| SuccessOrExit(error = otSrpClientClearService(GetInstancePtr(), const_cast<otSrpClientService *>(service))); |
| |
| otSrpClientBuffersFreeService(GetInstancePtr(), reinterpret_cast<otSrpClientBuffersServiceEntry *>( |
| const_cast<otSrpClientService *>(service))); |
| } |
| } |
| #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE |
| /** |
| * @cli srp client service key (get,set) |
| * @code |
| * srp client service key enable |
| * Done |
| * @endcode |
| * @code |
| * srp client service key |
| * Enabled |
| * Done |
| * @endcode |
| * @par |
| * Gets or sets the service key record inclusion mode in the SRP client. |
| * This command is intended for testing only, and requires that |
| * `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` be enabled. @moreinfo{@srp}. |
| * @sa otSrpClientIsServiceKeyRecordEnabled |
| */ |
| else if (aArgs[0] == "key") |
| { |
| // `key [enable/disable]` |
| |
| error = ProcessEnableDisable(aArgs + 1, otSrpClientIsServiceKeyRecordEnabled, |
| otSrpClientSetServiceKeyRecordEnabled); |
| } |
| #endif // OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE |
| else |
| { |
| error = OT_ERROR_INVALID_COMMAND; |
| } |
| |
| exit: |
| return error; |
| } |
| |
| otError SrpClient::ProcessServiceAdd(Arg aArgs[]) |
| { |
| // `add` <instance-name> <service-name> <port> [priority] [weight] [txt] [lease] [key-lease] |
| |
| otSrpClientBuffersServiceEntry *entry = nullptr; |
| uint16_t size; |
| char *string; |
| otError error; |
| char *label; |
| |
| entry = otSrpClientBuffersAllocateService(GetInstancePtr()); |
| |
| VerifyOrExit(entry != nullptr, error = OT_ERROR_NO_BUFS); |
| |
| SuccessOrExit(error = aArgs[3].ParseAsUint16(entry->mService.mPort)); |
| |
| // Successfully parsing aArgs[3] indicates that aArgs[1] and |
| // aArgs[2] are also non-empty. |
| |
| string = otSrpClientBuffersGetServiceEntryInstanceNameString(entry, &size); |
| SuccessOrExit(error = CopyString(string, size, aArgs[1].GetCString())); |
| |
| string = otSrpClientBuffersGetServiceEntryServiceNameString(entry, &size); |
| SuccessOrExit(error = CopyString(string, size, aArgs[2].GetCString())); |
| |
| // Service subtypes are added as part of service name as a comma separated list |
| // e.g., "_service._udp,_sub1,_sub2" |
| |
| label = strchr(string, ','); |
| |
| if (label != nullptr) |
| { |
| uint16_t arrayLength; |
| const char **subTypeLabels = otSrpClientBuffersGetSubTypeLabelsArray(entry, &arrayLength); |
| |
| // Leave the last array element as `nullptr` to indicate end of array. |
| for (uint16_t index = 0; index + 1 < arrayLength; index++) |
| { |
| *label++ = '\0'; |
| subTypeLabels[index] = label; |
| |
| label = strchr(label, ','); |
| |
| if (label == nullptr) |
| { |
| break; |
| } |
| } |
| |
| VerifyOrExit(label == nullptr, error = OT_ERROR_NO_BUFS); |
| } |
| |
| SuccessOrExit(error = aArgs[3].ParseAsUint16(entry->mService.mPort)); |
| |
| if (!aArgs[4].IsEmpty()) |
| { |
| SuccessOrExit(error = aArgs[4].ParseAsUint16(entry->mService.mPriority)); |
| } |
| |
| if (!aArgs[5].IsEmpty()) |
| { |
| SuccessOrExit(error = aArgs[5].ParseAsUint16(entry->mService.mWeight)); |
| } |
| |
| if (!aArgs[6].IsEmpty() && (aArgs[6] != "-")) |
| { |
| uint8_t *txtBuffer; |
| |
| txtBuffer = otSrpClientBuffersGetServiceEntryTxtBuffer(entry, &size); |
| entry->mTxtEntry.mValueLength = size; |
| |
| SuccessOrExit(error = aArgs[6].ParseAsHexString(entry->mTxtEntry.mValueLength, txtBuffer)); |
| } |
| else |
| { |
| entry->mService.mNumTxtEntries = 0; |
| } |
| |
| if (!aArgs[7].IsEmpty()) |
| { |
| SuccessOrExit(error = aArgs[7].ParseAsUint32(entry->mService.mLease)); |
| } |
| |
| if (!aArgs[8].IsEmpty()) |
| { |
| SuccessOrExit(error = aArgs[8].ParseAsUint32(entry->mService.mKeyLease)); |
| VerifyOrExit(aArgs[9].IsEmpty(), error = OT_ERROR_INVALID_ARGS); |
| } |
| |
| SuccessOrExit(error = otSrpClientAddService(GetInstancePtr(), &entry->mService)); |
| |
| entry = nullptr; |
| |
| exit: |
| if (entry != nullptr) |
| { |
| otSrpClientBuffersFreeService(GetInstancePtr(), entry); |
| } |
| |
| return error; |
| } |
| |
| void SrpClient::OutputHostInfo(uint8_t aIndentSize, const otSrpClientHostInfo &aHostInfo) |
| { |
| OutputFormat(aIndentSize, "name:"); |
| |
| if (aHostInfo.mName != nullptr) |
| { |
| OutputFormat("\"%s\"", aHostInfo.mName); |
| } |
| else |
| { |
| OutputFormat("(null)"); |
| } |
| |
| OutputFormat(", state:%s, addrs:", otSrpClientItemStateToString(aHostInfo.mState)); |
| |
| if (aHostInfo.mAutoAddress) |
| { |
| OutputLine("auto"); |
| } |
| else |
| { |
| OutputFormat("["); |
| |
| for (uint8_t index = 0; index < aHostInfo.mNumAddresses; index++) |
| { |
| if (index > 0) |
| { |
| OutputFormat(", "); |
| } |
| |
| OutputIp6Address(aHostInfo.mAddresses[index]); |
| } |
| |
| 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) |
| { |
| OutputFormat(aIndentSize, "instance:\"%s\", name:\"%s", aService.mInstanceName, aService.mName); |
| |
| if (aService.mSubTypeLabels != nullptr) |
| { |
| for (uint16_t index = 0; aService.mSubTypeLabels[index] != nullptr; index++) |
| { |
| OutputFormat(",%s", aService.mSubTypeLabels[index]); |
| } |
| } |
| |
| OutputLine("\", state:%s, port:%d, priority:%d, weight:%d", otSrpClientItemStateToString(aService.mState), |
| aService.mPort, aService.mPriority, aService.mWeight); |
| } |
| |
| /** |
| * @cli srp client start |
| * @code |
| * srp client start fd00::d88a:618b:384d:e760 4724 |
| * Done |
| * @endcode |
| * @cparam srp client start @ca{serveraddr} @ca{serverport} |
| * @par |
| * Starts the SRP client operation. @moreinfo{@srp}. |
| * @sa otSrpClientStart |
| */ |
| template <> otError SrpClient::Process<Cmd("start")>(Arg aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| otSockAddr serverSockAddr; |
| |
| SuccessOrExit(error = aArgs[0].ParseAsIp6Address(serverSockAddr.mAddress)); |
| SuccessOrExit(error = aArgs[1].ParseAsUint16(serverSockAddr.mPort)); |
| VerifyOrExit(aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS); |
| |
| error = otSrpClientStart(GetInstancePtr(), &serverSockAddr); |
| |
| exit: |
| return error; |
| } |
| |
| /** |
| * @cli srp client state |
| * @code |
| * srp client state |
| * Enabled |
| * Done |
| * @endcode |
| * @par api_copy |
| * #otSrpClientIsRunning |
| * @moreinfo{@srp}. |
| */ |
| template <> otError SrpClient::Process<Cmd("state")>(Arg aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS); |
| |
| OutputEnabledDisabledStatus(otSrpClientIsRunning(GetInstancePtr())); |
| |
| exit: |
| return error; |
| } |
| |
| /** |
| * @cli srp client stop |
| * @code |
| * srp client stop |
| * Done |
| * @endcode |
| * @par api_copy |
| * #otSrpClientStop |
| */ |
| template <> otError SrpClient::Process<Cmd("stop")>(Arg aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS); |
| otSrpClientStop(GetInstancePtr()); |
| |
| exit: |
| return error; |
| } |
| |
| /** |
| * @cli srp client ttl (get,set) |
| * @code |
| * srp client ttl 3600 |
| * Done |
| * @endcode |
| * @code |
| * srp client ttl |
| * 3600 |
| * Done |
| * @endcode |
| * @cparam srp client ttl [@ca{value}] |
| * @par |
| * Gets or sets the `ttl`(time to live) value in seconds. |
| * @sa otSrpClientGetTtl |
| * @sa otSrpClientSetTtl |
| */ |
| template <> otError SrpClient::Process<Cmd("ttl")>(Arg aArgs[]) |
| { |
| return ProcessGetSet(aArgs, otSrpClientGetTtl, otSrpClientSetTtl); |
| } |
| |
| 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) |
| { |
| OutputLine("SRP client callback - error:%s", otThreadErrorToString(aError)); |
| OutputLine("Host info:"); |
| OutputHostInfo(kIndentSize, *aHostInfo); |
| |
| OutputLine("Service list:"); |
| OutputServiceList(kIndentSize, aServices); |
| |
| if (aRemovedServices != nullptr) |
| { |
| OutputLine("Removed service list:"); |
| OutputServiceList(kIndentSize, aRemovedServices); |
| } |
| } |
| |
| // Go through removed services and free all removed services |
| |
| for (const otSrpClientService *service = aRemovedServices; service != nullptr; service = next) |
| { |
| next = service->mNext; |
| |
| otSrpClientBuffersFreeService(GetInstancePtr(), reinterpret_cast<otSrpClientBuffersServiceEntry *>( |
| const_cast<otSrpClientService *>(service))); |
| } |
| } |
| |
| otError SrpClient::Process(Arg aArgs[]) |
| { |
| #define CmdEntry(aCommandString) \ |
| { \ |
| aCommandString, &SrpClient::Process<Cmd(aCommandString)> \ |
| } |
| |
| static constexpr Command kCommands[] = { |
| CmdEntry("autostart"), CmdEntry("callback"), CmdEntry("host"), CmdEntry("keyleaseinterval"), |
| CmdEntry("leaseinterval"), CmdEntry("server"), CmdEntry("service"), CmdEntry("start"), |
| CmdEntry("state"), CmdEntry("stop"), CmdEntry("ttl"), |
| }; |
| |
| 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() ? error : OT_ERROR_NONE); |
| } |
| |
| command = BinarySearch::Find(aArgs[0].GetCString(), kCommands); |
| VerifyOrExit(command != nullptr); |
| |
| error = (this->*command->mHandler)(aArgs + 1); |
| |
| exit: |
| return error; |
| } |
| |
| } // namespace Cli |
| } // namespace ot |
| |
| #endif // OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE |