/* * 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. */ #ifndef SRP_CLIENT_HPP_ #define SRP_CLIENT_HPP_ #include "openthread-core-config.h" #if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE #include #include "common/as_core_type.hpp" #include "common/callback.hpp" #include "common/clearable.hpp" #include "common/linked_list.hpp" #include "common/locator.hpp" #include "common/log.hpp" #include "common/message.hpp" #include "common/non_copyable.hpp" #include "common/notifier.hpp" #include "common/numeric_limits.hpp" #include "common/timer.hpp" #include "crypto/ecdsa.hpp" #include "net/dns_types.hpp" #include "net/ip6.hpp" #include "net/udp6.hpp" #include "thread/network_data_service.hpp" /** * @file * This file includes definitions for the SRP (Service Registration Protocol) client. */ namespace ot { namespace Srp { #if !OPENTHREAD_CONFIG_ECDSA_ENABLE #error "SRP Client feature requires ECDSA support (OPENTHREAD_CONFIG_ECDSA_ENABLE)." #endif /** * Implements SRP client. * */ class Client : public InstanceLocator, private NonCopyable { friend class ot::Notifier; using DnsSrpUnicast = NetworkData::Service::DnsSrpUnicast; using DnsSrpAnycast = NetworkData::Service::DnsSrpAnycast; public: /** * Types represents an SRP client item (service or host info) state. * */ enum ItemState : uint8_t { kToAdd = OT_SRP_CLIENT_ITEM_STATE_TO_ADD, ///< Item to be added/registered. kAdding = OT_SRP_CLIENT_ITEM_STATE_ADDING, ///< Item is being added/registered. kToRefresh = OT_SRP_CLIENT_ITEM_STATE_TO_REFRESH, ///< Item to be refreshed (renew lease). kRefreshing = OT_SRP_CLIENT_ITEM_STATE_REFRESHING, ///< Item is being refreshed. kToRemove = OT_SRP_CLIENT_ITEM_STATE_TO_REMOVE, ///< Item to be removed. kRemoving = OT_SRP_CLIENT_ITEM_STATE_REMOVING, ///< Item is being removed. kRegistered = OT_SRP_CLIENT_ITEM_STATE_REGISTERED, ///< Item is registered with server. kRemoved = OT_SRP_CLIENT_ITEM_STATE_REMOVED, ///< Item is removed. }; /** * Pointer type defines the callback used by SRP client to notify user of a changes/events/errors. * * Please see `otSrpClientCallback` for more details. * */ typedef otSrpClientCallback ClientCallback; /** * Represents an SRP client host info. * */ class HostInfo : public otSrpClientHostInfo, private Clearable { friend class Client; friend class Clearable; public: /** * Initializes the `HostInfo` object. * */ void Init(void); /** * Clears the `HostInfo` object. * */ void Clear(void); /** * Gets the host name (label) string. * * @returns The host name (label) string, or `nullptr` if not yet set. * */ const char *GetName(void) const { return mName; } /** * Indicates whether or not the host auto address mode is enabled. * * @retval TRUE If the auto address mode is enabled. * @retval FALSE If the auto address mode is disabled. * */ bool IsAutoAddressEnabled(void) const { return mAutoAddress; } /** * Gets the number of host IPv6 addresses. * * @returns The number of host IPv6 addresses. * */ uint8_t GetNumAddresses(void) const { return mNumAddresses; } /** * Gets the host IPv6 address at a given index. * * @param[in] aIndex The index to get (MUST be smaller than `GetNumAddresses()`). * * @returns The host IPv6 address at index @p aIndex. * */ const Ip6::Address &GetAddress(uint8_t aIndex) const { return AsCoreType(&mAddresses[aIndex]); } /** * Gets the state of `HostInfo`. * * @returns The `HostInfo` state. * */ ItemState GetState(void) const { return static_cast(mState); } private: void SetName(const char *aName) { mName = aName; } void SetState(ItemState aState); void SetAddresses(const Ip6::Address *aAddresses, uint8_t aNumAddresses); void EnableAutoAddress(void); }; /** * Represents an SRP client service. * */ class Service : public otSrpClientService, public LinkedListEntry { friend class Client; friend class LinkedList; public: /** * Initializes and validates the `Service` object and its fields. * * @retval kErrorNone Successfully initialized and validated the `Service` object. * @retval kErrorInvalidArgs The info in `Service` object is not valid (e.g. null name or bad `TxtEntry`). * */ Error Init(void); /** * Gets the service name labels string. * * @returns The service name label string (e.g., "_chip._udp", not the full domain name). * */ const char *GetName(void) const { return mName; } /** * Gets the service instance name label (not the full name). * * @returns The service instance name label string. * */ const char *GetInstanceName(void) const { return mInstanceName; } /** * Indicates whether or not the service has any subtypes. * * @retval TRUE The service has at least one subtype. * @retval FALSE The service does not have any subtype. * */ bool HasSubType(void) const { return (mSubTypeLabels != nullptr); } /** * Gets the subtype label at a given index. * * MUST be used only after `HasSubType()` indicates that service has a subtype. * * @param[in] aIndex The index into list of subtype labels. * * @returns A pointer to subtype label at @p aIndex, or `nullptr` if there is no label (@p aIndex is after the * end of the subtype list). * */ const char *GetSubTypeLabelAt(uint16_t aIndex) const { return mSubTypeLabels[aIndex]; } /** * Gets the service port number. * * @returns The service port number. * */ uint16_t GetPort(void) const { return mPort; } /** * Gets the service priority. * * @returns The service priority. * */ uint16_t GetPriority(void) const { return mPriority; } /** * Gets the service weight. * * @returns The service weight. * */ uint16_t GetWeight(void) const { return mWeight; } /** * Gets the array of service TXT entries. * * @returns A pointer to an array of service TXT entries. * */ const Dns::TxtEntry *GetTxtEntries(void) const { return AsCoreTypePtr(mTxtEntries); } /** * Gets the number of entries in the service TXT entry array. * * @returns The number of entries in the service TXT entry array. * */ uint8_t GetNumTxtEntries(void) const { return mNumTxtEntries; } /** * Gets the state of service. * * @returns The service state. * */ ItemState GetState(void) const { return static_cast(mState); } /** * Gets the desired lease interval to request when registering this service. * * @returns The desired lease interval in sec. Zero indicates to use default. * */ uint32_t GetLease(void) const { return (mLease & kLeaseMask); } /** * Gets the desired key lease interval to request when registering this service. * * @returns The desired lease interval in sec. Zero indicates to use default. * */ uint32_t GetKeyLease(void) const { return mKeyLease; } private: // We use the high (MSB) bit of `mLease` as flag to indicate // whether or not the service is appended in the message. // This is then used when updating the service state. Note that // we guarantee that `mLease` is not greater than `kMaxLease` // which ensures that the last bit is unused. static constexpr uint32_t kAppendedInMsgFlag = (1U << 31); static constexpr uint32_t kLeaseMask = ~kAppendedInMsgFlag; void SetState(ItemState aState); TimeMilli GetLeaseRenewTime(void) const { return TimeMilli(mData); } void SetLeaseRenewTime(TimeMilli aTime) { mData = aTime.GetValue(); } bool IsAppendedInMessage(void) const { return mLease & kAppendedInMsgFlag; } void MarkAsAppendedInMessage(void) { mLease |= kAppendedInMsgFlag; } void ClearAppendedInMessageFlag(void) { mLease &= ~kAppendedInMsgFlag; } bool Matches(const Service &aOther) const; bool Matches(ItemState aState) const { return GetState() == aState; } }; /** * Initializes the SRP `Client` object. * * @param[in] aInstance A reference to the OpenThread instance. * */ explicit Client(Instance &aInstance); /** * Starts the SRP client operation. * * SRP client will prepare and send "SRP Update" message to the SRP server once all the following conditions are * met: * * - The SRP client is started - `Start()` is called * - Host name is set - `SetHostName()` is called. * - At least one host IPv6 address is set - `SetHostAddresses()` is called. * - At least one service is added - `AddService()` is called. * * It does not matter in which order these methods are called. When all conditions are met, the SRP client will * wait for a short delay before preparing an "SRP Update" message and sending it to server. This delay allows for * user to add multiple services and/or IPv6 addresses before the first SRP Update message is sent (ensuring a * single SRP Update is sent containing all the info). * * @param[in] aServerSockAddr The socket address (IPv6 address and port number) of the SRP server. * * @retval kErrorNone SRP client operation started successfully or it is already running with same server * socket address and callback. * @retval kErrorBusy SRP client is busy running with a different socket address. * @retval kErrorFailed Failed to open/connect the client's UDP socket. * */ Error Start(const Ip6::SockAddr &aServerSockAddr) { return Start(aServerSockAddr, kRequesterUser); } /** * Stops the SRP client operation. * * Stops any further interactions with the SRP server. Note that it does not remove or clear host info * and/or list of services. It marks all services to be added/removed again once the client is started again. * * If `OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE` (auto-start feature) is enabled, a call to this method * also disables the auto-start mode. * */ void Stop(void) { Stop(kRequesterUser, kResetRetryInterval); } #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE /** * Pointer type defines the callback used by SRP client to notify user when it is auto-started or * stopped. * */ typedef otSrpClientAutoStartCallback AutoStartCallback; /** * Enables the auto-start mode. * * Config option `OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_DEFAULT_MODE` specifies the default auto-start mode * (whether it is enabled or disabled at the start of OT stack). * * When auto-start is enabled, the SRP client will monitor the Thread Network Data to discover SRP servers and * select the preferred server and automatically start and stop the client when an SRP server is detected. * * There are three categories of Network Data entries indicating presence of SRP sever. They are preferred in the * following order: * * 1) Preferred unicast entries where server address is included in the service data. If there are multiple * options, the one with numerically lowest IPv6 address is preferred. * * 2) Anycast entries each having a seq number. A larger sequence number in the sense specified by Serial Number * Arithmetic logic in RFC-1982 is considered more recent and therefore preferred. The largest seq number * using serial number arithmetic is preferred if it is well-defined (i.e., the seq number is larger than all * other seq numbers). If it is not well-defined, then the numerically largest seq number is preferred. * * 3) Unicast entries where the server address info is included in server data. If there are multiple options, * the one with numerically lowest IPv6 address is preferred. * * When there is a change in the Network Data entries, client will check that the currently selected server is * still present in the Network Data and is still the preferred one. Otherwise the client will switch to the new * preferred server or stop if there is none. * * When the SRP client is explicitly started through a successful call to `Start()`, the given SRP server address * in `Start()` will continue to be used regardless of the state of auto-start mode and whether the same SRP * server address is discovered or not in the Thread Network Data. In this case, only an explicit `Stop()` call * will stop the client. * * @param[in] aCallback A callback to notify when client is auto-started/stopped. Can be `nullptr` if not needed. * @param[in] aContext A context to be passed when invoking @p aCallback. * */ void EnableAutoStartMode(AutoStartCallback aCallback, void *aContext); /** * Disables the auto-start mode. * * Disabling the auto-start mode will not stop the client if it is already running but the client stops monitoring * the Thread Network Data to verify that the selected SRP server is still present in it. * * Note that a call to `Stop()` will also disable the auto-start mode. * */ void DisableAutoStartMode(void) { mAutoStart.SetState(AutoStart::kDisabled); } /** * Indicates the current state of auto-start mode (enabled or disabled). * * @returns TRUE if the auto-start mode is enabled, FALSE otherwise. * */ bool IsAutoStartModeEnabled(void) const { return mAutoStart.GetState() != AutoStart::kDisabled; } /** * Indicates whether or not the current SRP server's address is selected by auto-start. * * @returns TRUE if the SRP server's address is selected by auto-start, FALSE otherwise. * */ bool IsServerSelectedByAutoStart(void) const { return mAutoStart.HasSelectedServer(); } #endif // OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE /** * Indicates whether the SRP client is running or not. * * @returns TRUE if the SRP client is running, FALSE otherwise. * */ bool IsRunning(void) const { return (mState != kStateStopped); } /** * Gets the socket address (IPv6 address and port number) of the SRP server which is being used by SRP * client. * * If the client is not running, the address is unspecified (all zero) with zero port number. * * @returns The SRP server's socket address. * */ const Ip6::SockAddr &GetServerAddress(void) const { return mSocket.GetPeerName(); } /** * Sets the callback used to notify caller of events/changes. * * The SRP client allows a single callback to be registered. So consecutive calls to this method will overwrite any * previously set callback functions. * * @param[in] aCallback The callback to notify of events and changes. Can be `nullptr` if not needed. * @param[in] aContext An arbitrary context used with @p aCallback. * */ void SetCallback(ClientCallback aCallback, void *aContext) { mCallback.Set(aCallback, aContext); } /** * Gets the TTL used in SRP update requests. * * Note that this is the TTL requested by the SRP client. The server may choose to accept a different TTL. * * By default, the TTL will equal the lease interval. Passing 0 or a value larger than the lease interval via * `otSrpClientSetTtl()` will also cause the TTL to equal the lease interval. * * @returns The TTL (in seconds). * */ uint32_t GetTtl(void) const { return mTtl; } /** * Sets the TTL used in SRP update requests. * * Changing the TTL does not impact the TTL of already registered services/host-info. * It only changes any future SRP update messages (i.e adding new services and/or refreshes of existing services). * * @param[in] aTtl The TTL (in seconds). If value is zero or greater than lease interval, the TTL is set to the * lease interval. * */ void SetTtl(uint32_t aTtl) { mTtl = aTtl; } /** * Gets the lease interval used in SRP update requests. * * Note that this is lease duration that would be requested by the SRP client. Server may choose to accept a * different lease interval. * * @returns The lease interval (in seconds). * */ uint32_t GetLeaseInterval(void) const { return mDefaultLease; } /** * Sets the lease interval used in SRP update requests. * * Changing the lease interval does not impact the accepted lease interval of already registered services/host-info. * It only changes any future SRP update messages (i.e adding new services and/or refreshes of existing services). * * @param[in] aInterval The lease interval (in seconds). If zero, the default value `kDefaultLease` would be used. * */ void SetLeaseInterval(uint32_t aInterval) { mDefaultLease = DetermineLeaseInterval(aInterval, kDefaultLease); } /** * Gets the key lease interval used in SRP update requests. * * @returns The key lease interval (in seconds). * */ uint32_t GetKeyLeaseInterval(void) const { return mDefaultKeyLease; } /** * Sets the key lease interval used in SRP update requests. * * Changing the lease interval does not impact the accepted lease interval of already registered services/host-info. * It only changes any future SRP update messages (i.e adding new services and/or refreshes of existing services). * * @param[in] aInterval The key lease interval (in seconds). If zero, the default value `kDefaultKeyLease` would be * used. * */ void SetKeyLeaseInterval(uint32_t aInterval) { mDefaultKeyLease = DetermineLeaseInterval(aInterval, kDefaultKeyLease); } /** * Gets the host info. * * @returns A reference to host info structure. * */ const HostInfo &GetHostInfo(void) const { return mHostInfo; } /** * Sets the host name label. * * After a successful call to this method, `Callback` will be called to report the status of host info * registration with SRP server. * * The host name can be set before client is started or after start but before host info is registered with server * (host info should be in either `kToAdd` or `kRemoved`). * * @param[in] aName A pointer to host name label string (MUST NOT be NULL). Pointer the string buffer MUST * persist and remain valid and constant after return from this method. * * @retval kErrorNone The host name label was set successfully. * @retval kErrorInvalidArgs The @p aName is NULL. * @retval kErrorInvalidState The host name is already set and registered with the server. * */ Error SetHostName(const char *aName); /** * Enables auto host address mode. * * When enabled host IPv6 addresses are automatically set by SRP client using all the unicast addresses on Thread * netif excluding the link-local and mesh-local addresses. If there is no valid address, then Mesh Local EID * address is added. The SRP client will automatically re-register when/if addresses on Thread netif are updated * (new addresses are added or existing addresses are removed). * * The auto host address mode can be enabled before start or during operation of SRP client except when the host * info is being removed (client is busy handling a remove request from an call to `RemoveHostAndServices()` and * host info still being in either `kStateToRemove` or `kStateRemoving` states). * * After auto host address mode is enabled, it can be disabled by a call to `SetHostAddresses()` which then * explicitly sets the host addresses. * * @retval kErrorNone Successfully enabled auto host address mode. * @retval kErrorInvalidState Host is being removed and therefore cannot enable auto host address mode. * */ Error EnableAutoHostAddress(void); /** * Sets/updates the list of host IPv6 address. * * Host IPv6 addresses can be set/changed before start or even during operation of SRP client (e.g. to add/remove * or change a previously registered host address), except when the host info is being removed (client is busy * handling a remove request from an earlier call to `RemoveHostAndServices()` and host info still being in either * `kStateToRemove` or `kStateRemoving` states). * * After a successful call to this method, `Callback` will be called to report the status of the address * registration with SRP server. * * Calling this method disables auto host address mode if it was previously enabled from a successful call to * `EnableAutoHostAddress()`. * * @param[in] aAddresses A pointer to the an array containing the host IPv6 addresses. * @param[in] aNumAddresses The number of addresses in the @p aAddresses array. * * @retval kErrorNone The host IPv6 address list change started successfully. The `Callback` will be * called to report the status of registering addresses with server. * @retval kErrorInvalidArgs The address list is invalid (e.g., must contain at least one address). * @retval kErrorInvalidState Host is being removed and therefore cannot change host address. * */ Error SetHostAddresses(const Ip6::Address *aAddresses, uint8_t aNumAddresses); /** * Adds a service to be registered with server. * * After a successful call to this method, `Callback` will be called to report the status of the service * addition/registration with SRP server. * * @param[in] aService A `Service` to add (the instance must persist and remain unchanged after * successful return from this method). * * @retval kErrorNone The addition of service started successfully. The `Callback` will be called to * report the status. * @retval kErrorAlready A service with the same service and instance names is already in the list. * @retval kErrorInvalidArgs The service structure is invalid (e.g., bad service name or `TxEntry`). * */ Error AddService(Service &aService); /** * Removes a service to be unregistered with server. * * @param[in] aService A `Service` to remove (the instance must persist and remain unchanged after * successful return from this method). * * @retval kErrorNone The removal of service started successfully. The `Callback` will be called to report * the status. * @retval kErrorNotFound The service could not be found in the list. * */ Error RemoveService(Service &aService); /** * Clears a service, immediately removing it from the client service list. * * Unlike `RemoveService()` which sends an update message to the server to remove the service, this method clears * the service from the client's service list without any interaction with the server. On a successful call * to this method, the `Callback` will NOT be called and the @p aService entry can be reclaimed and re-used by the * caller immediately. * * @param[in] aService A service to delete from the list. * * @retval kErrorNone The @p aService is cleared successfully. It can be reclaimed and re-used immediately. * @retval kErrorNotFound The service could not be found in the list. * */ Error ClearService(Service &aService); /** * Gets the list of services being managed by client. * * @returns The list of services. * */ const LinkedList &GetServices(void) const { return mServices; } /** * Starts the remove process of the host info and all services. * * After returning from this method, `Callback` will be called to report the status of remove request with * SRP server. * * If the host info is to be permanently removed from server, @p aRemoveKeyLease should be set to `true` which * removes the key lease associated with host on server. Otherwise, the key lease record is kept as before, which * ensures that the server holds the host name in reserve for when the client once again able to provide and * register its service(s). * * The @p aSendUnregToServer determines the behavior when the host info is not yet registered with the server. If * @p aSendUnregToServer is set to `false` (which is the default/expected value) then the SRP client will * immediately remove the host info and services without sending an update message to server (no need to update the * server if nothing is yet registered with it). If @p aSendUnregToServer is set to `true` then the SRP client will * send an update message to the server. Note that if the host info is registered then the value of * @p aSendUnregToServer does not matter and the SRP client will always send an update message to server requesting * removal of all info. * * One situation where @p aSendUnregToServer can be useful is on a device reset/reboot, caller may want to remove * any previously registered services with the server. In this case, caller can `SetHostName()` and then request * `RemoveHostAndServices()` with `aSendUnregToServer` as `true`. * * @param[in] aShouldRemoveKeyLease A boolean indicating whether or not the host key lease should also be removed. * @param[in] aSendUnregToServer A boolean indicating whether to send update to server when host info is not * registered. * * @retval kErrorNone The removal of host and services started successfully. The `Callback` will be called * to report the status. * @retval kErrorAlready The host is already removed. * */ Error RemoveHostAndServices(bool aShouldRemoveKeyLease, bool aSendUnregToServer = false); /** * Clears all host info and all the services. * * Unlike `RemoveHostAndServices()` which sends an update message to the server to remove all the info, this method * clears all the info immediately without any interaction with the server. * */ void ClearHostAndServices(void); #if OPENTHREAD_CONFIG_SRP_CLIENT_DOMAIN_NAME_API_ENABLE /** * Gets the domain name being used by SRP client. * * If domain name is not set, "default.service.arpa" will be used. * * @returns The domain name string. * */ const char *GetDomainName(void) const { return mDomainName; } /** * Sets the domain name to be used by SRP client. * * This is an optional method. If not set "default.service.arpa" will be used. * * The domain name can be set before client is started or after start but before host info is registered with server * (host info should be in either `kToAdd` or `kToRemove`). * * @param[in] aName A pointer to the domain name string. If NULL sets it to default "default.service.arpa". * * @retval kErrorNone The domain name label was set successfully. * @retval kErrorInvalidState The host info is already registered with server. * */ Error SetDomainName(const char *aName); #endif // OPENTHREAD_CONFIG_SRP_CLIENT_DOMAIN_NAME_API_ENABLE /** * Converts a `ItemState` to a string. * * @param[in] aState An `ItemState`. * * @returns A string representation of @p aState. * */ static const char *ItemStateToString(ItemState aState); #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE /** * Enables/disables "service key record inclusion" mode. * * When enabled, SRP client will include KEY record in Service Description Instructions in the SRP update messages * that it sends. * * @note KEY record is optional in Service Description Instruction (it is required and always included in the Host * Description Instruction). The default behavior of SRP client is to not include it. This method is added under * `REFERENCE_DEVICE` config and is intended to override the default behavior for testing only. * * @param[in] aEnabled TRUE to enable, FALSE to disable the "service key record inclusion" mode. * */ void SetServiceKeyRecordEnabled(bool aEnabled) { mServiceKeyRecordEnabled = aEnabled; } /** * Indicates whether the "service key record inclusion" mode is enabled or disabled. * * @returns TRUE if "service key record inclusion" mode is enabled, FALSE otherwise. * */ bool IsServiceKeyRecordEnabled(void) const { return mServiceKeyRecordEnabled; } /** * Enables/disables "use short Update Lease Option" behavior. * * When enabled, the SRP client will use the short variant format of Update Lease Option in its message. The short * format only includes the lease interval. * * Is added under `REFERENCE_DEVICE` config and is intended to override the default behavior for * testing only. * * @param[in] aUseShort TRUE to enable, FALSE to disable the "use short Update Lease Option" mode. * */ void SetUseShortLeaseOption(bool aUseShort) { mUseShortLeaseOption = aUseShort; } /** * Gets the current "use short Update Lease Option" mode. * * @returns TRUE if "use short Update Lease Option" mode is enabled, FALSE otherwise. * */ bool GetUseShortLeaseOption(void) const { return mUseShortLeaseOption; } #endif // OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE private: // Number of fast data polls after SRP Update tx (11x 188ms = ~2 seconds) static constexpr uint8_t kFastPollsAfterUpdateTx = 11; #if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE static constexpr uint32_t kSrpEcdsaKeyRef = Crypto::Storage::kEcdsaRef; #endif #if OPENTHREAD_CONFIG_SRP_CLIENT_SWITCH_SERVER_ON_FAILURE static constexpr uint8_t kMaxTimeoutFailuresToSwitchServer = OPENTHREAD_CONFIG_SRP_CLIENT_MAX_TIMEOUT_FAILURES_TO_SWITCH_SERVER; #endif static constexpr uint16_t kUdpPayloadSize = Ip6::kMaxDatagramLength - sizeof(Ip6::Udp::Header); // ------------------------------- // Lease related constants static constexpr uint32_t kDefaultLease = OPENTHREAD_CONFIG_SRP_CLIENT_DEFAULT_LEASE; // in seconds. static constexpr uint32_t kDefaultKeyLease = OPENTHREAD_CONFIG_SRP_CLIENT_DEFAULT_KEY_LEASE; // in seconds. // The guard interval determines how much earlier (relative to // the lease expiration time) the client will send an update // to renew the lease. Value is in seconds. static constexpr uint32_t kLeaseRenewGuardInterval = OPENTHREAD_CONFIG_SRP_CLIENT_LEASE_RENEW_GUARD_INTERVAL; // Max allowed lease time to avoid timer roll-over (~24.8 days). static constexpr uint32_t kMaxLease = (Timer::kMaxDelay / 1000) - 1; // Opportunistic early refresh: When sending an SRP update, the // services that are not yet expired but are close, are allowed // to refresh early and are included in the SRP update. This // helps place more services on the same lease refresh schedule // reducing number of messages sent to the SRP server. The // "early lease renewal interval" is used to determine if a // service can renew early. The interval is calculated by // multiplying the accepted lease interval by the"early lease // renewal factor" which is given as a fraction (numerator and // denominator). // // If the factor is set to zero (numerator=0, denominator=1), // the opportunistic early refresh behavior is disabled. If // denominator is set to zero (the factor is set to infinity), // then all services (including previously registered ones) // are always included in SRP update message. static constexpr uint32_t kEarlyLeaseRenewFactorNumerator = OPENTHREAD_CONFIG_SRP_CLIENT_EARLY_LEASE_RENEW_FACTOR_NUMERATOR; static constexpr uint32_t kEarlyLeaseRenewFactorDenominator = OPENTHREAD_CONFIG_SRP_CLIENT_EARLY_LEASE_RENEW_FACTOR_DENOMINATOR; // ------------------------------- // When there is a change (e.g., a new service is added/removed) // that requires an update, the SRP client will wait for a short // delay as specified by `kUpdateTxDelay` before sending an SRP // update to server. This allows the user to provide more change // that are then all sent in same update message. static constexpr uint32_t kUpdateTxMinDelay = OPENTHREAD_CONFIG_SRP_CLIENT_UPDATE_TX_MIN_DELAY; // in msec. static constexpr uint32_t kUpdateTxMaxDelay = OPENTHREAD_CONFIG_SRP_CLIENT_UPDATE_TX_MAX_DELAY; // in msec. // ------------------------------- // Retry related constants // // If the preparation or transmission of an SRP update message // fails (e.g., no buffer to allocate the message), SRP client // will retry after a short interval `kTxFailureRetryInterval` // up to `kMaxTxFailureRetries` attempts. After this, the retry // wait interval will be used (which keeps growing on each failure // - please see below). // // If the update message is sent successfully but there is no // response from server or if server rejects the update, the // client will retransmit the update message after some wait // interval. The wait interval starts from the minimum value and // is increased by the growth factor on back-to-back failures up // to the max value. The growth factor is given as a fraction // (e.g., for 1.5, we can use 15 as the numerator and 10 as the // denominator). A random jitter is added to the retry interval. // If the current wait interval value is smaller than the jitter // interval, then wait interval value itself is used as the // jitter value. For example, with jitter interval of 2 seconds // if the current retry interval is 800ms, then a random wait // interval in [0,2*800] ms will be used. static constexpr uint32_t kTxFailureRetryInterval = 250; // in ms static constexpr uint32_t kMaxTxFailureRetries = 8; // num of quick retries after tx failure static constexpr uint32_t kMinRetryWaitInterval = OPENTHREAD_CONFIG_SRP_CLIENT_MIN_RETRY_WAIT_INTERVAL; // in ms static constexpr uint32_t kMaxRetryWaitInterval = OPENTHREAD_CONFIG_SRP_CLIENT_MAX_RETRY_WAIT_INTERVAL; // in ms static constexpr uint32_t kRetryIntervalGrowthFactorNumerator = OPENTHREAD_CONFIG_SRP_CLIENT_RETRY_INTERVAL_GROWTH_FACTOR_NUMERATOR; static constexpr uint32_t kRetryIntervalGrowthFactorDenominator = OPENTHREAD_CONFIG_SRP_CLIENT_RETRY_INTERVAL_GROWTH_FACTOR_DENOMINATOR; static constexpr uint16_t kTxFailureRetryJitter = 10; // in ms static constexpr uint16_t kRetryIntervalJitter = OPENTHREAD_CONFIG_SRP_CLIENT_RETRY_WAIT_INTERVAL_JITTER; // in ms static_assert(kDefaultLease <= static_cast(kMaxLease), "kDefaultLease is larger than max"); static_assert(kDefaultKeyLease <= static_cast(kMaxLease), "kDefaultKeyLease is larger than max"); enum State : uint8_t { kStateStopped, // Client is stopped. kStatePaused, // Client is paused (due to device being detached). kStateToUpdate, // Waiting to send SRP update kStateUpdating, // SRP update is sent, waiting for response from server. kStateUpdated, // SRP update response received from server. kStateToRetry, // SRP update tx failed, waiting to retry. }; static constexpr bool kAutoStartDefaultMode = OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_DEFAULT_MODE; static constexpr bool kDisallowSwitchOnRegisteredHost = OPENTHREAD_CONFIG_SRP_CLIENT_DISALLOW_SERVER_SWITCH_WITH_REGISTERED_HOST; // Port number to use when server is discovered using "network data anycast service". static constexpr uint16_t kAnycastServerPort = 53; static constexpr uint32_t kUnspecifiedInterval = 0; // Used for lease/key-lease intervals. // This enumeration type is used by the private `Start()` and // `Stop()` methods to indicate whether it is being requested by the // user or by the auto-start feature. enum Requester : uint8_t { kRequesterUser, #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE kRequesterAuto, #endif }; // This enumeration is used as an input to private `Stop()` to // indicate whether to reset the retry interval or keep it as is. enum StopMode : uint8_t { kResetRetryInterval, kKeepRetryInterval, }; // Used in `ChangeHostAndServiceStates()` enum ServiceStateChangeMode : uint8_t { kForAllServices, kForServicesAppendedInMessage, }; #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE class AutoStart : public Clearable { public: enum State : uint8_t{ kDisabled, // AutoStart is disabled. kSelectedNone, // AutoStart is enabled but not yet selected any servers. kSelectedUnicastPreferred, // AutoStart selected a preferred unicast entry (address in service data). kSelectedAnycast, // AutoStart selected an anycast entry with `mAnycastSeqNum`. kSelectedUnicast, // AutoStart selected a unicast entry (address in server data). }; AutoStart(void); bool HasSelectedServer(void) const; State GetState(void) const { return mState; } void SetState(State aState); uint8_t GetAnycastSeqNum(void) const { return mAnycastSeqNum; } void SetAnycastSeqNum(uint8_t aAnycastSeqNum) { mAnycastSeqNum = aAnycastSeqNum; } void SetCallback(AutoStartCallback aCallback, void *aContext) { mCallback.Set(aCallback, aContext); } void InvokeCallback(const Ip6::SockAddr *aServerSockAddr) const; #if OPENTHREAD_CONFIG_SRP_CLIENT_SWITCH_SERVER_ON_FAILURE uint8_t GetTimeoutFailureCount(void) const { return mTimeoutFailureCount; } void ResetTimeoutFailureCount(void) { mTimeoutFailureCount = 0; } void IncrementTimeoutFailureCount(void) { if (mTimeoutFailureCount < NumericLimits::kMax) { mTimeoutFailureCount++; } } #endif private: static constexpr bool kDefaultMode = OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_DEFAULT_MODE; static const char *StateToString(State aState); Callback mCallback; State mState; uint8_t mAnycastSeqNum; #if OPENTHREAD_CONFIG_SRP_CLIENT_SWITCH_SERVER_ON_FAILURE uint8_t mTimeoutFailureCount; // Number of no-response timeout failures with the currently selected server. #endif }; #endif // OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE struct Info : public Clearable { static constexpr uint16_t kUnknownOffset = 0; // Unknown offset value (used when offset is not yet set). uint16_t mDomainNameOffset; // Offset of domain name serialization uint16_t mHostNameOffset; // Offset of host name serialization. uint16_t mRecordCount; // Number of resource records in Update section. #if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE Crypto::Ecdsa::P256::KeyPairAsRef mKeyRef; // The ECDSA key ref for key-pair. #else Crypto::Ecdsa::P256::KeyPair mKeyPair; // The ECDSA key pair. #endif }; Error Start(const Ip6::SockAddr &aServerSockAddr, Requester aRequester); void Stop(Requester aRequester, StopMode aMode); void Resume(void); void Pause(void); void HandleNotifierEvents(Events aEvents); void HandleRoleChanged(void); Error UpdateHostInfoStateOnAddressChange(void); void UpdateServiceStateToRemove(Service &aService); State GetState(void) const { return mState; } void SetState(State aState); void ChangeHostAndServiceStates(const ItemState *aNewStates, ServiceStateChangeMode aMode); void InvokeCallback(Error aError) const; void InvokeCallback(Error aError, const HostInfo &aHostInfo, const Service *aRemovedServices) const; void HandleHostInfoOrServiceChange(void); void SendUpdate(void); Error PrepareUpdateMessage(Message &aMessage); #if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE Error ReadOrGenerateKey(Crypto::Ecdsa::P256::KeyPairAsRef &aKeyRef); #else Error ReadOrGenerateKey(Crypto::Ecdsa::P256::KeyPair &aKeyPair); #endif Error AppendServiceInstructions(Message &aMessage, Info &aInfo); bool CanAppendService(const Service &aService); Error AppendServiceInstruction(Service &aService, Message &aMessage, Info &aInfo); Error AppendHostDescriptionInstruction(Message &aMessage, Info &aInfo); Error AppendKeyRecord(Message &aMessage, Info &aInfo) const; Error AppendDeleteAllRrsets(Message &aMessage) const; Error AppendHostName(Message &aMessage, Info &aInfo, bool aDoNotCompress = false) const; Error AppendAaaaRecord(const Ip6::Address &aAddress, Message &aMessage, Info &aInfo) const; Error AppendUpdateLeaseOptRecord(Message &aMessage); Error AppendSignature(Message &aMessage, Info &aInfo); void UpdateRecordLengthInMessage(Dns::ResourceRecord &aRecord, uint16_t aOffset, Message &aMessage) const; static void HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); void ProcessResponse(Message &aMessage); void HandleUpdateDone(void); void GetRemovedServices(LinkedList &aRemovedServices); static Error ReadResourceRecord(const Message &aMessage, uint16_t &aOffset, Dns::ResourceRecord &aRecord); Error ProcessOptRecord(const Message &aMessage, uint16_t aOffset, const Dns::OptRecord &aOptRecord); void UpdateState(void); uint32_t GetRetryWaitInterval(void) const { return mRetryWaitInterval; } void ResetRetryWaitInterval(void) { mRetryWaitInterval = kMinRetryWaitInterval; } void GrowRetryWaitInterval(void); uint32_t DetermineLeaseInterval(uint32_t aInterval, uint32_t aDefaultInterval) const; uint32_t DetermineTtl(void) const; bool ShouldRenewEarly(const Service &aService) const; void HandleTimer(void); #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE void ProcessAutoStart(void); Error SelectUnicastEntry(DnsSrpUnicast::Origin aOrigin, DnsSrpUnicast::Info &aInfo) const; #if OPENTHREAD_CONFIG_SRP_CLIENT_SWITCH_SERVER_ON_FAILURE void SelectNextServer(bool aDisallowSwitchOnRegisteredHost); #endif #endif #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) static const char *StateToString(State aState); void LogRetryWaitInterval(void) const; #else void LogRetryWaitInterval(void) const {} #endif static const char kDefaultDomainName[]; static_assert(kMaxTxFailureRetries < 16, "kMaxTxFailureRetries exceed the range of mTxFailureRetryCount (4-bit)"); using DelayTimer = TimerMilliIn; State mState; uint8_t mTxFailureRetryCount : 4; bool mShouldRemoveKeyLease : 1; bool mAutoHostAddressAddedMeshLocal : 1; bool mSingleServiceMode : 1; #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE bool mServiceKeyRecordEnabled : 1; bool mUseShortLeaseOption : 1; #endif uint16_t mUpdateMessageId; uint32_t mRetryWaitInterval; TimeMilli mLeaseRenewTime; uint32_t mTtl; uint32_t mLease; uint32_t mKeyLease; uint32_t mDefaultLease; uint32_t mDefaultKeyLease; Ip6::Udp::Socket mSocket; Callback mCallback; const char *mDomainName; HostInfo mHostInfo; LinkedList mServices; DelayTimer mTimer; #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE AutoStart mAutoStart; #endif }; } // namespace Srp DefineCoreType(otSrpClientHostInfo, Srp::Client::HostInfo); DefineCoreType(otSrpClientService, Srp::Client::Service); DefineMapEnum(otSrpClientItemState, Srp::Client::ItemState); } // namespace ot #endif // OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE #endif // SRP_CLIENT_HPP_