diff --git a/src/lib/spinel/spinel.c b/src/lib/spinel/spinel.c index 7595a7f55..9e7d8a099 100644 --- a/src/lib/spinel/spinel.c +++ b/src/lib/spinel/spinel.c @@ -2003,6 +2003,46 @@ const char *spinel_prop_key_to_cstr(spinel_prop_key_t prop_key) ret = "NEIGHBOR_TABLE_MULTI_RADIO_INFO"; break; + case SPINEL_PROP_SRP_CLIENT_START: + ret = "SRP_CLIENT_START"; + break; + + case SPINEL_PROP_SRP_CLIENT_LEASE_INTERVAL: + ret = "SRP_CLIENT_LEASE_INTERVAL"; + break; + + case SPINEL_PROP_SRP_CLIENT_KEY_LEASE_INTERVAL: + ret = "SRP_CLIENT_KEY_LEASE_INTERVAL"; + break; + + case SPINEL_PROP_SRP_CLIENT_HOST_INFO: + ret = "SRP_CLIENT_HOST_INFO"; + break; + + case SPINEL_PROP_SRP_CLIENT_HOST_NAME: + ret = "SRP_CLIENT_HOST_NAME"; + break; + + case SPINEL_PROP_SRP_CLIENT_HOST_ADDRESSES: + ret = "SRP_CLIENT_HOST_ADDRESSES"; + break; + + case SPINEL_PROP_SRP_CLIENT_SERVICES: + ret = "SRP_CLIENT_SERVICES"; + break; + + case SPINEL_PROP_SRP_CLIENT_HOST_SERVICES_REMOVE: + ret = "SRP_CLIENT_HOST_SERVICES_REMOVE"; + break; + + case SPINEL_PROP_SRP_CLIENT_HOST_SERVICES_CLEAR: + ret = "SRP_CLIENT_HOST_SERVICES_CLEAR"; + break; + + case SPINEL_PROP_SRP_CLIENT_EVENT: + ret = "SRP_CLIENT_EVENT"; + break; + case SPINEL_PROP_SERVER_ALLOW_LOCAL_DATA_CHANGE: ret = "SERVER_ALLOW_LOCAL_DATA_CHANGE"; break; @@ -2695,6 +2735,10 @@ const char *spinel_capability_to_cstr(spinel_capability_t capability) ret = "MULTI_RADIO"; break; + case SPINEL_CAP_SRP_CLIENT: + ret = "SRP_CLIENT"; + break; + case SPINEL_CAP_ERROR_RATE_TRACKING: ret = "ERROR_RATE_TRACKING"; break; diff --git a/src/lib/spinel/spinel.h b/src/lib/spinel/spinel.h index d7bf258bc..bf2a1f8bf 100644 --- a/src/lib/spinel/spinel.h +++ b/src/lib/spinel/spinel.h @@ -718,6 +718,32 @@ enum SPINEL_RADIO_LINK_TREL_UDP6 = 1, }; +typedef enum +{ + SPINEL_SRP_CLIENT_ITEM_STATE_TO_ADD = 0, // Item to be added/registered. + SPINEL_SRP_CLIENT_ITEM_STATE_ADDING = 1, // Item is being added/registered. + SPINEL_SRP_CLIENT_ITEM_STATE_TO_REFRESH = 2, // Item to be refreshed (re-register to renew lease). + SPINEL_SRP_CLIENT_ITEM_STATE_REFRESHING = 3, // Item is being refreshed. + SPINEL_SRP_CLIENT_ITEM_STATE_TO_REMOVE = 4, // Item to be removed. + SPINEL_SRP_CLIENT_ITEM_STATE_REMOVING = 5, // Item is being removed. + SPINEL_SRP_CLIENT_ITEM_STATE_REGISTERED = 6, // Item is registered with server. + SPINEL_SRP_CLIENT_ITEM_STATE_REMOVED = 7, // Item is removed. +} spinel_srp_client_item_state_t; + +typedef enum +{ + SPINEL_SRP_CLIENT_ERROR_NONE = 0, // No error. + SPINEL_SRP_CLIENT_ERROR_PARSE = 1, // Server unable to interpret due to format error. + SPINEL_SRP_CLIENT_ERROR_FAILED = 2, // Server encountered an internal failure. + SPINEL_SRP_CLIENT_ERROR_NOT_FOUND = 3, // Name that ought to exist, does not exists. + SPINEL_SRP_CLIENT_ERROR_NOT_IMPLEMENTED = 4, // Server does not support the query type. + SPINEL_SRP_CLIENT_ERROR_SECURITY = 5, // Service is not authoritative for zone. + SPINEL_SRP_CLIENT_ERROR_DUPLICATED = 6, // Some name that ought not to exist, does exist. + SPINEL_SRP_CLIENT_ERROR_RESPONSE_TIMEOUT = 7, // Timed out waiting for response from server (client would retry). + SPINEL_SRP_CLIENT_ERROR_INVALID_ARGS = 8, // Invalid args (e.g., bad service name or TXT-DATA). + SPINEL_SRP_CLIENT_ERROR_NO_BUFS = 9, // No buffer to send the SRP update message. +} spinel_srp_client_error_t; + typedef struct { uint8_t bytes[8]; @@ -1153,6 +1179,7 @@ enum SPINEL_CAP_RADIO_COEX = (SPINEL_CAP_OPENTHREAD__BEGIN + 11), SPINEL_CAP_MAC_RETRY_HISTOGRAM = (SPINEL_CAP_OPENTHREAD__BEGIN + 12), SPINEL_CAP_MULTI_RADIO = (SPINEL_CAP_OPENTHREAD__BEGIN + 13), + SPINEL_CAP_SRP_CLIENT = (SPINEL_CAP_OPENTHREAD__BEGIN + 14), SPINEL_CAP_OPENTHREAD__END = 640, SPINEL_CAP_THREAD__BEGIN = 1024, @@ -3637,6 +3664,143 @@ enum */ SPINEL_PROP_NEIGHBOR_TABLE_MULTI_RADIO_INFO = SPINEL_PROP_OPENTHREAD__BEGIN + 16, + /// SRP Client Start + /** Format: `b(6Sb)` - Write only + * Required capability: `SPINEL_CAP_SRP_CLIENT`. + * + * Writing to this property allows user to start or stop the SRP client operation with a given SRP server. + * + * Written value format is: + * + * `b` : TRUE to start the client, FALSE to stop the client. + * + * When used to start the SRP client, the following fields should also be included: + * + * `6` : SRP server IPv6 address. + * `U` : SRP server port number. + * `b` : Boolean to indicate whether or not to emit SRP client events (using `SPINEL_PROP_SRP_CLIENT_EVENT`). + * + */ + SPINEL_PROP_SRP_CLIENT_START = SPINEL_PROP_OPENTHREAD__BEGIN + 17, + + /// SRP Client Lease Interval + /** Format: `L` - Read/Write + * Required capability: `SPINEL_CAP_SRP_CLIENT`. + * + * The lease interval used in SRP update requests (in seconds). + * + */ + SPINEL_PROP_SRP_CLIENT_LEASE_INTERVAL = SPINEL_PROP_OPENTHREAD__BEGIN + 18, + + /// SRP Client Key Lease Interval + /** Format: `L` - Read/Write + * Required capability: `SPINEL_CAP_SRP_CLIENT`. + * + * The key lease interval used in SRP update requests (in seconds). + * + */ + SPINEL_PROP_SRP_CLIENT_KEY_LEASE_INTERVAL = SPINEL_PROP_OPENTHREAD__BEGIN + 19, + + /// SRP Client Host Info + /** Format: `UCt(A(6))` - Read only + * Required capability: `SPINEL_CAP_SRP_CLIENT`. + * + * Format is: + * + * `U` : The host name. + * `C` : The host state (values from `spinel_srp_client_item_state_t`). + * `t(A(6))` : Structure containing array of host IPv6 addresses. + * + */ + SPINEL_PROP_SRP_CLIENT_HOST_INFO = SPINEL_PROP_OPENTHREAD__BEGIN + 20, + + /// SRP Client Host Name (label). + /** Format: `U` - Read/Write + * Required capability: `SPINEL_CAP_SRP_CLIENT`. + * + */ + SPINEL_PROP_SRP_CLIENT_HOST_NAME = SPINEL_PROP_OPENTHREAD__BEGIN + 21, + + /// SRP Client Host Addresses + /** Format: `A(6)` - Read/Write + * Required capability: `SPINEL_CAP_SRP_CLIENT`. + * + */ + SPINEL_PROP_SRP_CLIENT_HOST_ADDRESSES = SPINEL_PROP_OPENTHREAD__BEGIN + 22, + + /// SRP Client Services + /** Format: `A(t(UUSSSd))` - Read/Insert/Remove + * Required capability: `SPINEL_CAP_SRP_CLIENT`. + * + * This property provide a list/array of services. Data per item is + * + * `U` : The service name labels (e.g., "_chip._udp", not the full domain name. + * `U` : The service instance name label (not the full name). + * `S` : The service port number. + * `S` : The service priority. + * `S` : The service weight. + * + * During remove operation, only service name and service instance name would be used. + * + */ + SPINEL_PROP_SRP_CLIENT_SERVICES = SPINEL_PROP_OPENTHREAD__BEGIN + 23, + + /// SRP Client Host And Services Remove + /** Format: `b` : Write only + * Required capability: `SPINEL_CAP_SRP_CLIENT`. + * + * Writing to this property starts the remove process of the host info and all services. + * Please see `otSrpClientRemoveHostAndServices()` for more details. + * + * Format is: + * + * `b` : A boolean indicating whether or not the host key lease should also be removed. + * + */ + SPINEL_PROP_SRP_CLIENT_HOST_SERVICES_REMOVE = SPINEL_PROP_OPENTHREAD__BEGIN + 24, + + /// SRP Client Host And Services Clear + /** Format: Empty : Write only + * Required capability: `SPINEL_CAP_SRP_CLIENT`. + * + * Writing to this property clears all host info and all the services. + * Please see `otSrpClientClearHostAndServices()` for more details. + * + */ + SPINEL_PROP_SRP_CLIENT_HOST_SERVICES_CLEAR = SPINEL_PROP_OPENTHREAD__BEGIN + 25, + + /// SRP Client Event + /** Format: t() : Asynchronous event only + * Required capability: `SPINEL_CAP_SRP_CLIENT`. + * + * This property is asynchronously emitted when there is an event from SRP client notifying some state changes or + * errors. + * + * The general format of this property is as follows: + * + * `S` : Error code (see `spinel_srp_client_error_t` enumeration). + * `d` : Host info data. + * `d` : Active services. + * `d` : Removed services. + * + * The host info data contains: + * + * `U` : The host name. + * `C` : The host state (values from `spinel_srp_client_item_state_t`). + * `t(A(6))` : Structure containing array of host IPv6 addresses. + * + * The active or removed services data is an array of services `A(t(UUSSSd))` with each service format: + * + * `U` : The service name labels (e.g., "_chip._udp", not the full domain name. + * `U` : The service instance name label (not the full name). + * `S` : The service port number. + * `S` : The service priority. + * `S` : The service weight. + * `d` : The encoded TXT-DATA. + * + */ + SPINEL_PROP_SRP_CLIENT_EVENT = SPINEL_PROP_OPENTHREAD__BEGIN + 26, + SPINEL_PROP_OPENTHREAD__END = 0x2000, SPINEL_PROP_SERVER__BEGIN = 0xA0, diff --git a/src/ncp/ncp_base.cpp b/src/ncp/ncp_base.cpp index 5908c6663..a55c5680d 100644 --- a/src/ncp/ncp_base.cpp +++ b/src/ncp/ncp_base.cpp @@ -243,6 +243,10 @@ NcpBase::NcpBase(Instance *aInstance) , mOutboundInsecureIpFrameCounter(0) , mDroppedOutboundIpFrameCounter(0) , mDroppedInboundIpFrameCounter(0) +#if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE + , mSrpClientNumHostAddresses(0) + , mSrpClientCallbackEnabled(false) +#endif #endif // OPENTHREAD_MTD || OPENTHREAD_FTD , mFramingErrorCounter(0) , mRxSpinelFrameCounter(0) @@ -281,6 +285,7 @@ NcpBase::NcpBase(Instance *aInstance) #endif otThreadRegisterParentResponseCallback(mInstance, &NcpBase::HandleParentResponseInfo, static_cast(this)); #endif // OPENTHREAD_FTD + #if OPENTHREAD_CONFIG_LEGACY_ENABLE mLegacyNodeDidJoin = false; mLegacyHandlers = nullptr; @@ -1911,6 +1916,10 @@ template <> otError NcpBase::HandlePropertyGet(void) SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_MULTI_RADIO)); #endif +#if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE + SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_SRP_CLIENT)); +#endif + #endif // OPENTHREAD_MTD || OPENTHREAD_FTD exit: diff --git a/src/ncp/ncp_base.hpp b/src/ncp/ncp_base.hpp index acb2ca49a..6779dcbee 100644 --- a/src/ncp/ncp_base.hpp +++ b/src/ncp/ncp_base.hpp @@ -50,6 +50,9 @@ #if OPENTHREAD_CONFIG_MULTI_RADIO #include #endif +#if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE +#include +#endif #include "changed_props_set.hpp" #include "common/instance.hpp" @@ -594,6 +597,45 @@ protected: uint32_t mOutboundInsecureIpFrameCounter; // Number of insecure outbound data/IP frames. uint32_t mDroppedOutboundIpFrameCounter; // Number of dropped outbound data/IP frames. uint32_t mDroppedInboundIpFrameCounter; // Number of dropped inbound data/IP frames. + +#if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE + enum : uint8_t + { + kSrpClientMaxServices = OPENTHREAD_CONFIG_NCP_SRP_CLIENT_MAX_SERVICES, + kSrpClientMaxHostAddresses = OPENTHREAD_CONFIG_NCP_SRP_CLIENT_MAX_HOST_ADDRESSES, + kSrpClientNameSize = 64, + }; + + struct SrpClientService + { + void MarkAsNotInUse(void) { mService.mNext = &mService; } + bool IsInUse(void) const { return (mService.mNext != &mService); } + + otSrpClientService mService; + char mInstanceName[kSrpClientNameSize]; + char mServiceName[kSrpClientNameSize]; + }; + + otError EncodeSrpClientHostInfo(const otSrpClientHostInfo &aHostInfo); + otError EncodeSrpClientServices(const otSrpClientService *aServices); + + static void HandleSrpClientCallback(otError aError, + const otSrpClientHostInfo *aHostInfo, + const otSrpClientService * aServices, + const otSrpClientService * aRemovedServices, + void * aContext); + void HandleSrpClientCallback(otError aError, + const otSrpClientHostInfo *aHostInfo, + const otSrpClientService * aServices, + const otSrpClientService * aRemovedServices); + + char mSrpClientHostName[kSrpClientNameSize]; + SrpClientService mSrpClientServicePool[kSrpClientMaxServices]; + otIp6Address mSrpClientHostAddresses[kSrpClientMaxHostAddresses]; + uint8_t mSrpClientNumHostAddresses; + bool mSrpClientCallbackEnabled; +#endif // OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE + #if OPENTHREAD_CONFIG_LEGACY_ENABLE const otNcpLegacyHandlers *mLegacyHandlers; uint8_t mLegacyUlaPrefix[OT_NCP_LEGACY_ULA_PREFIX_LENGTH]; diff --git a/src/ncp/ncp_base_dispatcher.cpp b/src/ncp/ncp_base_dispatcher.cpp index 273c0c9f8..7c11ec749 100644 --- a/src/ncp/ncp_base_dispatcher.cpp +++ b/src/ncp/ncp_base_dispatcher.cpp @@ -329,6 +329,15 @@ NcpBase::PropertyHandler NcpBase::FindGetPropertyHandler(spinel_prop_key_t aKey) #if OPENTHREAD_CONFIG_MULTI_RADIO OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_NEIGHBOR_TABLE_MULTI_RADIO_INFO), #endif +#if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE + OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_SRP_CLIENT_LEASE_INTERVAL), + OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_SRP_CLIENT_KEY_LEASE_INTERVAL), + OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_SRP_CLIENT_HOST_INFO), + OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_SRP_CLIENT_HOST_NAME), + OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_SRP_CLIENT_HOST_ADDRESSES), + OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_SRP_CLIENT_SERVICES), +#endif + #if OPENTHREAD_CONFIG_LEGACY_ENABLE OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_NEST_LEGACY_ULA_PREFIX), OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_NEST_LEGACY_LAST_NODE_JOINED), @@ -544,6 +553,15 @@ NcpBase::PropertyHandler NcpBase::FindSetPropertyHandler(spinel_prop_key_t aKey) #if OPENTHREAD_CONFIG_IP6_SLAAC_ENABLE OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_SLAAC_ENABLED), #endif +#if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE + OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_SRP_CLIENT_START), + OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_SRP_CLIENT_LEASE_INTERVAL), + OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_SRP_CLIENT_KEY_LEASE_INTERVAL), + OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_SRP_CLIENT_HOST_NAME), + OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_SRP_CLIENT_HOST_ADDRESSES), + OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_SRP_CLIENT_HOST_SERVICES_REMOVE), + OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_SRP_CLIENT_HOST_SERVICES_CLEAR), +#endif #if OPENTHREAD_CONFIG_LEGACY_ENABLE OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_NEST_LEGACY_ULA_PREFIX), #endif @@ -602,6 +620,11 @@ NcpBase::PropertyHandler NcpBase::FindInsertPropertyHandler(spinel_prop_key_t aK #endif #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE OT_NCP_INSERT_HANDLER_ENTRY(SPINEL_PROP_THREAD_JOINERS), +#endif +#if OPENTHREAD_FTD || OPENTHREAD_MTD +#if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE + OT_NCP_INSERT_HANDLER_ENTRY(SPINEL_PROP_SRP_CLIENT_SERVICES), +#endif #endif }; @@ -650,6 +673,11 @@ NcpBase::PropertyHandler NcpBase::FindRemovePropertyHandler(spinel_prop_key_t aK #endif #if OPENTHREAD_FTD OT_NCP_REMOVE_HANDLER_ENTRY(SPINEL_PROP_THREAD_ACTIVE_ROUTER_IDS), +#endif +#if OPENTHREAD_FTD || OPENTHREAD_MTD +#if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE + OT_NCP_REMOVE_HANDLER_ENTRY(SPINEL_PROP_SRP_CLIENT_SERVICES), +#endif #endif }; diff --git a/src/ncp/ncp_base_mtd.cpp b/src/ncp/ncp_base_mtd.cpp index 37af9747e..45829ec44 100644 --- a/src/ncp/ncp_base_mtd.cpp +++ b/src/ncp/ncp_base_mtd.cpp @@ -64,6 +64,7 @@ #include "common/code_utils.hpp" #include "common/debug.hpp" #include "common/instance.hpp" +#include "common/string.hpp" #include "net/ip6.hpp" #if OPENTHREAD_MTD || OPENTHREAD_FTD @@ -3213,6 +3214,432 @@ exit: } #endif // OPENTHREAD_CONFIG_MULTI_RADIO +// ---------------------------------------------------------------------------- +// SRP Client +// ---------------------------------------------------------------------------- + +#if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE + +template <> otError NcpBase::HandlePropertySet(void) +{ + otError error = OT_ERROR_NONE; + bool start; + bool callbackEnabled; + otSockAddr serverAddr; + + SuccessOrExit(error = mDecoder.ReadBool(start)); + + if (!start) + { + otSrpClientStop(mInstance); + ExitNow(); + } + + SuccessOrExit(error = mDecoder.ReadIp6Address(serverAddr.mAddress)); + SuccessOrExit(error = mDecoder.ReadUint16(serverAddr.mPort)); + SuccessOrExit(error = mDecoder.ReadBool(callbackEnabled)); + + SuccessOrExit(error = otSrpClientStart(mInstance, &serverAddr, HandleSrpClientCallback, this)); + mSrpClientCallbackEnabled = callbackEnabled; + +exit: + return error; +} + +template <> otError NcpBase::HandlePropertyGet(void) +{ + return mEncoder.WriteUint32(otSrpClientGetLeaseInterval(mInstance)); +} + +template <> otError NcpBase::HandlePropertySet(void) +{ + otError error; + uint32_t interval; + + SuccessOrExit(error = mDecoder.ReadUint32(interval)); + otSrpClientSetLeaseInterval(mInstance, interval); + +exit: + return error; +} + +template <> otError NcpBase::HandlePropertyGet(void) +{ + return mEncoder.WriteUint32(otSrpClientGetKeyLeaseInterval(mInstance)); +} + +template <> otError NcpBase::HandlePropertySet(void) +{ + otError error; + uint32_t interval; + + SuccessOrExit(error = mDecoder.ReadUint32(interval)); + otSrpClientSetKeyLeaseInterval(mInstance, interval); + +exit: + return error; +} + +static spinel_srp_client_item_state_t SrpClientItemStatetoSpinel(otSrpClientItemState aItemState) +{ + spinel_srp_client_item_state_t state = SPINEL_SRP_CLIENT_ITEM_STATE_REMOVED; + + switch (aItemState) + { + case OT_SRP_CLIENT_ITEM_STATE_TO_ADD: + state = SPINEL_SRP_CLIENT_ITEM_STATE_TO_ADD; + break; + case OT_SRP_CLIENT_ITEM_STATE_ADDING: + state = SPINEL_SRP_CLIENT_ITEM_STATE_ADDING; + break; + case OT_SRP_CLIENT_ITEM_STATE_TO_REFRESH: + state = SPINEL_SRP_CLIENT_ITEM_STATE_TO_REFRESH; + break; + case OT_SRP_CLIENT_ITEM_STATE_REFRESHING: + state = SPINEL_SRP_CLIENT_ITEM_STATE_REFRESHING; + break; + case OT_SRP_CLIENT_ITEM_STATE_TO_REMOVE: + state = SPINEL_SRP_CLIENT_ITEM_STATE_TO_REMOVE; + break; + case OT_SRP_CLIENT_ITEM_STATE_REMOVING: + state = SPINEL_SRP_CLIENT_ITEM_STATE_REMOVING; + break; + case OT_SRP_CLIENT_ITEM_STATE_REGISTERED: + state = SPINEL_SRP_CLIENT_ITEM_STATE_REGISTERED; + break; + case OT_SRP_CLIENT_ITEM_STATE_REMOVED: + state = SPINEL_SRP_CLIENT_ITEM_STATE_REMOVED; + break; + } + + return state; +} + +otError NcpBase::EncodeSrpClientHostInfo(const otSrpClientHostInfo &aHostInfo) +{ + otError error; + + SuccessOrExit(error = mEncoder.WriteUtf8(aHostInfo.mName != nullptr ? aHostInfo.mName : "")); + SuccessOrExit(error = mEncoder.WriteUint8(SrpClientItemStatetoSpinel(aHostInfo.mState))); + + SuccessOrExit(error = mEncoder.OpenStruct()); + + for (uint8_t index = 0; index < aHostInfo.mNumAddresses; index++) + { + SuccessOrExit(error = mEncoder.WriteIp6Address(aHostInfo.mAddresses[index])); + } + + SuccessOrExit(error = mEncoder.CloseStruct()); + +exit: + return error; +} + +template <> otError NcpBase::HandlePropertyGet(void) +{ + return EncodeSrpClientHostInfo(*otSrpClientGetHostInfo(mInstance)); +} + +template <> otError NcpBase::HandlePropertyGet(void) +{ + const char *name = otSrpClientGetHostInfo(mInstance)->mName; + + return mEncoder.WriteUtf8(name != nullptr ? name : ""); +} + +template <> otError NcpBase::HandlePropertySet(void) +{ + otError error; + const char *name; + + SuccessOrExit(error = mDecoder.ReadUtf8(name)); + + VerifyOrExit(StringLength(name, kSrpClientNameSize) < kSrpClientNameSize, error = OT_ERROR_INVALID_ARGS); + + // We first make sure we can set the name, and if so + // we copy it to the `mSrpClientHostName` buffer and set + // the host name again now with the persisted buffer + // This ensures that we do not overwrite a previous + // `mSrpClientHostName` when host name cannot be set. + + SuccessOrExit(error = otSrpClientSetHostName(mInstance, name)); + + strcpy(mSrpClientHostName, name); + error = otSrpClientSetHostName(mInstance, mSrpClientHostName); + OT_ASSERT(error == OT_ERROR_NONE); + +exit: + return error; +} + +template <> otError NcpBase::HandlePropertyGet(void) +{ + otError error = OT_ERROR_NONE; + const otSrpClientHostInfo *hostInfo = otSrpClientGetHostInfo(mInstance); + + for (uint8_t index = 0; index < hostInfo->mNumAddresses; index++) + { + SuccessOrExit(error = mEncoder.WriteIp6Address(hostInfo->mAddresses[index])); + } + +exit: + return error; +} + +template <> otError NcpBase::HandlePropertySet(void) +{ + otError error; + otIp6Address addresses[kSrpClientMaxHostAddresses]; + uint8_t numAddresses = 0; + + while (!mDecoder.IsAllReadInStruct()) + { + VerifyOrExit(numAddresses < kSrpClientMaxHostAddresses, error = OT_ERROR_NO_BUFS); + + SuccessOrExit(error = mDecoder.ReadIp6Address(addresses[numAddresses])); + numAddresses++; + } + + // We first make sure we can set the addresses, and if so + // we copy the address list into `mSrpClientHostAddresses` + // 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. + + SuccessOrExit(error = otSrpClientSetHostAddresses(mInstance, addresses, numAddresses)); + + memcpy(mSrpClientHostAddresses, addresses, sizeof(addresses)); + mSrpClientNumHostAddresses = numAddresses; + + error = otSrpClientSetHostAddresses(mInstance, mSrpClientHostAddresses, numAddresses); + OT_ASSERT(error == OT_ERROR_NONE); + +exit: + return error; +} + +otError NcpBase::EncodeSrpClientServices(const otSrpClientService *aServices) +{ + otError error = OT_ERROR_NONE; + + for (; aServices != nullptr; aServices = aServices->mNext) + { + SuccessOrExit(error = mEncoder.OpenStruct()); + + SuccessOrExit(error = mEncoder.WriteUtf8(aServices->mName)); + SuccessOrExit(error = mEncoder.WriteUtf8(aServices->mInstanceName)); + SuccessOrExit(error = mEncoder.WriteUint16(aServices->mPort)); + SuccessOrExit(error = mEncoder.WriteUint16(aServices->mPriority)); + SuccessOrExit(error = mEncoder.WriteUint16(aServices->mWeight)); + + SuccessOrExit(error = mEncoder.CloseStruct()); + } + +exit: + return error; +} + +template <> otError NcpBase::HandlePropertyGet(void) +{ + return EncodeSrpClientServices(otSrpClientGetServices(mInstance)); +} + +template <> otError NcpBase::HandlePropertyInsert(void) +{ + otError error = OT_ERROR_NONE; + SrpClientService *entry = nullptr; + const char * serviceName; + const char * instanceName; + + for (SrpClientService &poolEntry : mSrpClientServicePool) + { + if (!poolEntry.IsInUse()) + { + entry = &poolEntry; + break; + } + } + + VerifyOrExit(entry != nullptr, error = OT_ERROR_NO_BUFS); + + SuccessOrExit(error = mDecoder.ReadUtf8(serviceName)); + VerifyOrExit(StringLength(serviceName, kSrpClientNameSize) < kSrpClientNameSize, error = OT_ERROR_INVALID_ARGS); + strcpy(entry->mServiceName, serviceName); + entry->mService.mName = entry->mServiceName; + + SuccessOrExit(error = mDecoder.ReadUtf8(instanceName)); + VerifyOrExit(StringLength(instanceName, kSrpClientNameSize) < kSrpClientNameSize, error = OT_ERROR_INVALID_ARGS); + strcpy(entry->mInstanceName, instanceName); + entry->mService.mInstanceName = entry->mInstanceName; + + SuccessOrExit(error = mDecoder.ReadUint16(entry->mService.mPort)); + SuccessOrExit(error = mDecoder.ReadUint16(entry->mService.mPriority)); + SuccessOrExit(error = mDecoder.ReadUint16(entry->mService.mWeight)); + + error = otSrpClientAddService(mInstance, &entry->mService); + + if (error != OT_ERROR_NONE) + { + entry->MarkAsNotInUse(); + } + +exit: + return error; +} + +template <> otError NcpBase::HandlePropertyRemove(void) +{ + otError error = OT_ERROR_NONE; + const char *serviceName; + const char *instanceName; + + SuccessOrExit(error = mDecoder.ReadUtf8(serviceName)); + SuccessOrExit(error = mDecoder.ReadUtf8(instanceName)); + + for (SrpClientService &poolEntry : mSrpClientServicePool) + { + if (!poolEntry.IsInUse() || (strcmp(serviceName, poolEntry.mServiceName) != 0) || + (strcmp(instanceName, poolEntry.mInstanceName) != 0)) + { + continue; + } + + error = otSrpClientRemoveService(mInstance, &poolEntry.mService); + ExitNow(); + } + + error = OT_ERROR_NOT_FOUND; + +exit: + return error; +} + +template <> otError NcpBase::HandlePropertySet(void) +{ + otError error = OT_ERROR_NONE; + bool removeKeyLease; + + SuccessOrExit(error = mDecoder.ReadBool(removeKeyLease)); + + error = otSrpClientRemoveHostAndServices(mInstance, removeKeyLease); + +exit: + return error; +} + +template <> otError NcpBase::HandlePropertySet(void) +{ + otSrpClientClearHostAndServices(mInstance); + + return OT_ERROR_NONE; +} + +static spinel_srp_client_error_t SrpClientErrorToSpinelError(otError aError) +{ + spinel_srp_client_error_t error = SPINEL_SRP_CLIENT_ERROR_FAILED; + + switch (aError) + { + case OT_ERROR_NONE: + error = SPINEL_SRP_CLIENT_ERROR_NONE; + break; + case OT_ERROR_PARSE: + error = SPINEL_SRP_CLIENT_ERROR_PARSE; + break; + case OT_ERROR_NOT_FOUND: + error = SPINEL_SRP_CLIENT_ERROR_NOT_FOUND; + break; + case OT_ERROR_NOT_IMPLEMENTED: + error = SPINEL_SRP_CLIENT_ERROR_NOT_IMPLEMENTED; + break; + case OT_ERROR_SECURITY: + error = SPINEL_SRP_CLIENT_ERROR_SECURITY; + break; + case OT_ERROR_DUPLICATED: + error = SPINEL_SRP_CLIENT_ERROR_DUPLICATED; + break; + case OT_ERROR_RESPONSE_TIMEOUT: + error = SPINEL_SRP_CLIENT_ERROR_RESPONSE_TIMEOUT; + break; + case OT_ERROR_INVALID_ARGS: + error = SPINEL_SRP_CLIENT_ERROR_INVALID_ARGS; + break; + case OT_ERROR_NO_BUFS: + error = SPINEL_SRP_CLIENT_ERROR_NO_BUFS; + break; + case OT_ERROR_FAILED: + default: + error = SPINEL_SRP_CLIENT_ERROR_FAILED; + break; + } + + return error; +} + +void NcpBase::HandleSrpClientCallback(otError aError, + const otSrpClientHostInfo *aHostInfo, + const otSrpClientService * aServices, + const otSrpClientService * aRemovedServices, + void * aContext) +{ + static_cast(aContext)->HandleSrpClientCallback(aError, aHostInfo, aServices, aRemovedServices); +} + +void NcpBase::HandleSrpClientCallback(otError aError, + const otSrpClientHostInfo *aHostInfo, + const otSrpClientService * aServices, + const otSrpClientService * aRemovedServices) +{ + otError error = OT_ERROR_NONE; + const otSrpClientService *service; + const otSrpClientService *next; + + VerifyOrExit(mSrpClientCallbackEnabled); + + SuccessOrExit(error = mEncoder.BeginFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_CMD_PROP_VALUE_IS, + SPINEL_PROP_SRP_CLIENT_EVENT)); + + SuccessOrExit(error = mEncoder.WriteUint16(SrpClientErrorToSpinelError(aError))); + + SuccessOrExit(error = mEncoder.OpenStruct()); + SuccessOrExit(error = EncodeSrpClientHostInfo(*aHostInfo)); + SuccessOrExit(error = mEncoder.CloseStruct()); + + SuccessOrExit(error = mEncoder.OpenStruct()); + SuccessOrExit(error = EncodeSrpClientServices(aServices)); + SuccessOrExit(error = mEncoder.CloseStruct()); + + SuccessOrExit(error = mEncoder.OpenStruct()); + SuccessOrExit(error = EncodeSrpClientServices(aRemovedServices)); + SuccessOrExit(error = mEncoder.CloseStruct()); + + SuccessOrExit(error = mEncoder.EndFrame()); + +exit: + + if (error != OT_ERROR_NONE) + { + // Emit a NONMEM status if we fail to send the event. + mChangedPropsSet.AddLastStatus(SPINEL_STATUS_NOMEM); + mUpdateChangedPropsTask.Post(); + } + + for (service = aRemovedServices; service != nullptr; service = next) + { + next = service->mNext; + + for (SrpClientService &poolEntry : mSrpClientServicePool) + { + if (&poolEntry.mService == service) + { + poolEntry.MarkAsNotInUse(); + } + } + } +} + +#endif // OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE + #if OPENTHREAD_CONFIG_LEGACY_ENABLE void NcpBase::RegisterLegacyHandlers(const otNcpLegacyHandlers *aHandlers) diff --git a/src/ncp/ncp_config.h b/src/ncp/ncp_config.h index b93cb6f5c..2d1ac2636 100644 --- a/src/ncp/ncp_config.h +++ b/src/ncp/ncp_config.h @@ -189,4 +189,28 @@ #define OPENTHREAD_ENABLE_NCP_VENDOR_HOOK 0 #endif +/** + * @def OPENTHREAD_CONFIG_NCP_SRP_CLIENT_MAX_SERVICES + * + * The maximum number of service entries supported by SRP client. + * + * This is only applicable when SRP client is enabled, i.e., OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE is set. + * + */ +#ifndef OPENTHREAD_CONFIG_NCP_SRP_CLIENT_MAX_SERVICES +#define OPENTHREAD_CONFIG_NCP_SRP_CLIENT_MAX_SERVICES 2 +#endif + +/** + * @def OPENTHREAD_CONFIG_NCP_SRP_CLIENT_MAX_HOST_ADDRESSES + * + * The maximum number of host IPv6 address entries supported by SRP client. + * + * This is only applicable when SRP client is enabled, i.e., OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE is set. + * + */ +#ifndef OPENTHREAD_CONFIG_NCP_SRP_CLIENT_MAX_HOST_ADDRESSES +#define OPENTHREAD_CONFIG_NCP_SRP_CLIENT_MAX_HOST_ADDRESSES 2 +#endif + #endif // CONFIG_NCP_H_