1 /* 2 * Copyright (c) 2023, The OpenThread Authors. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 3. Neither the name of the copyright holder nor the 13 * names of its contributors may be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 /** 30 * @file 31 * This file includes definitions for Advertising Proxy. 32 */ 33 34 #ifndef SRP_ADVERTISING_PROXY_HPP_ 35 #define SRP_ADVERTISING_PROXY_HPP_ 36 37 #include "openthread-core-config.h" 38 39 #if OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE 40 41 #if !OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE && !OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE 42 #error "OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE requires PLATFORM_DNSSD_ENABLE or MULTICAST_DNS_ENABLE" 43 #endif 44 45 #if !OPENTHREAD_CONFIG_SRP_SERVER_ENABLE 46 #error "OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE requires OPENTHREAD_CONFIG_SRP_SERVER_ENABLE" 47 #endif 48 49 #if !OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE 50 #error "OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE requires OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE" 51 #endif 52 53 #include "common/clearable.hpp" 54 #include "common/heap_allocatable.hpp" 55 #include "common/locator.hpp" 56 #include "common/non_copyable.hpp" 57 #include "common/owning_list.hpp" 58 #include "common/tasklet.hpp" 59 #include "common/timer.hpp" 60 #include "net/dnssd.hpp" 61 #include "net/srp_server.hpp" 62 63 namespace ot { 64 namespace Srp { 65 66 /** 67 * Implements SRP Advertising Proxy. 68 */ 69 class AdvertisingProxy : public InstanceLocator, private NonCopyable 70 { 71 public: 72 typedef Server::Host Host; ///< An SRP server host registration. 73 typedef Server::Service Service; ///< An SRP server service registration. 74 75 /** 76 * Represents counters for Advertising Proxy. 77 */ 78 struct Counters : public Clearable<Counters> 79 { 80 uint32_t mAdvTotal; ///< Total number of advertisement requests, i.e., calls to `Advertise()`. 81 uint32_t mAdvReplaced; ///< Number of advertisements that were replaced by a newer one. 82 uint32_t mAdvSkipped; ///< Number of advertisement that were skipped (DNS-SD platform not yet ready). 83 uint32_t mAdvSuccessful; ///< Number of successful adv (all requests registered successfully). 84 uint32_t mAdvRejected; ///< Number of rejected adv (at least one request was rejected by DNS-SD plat). 85 uint32_t mAdvTimeout; ///< Number of advertisements that timed out (no response from DNS-SD platform). 86 uint32_t mAdvHostRemovals; ///< Number of host removal adv, i.e., calls to `AdvertiseRemovalOf(Host &)` 87 uint32_t mAdvServiceRemovals; ///< Number of service removal adv, i.e., calls to `AdvertiseRemovalOf(Service &)` 88 uint32_t mStateChanges; ///< Number of state changes of Advertising Proxy. 89 }; 90 91 /** 92 * Initializes the `AdvertisingProxy` object. 93 * 94 * @param[in] aInstance The OpenThread instance 95 */ 96 explicit AdvertisingProxy(Instance &aInstance); 97 98 /** 99 * Indicates whether or not the Advertising Proxy is running. 100 * 101 * @retval TRUE The Advertising Proxy is running. 102 * @retval FALSE The Advertising Proxy is not running (it is stopped). 103 */ IsRunning(void) const104 bool IsRunning(void) const { return mState == kStateRunning; } 105 106 /** 107 * Requests advertisement of a newly received SRP Update message. 108 * 109 * Once advertisement is completed, `AdvertisingProxy` notifies server by invoking `Server::CommitSrpUpdate()` 110 * using the same `aHost` and `aMetadata` as input parameters along with an `Error` indicating the outcome of the 111 * advertisement. 112 * 113 * The `aHost` instance ownership is passed to `AdvertisingProxy` until it is passed back to the `Server` in the 114 * `CommitSrpUpdate()` call. The call to `CommitSrpUpdate()` may happen before this method returns, for example, 115 * if the proxy is not running and therefore the advertisement is skipped. 116 * 117 * @param[in] aHost The `aHost` instance constructed from processing a newly received SRP Update message. 118 * @param[in] aMetadata The `MessageMetadata` associated with the received SRP Update message by server. 119 */ 120 void Advertise(Host &aHost, const Server::MessageMetadata &aMetadata); 121 122 /** 123 * Requests advertisement of removal of an already committed host and all its services, for example, due to its 124 * lease expiration. 125 * 126 * The removal does not use any callback to notify the SRP server since the server always immediately commits the 127 * removed entries. 128 * 129 * If there is an outstanding advertisement request (an earlier call to `Advertise()` that has not yet completed) 130 * that is registering the same host name as @p aHost that is being removed, the outstanding advertisement is 131 * rejected using the `kErrorAbort` error. This situation can happen if the client tries to refresh or update its 132 * registration close to its lease expiration time. By rejecting any outstanding advertisements, we ensure that an 133 * expired host is not re-added by mistake due to a delay in registration by the DNS-SD platform. The error is 134 * passed back to the client, triggering it to retry its registration. 135 * 136 * @param[in] aHost The host which is being removed. 137 */ 138 void AdvertiseRemovalOf(Host &aHost); 139 140 /** 141 * Requests advertisement of removal of an already committed service, for example, due to its lease expiration. 142 * 143 * The removal does not use any callback to notify the SRP server since the server always immediately commits the 144 * removed services. 145 * 146 * If there is an outstanding advertisement request (an earlier call to `Advertise()` that has not yet completed) 147 * that is registering the same service as @p aService that is being removed, we skip the advertisement of service 148 * removal (do not unregister the service on infrastructure DNS-SD). This ensures that when the outstanding 149 * advertisement is completed, the service is re-added successfully (and it is still being advertised by proxy). 150 * This behavior is different from `AdvertiseRemovalOf(Host &)`, where the outstanding advertisement is rejected 151 * because service removals are individual, compared to when removing a host where the host and all its associated 152 * services are removed. 153 * 154 * @param[in] aHost The host which is being removed. 155 */ 156 void AdvertiseRemovalOf(Service &aService); 157 158 /** 159 * Gets the set of counters. 160 * 161 * @returns The `AdvertisingProxy` counter. 162 */ GetCounters(void) const163 const Counters &GetCounters(void) const { return mCounters; } 164 165 /** 166 * Resets the counters 167 */ ResetCounters(void)168 void ResetCounters(void) { mCounters.Clear(); } 169 170 /** 171 * Gets the advertisement timeout (in msec). 172 * 173 * The default value of `OPENTHREAD_CONFIG_SRP_SERVER_SERVICE_UPDATE_TIMEOUT` is used when not explicitly set. 174 * 175 * @returns The advertisement timeout (in msec). 176 */ GetAdvTimeout(void) const177 uint32_t GetAdvTimeout(void) const { return mAdvTimeout; } 178 179 /** 180 * Sets the advertisement timeout. 181 * 182 * Changing the timeout is intended for testing purposes only. This allows tests to use a long timeout to validate 183 * the behavior of `AdvertisingProxy` when new `Advertise()` requests replace entries in earlier requests. 184 * 185 * @param[in] aTimeout The advertisement timeout (in msec). 186 */ SetAdvTimeout(uint32_t aTimeout)187 void SetAdvTimeout(uint32_t aTimeout) { mAdvTimeout = Max(aTimeout, kAdvTimeout); } 188 189 /** 190 * Notifies `AdvertisingProxy` that SRP sever state changed. 191 */ HandleServerStateChange(void)192 void HandleServerStateChange(void) { UpdateState(); } 193 194 /** 195 * Notifies `AdvertisingProxy` that DND-SD platform state changed. 196 */ HandleDnssdPlatformStateChange(void)197 void HandleDnssdPlatformStateChange(void) { UpdateState(); } 198 199 /** 200 * Notifies `AdvertisingProxy` that `InfraIf` state changed. 201 */ HandleInfraIfStateChanged(void)202 void HandleInfraIfStateChanged(void) { UpdateState(); } 203 204 private: 205 typedef Dnssd::RequestId RequestId; 206 typedef char DnsName[Dns::Name::kMaxNameSize]; 207 208 static constexpr RequestId kInvalidRequestId = Server::kInvalidRequestId; 209 210 static constexpr uint32_t kAdvTimeout = OPENTHREAD_CONFIG_SRP_SERVER_SERVICE_UPDATE_TIMEOUT; // in msec 211 212 enum State : uint8_t 213 { 214 kStateStopped, 215 kStateRunning, 216 }; 217 218 struct AdvInfo : public Heap::Allocatable<AdvInfo>, public LinkedListEntry<AdvInfo>, public GetProvider<AdvInfo> 219 { 220 struct CompletionChecker 221 { 222 // Used in `Matches()` to check if advertisement is 223 // completed (successfully or failed). 224 }; 225 226 struct ExpirationChecker 227 { ExpirationCheckerot::Srp::AdvertisingProxy::AdvInfo::ExpirationChecker228 explicit ExpirationChecker(TimeMilli aNow) 229 : mNow(aNow) 230 { 231 } 232 233 TimeMilli mNow; 234 }; 235 236 AdvInfo(Host &aHost, const Server::MessageMetadata &aMetadata, uint32_t aTimeout); 237 void SignalServerToCommit(void); 238 bool IsCompleted(void) const; Matchesot::Srp::AdvertisingProxy::AdvInfo239 bool Matches(const CompletionChecker &) const { return IsCompleted(); } Matchesot::Srp::AdvertisingProxy::AdvInfo240 bool Matches(const ExpirationChecker &aChecker) const { return (mExpireTime <= aChecker.mNow); } GetInstanceot::Srp::AdvertisingProxy::AdvInfo241 Instance &GetInstance(void) const { return mHost.GetInstance(); } 242 243 AdvInfo *mNext; 244 AdvInfo *mBlockingAdv; 245 Host &mHost; 246 TimeMilli mExpireTime; 247 Server::MessageMetadata mMessageMetadata; 248 Ip6::MessageInfo mMessageInfo; 249 Error mError; 250 }; 251 252 template <typename Entry> void UpdateAdvIdRangeOn(Entry &aEntry); 253 template <typename Entry> bool IsRegisteredOrRegistering(const Entry &aEntry) const; 254 template <typename Entry> bool IsKeyRegisteredOrRegistering(const Entry &aEntry) const; 255 template <typename Entry> void DecideToAdvertise(Entry &aEntry, bool aUnregisterEntry, bool aUnregisterKey); 256 template <typename Entry> void UpdateKeyRegistrationStatus(Entry &aEntry, const Entry &aExistingEntry); 257 template <typename Entry> bool CompareAndUpdate(Entry &aEntry, Entry &aExistingEntry); 258 template <typename Entry> bool EntriesMatch(const Entry &aFirstEntry, const Entry &aSecondEntry); 259 260 void Start(void); 261 void Stop(void); 262 void UpdateState(void); 263 RequestId AllocateNextRequestId(void); 264 void Advertise(Host &aHost); 265 void UnregisterHostAndItsServicesAndKeys(Host &aHost); 266 bool CompareAndUpdateHostAndServices(Host &aHost, Host &aExistingHost); 267 bool CompareAndUpdateHost(Host &aHost, Host &aExistingHost); 268 bool CompareAndUpdateService(Service &aService, Service &aExistingService); 269 void RegisterHost(Host &aHost); 270 void UnregisterHost(Host &aHost); 271 void RegisterService(Service &aService); 272 void UnregisterService(Service &aService); 273 void RegisterKey(Host &aHost); 274 void RegisterKey(Service &aService); 275 void RegisterKey(const char *aName, 276 const char *aServiceType, 277 const Host::Key &aKey, 278 RequestId aRequestId, 279 uint32_t aTtl); 280 void UnregisterKey(Service &aService); 281 void UnregisterKey(Host &aHost); 282 void UnregisterKey(const char *aName, const char *aServiceType); 283 void CopyNameAndRemoveDomain(DnsName &aName, const char *aFullName); 284 static void HandleRegistered(otInstance *aInstance, otPlatDnssdRequestId aRequestId, otError aError); 285 void HandleRegistered(RequestId aRequestId, Error aError); 286 bool HandleRegisteredRequestIdOn(Host &aHost, RequestId aRequestId, Error aError); 287 void HandleTimer(void); 288 void HandleTasklet(void); 289 void SignalAdvCompleted(AdvInfo &aAdvInfo); 290 291 using AdvTimer = TimerMilliIn<AdvertisingProxy, &AdvertisingProxy::HandleTimer>; 292 using AdvTasklet = TaskletIn<AdvertisingProxy, &AdvertisingProxy::HandleTasklet>; 293 294 State mState; 295 RequestId mCurrentRequestId; 296 uint32_t mAdvTimeout; 297 OwningList<AdvInfo> mAdvInfoList; 298 AdvTimer mTimer; 299 AdvTasklet mTasklet; 300 Counters mCounters; 301 }; 302 303 } // namespace Srp 304 } // namespace ot 305 306 #endif // OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE 307 308 #endif // SRP_ADVERTISING_PROXY_HPP_ 309