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 */ 70 class AdvertisingProxy : public InstanceLocator, private NonCopyable 71 { 72 public: 73 typedef Server::Host Host; ///< An SRP server host registration. 74 typedef Server::Service Service; ///< An SRP server service registration. 75 76 /** 77 * Represents counters for Advertising Proxy. 78 * 79 */ 80 struct Counters : public Clearable<Counters> 81 { 82 uint32_t mAdvTotal; ///< Total number of advertisement requests, i.e., calls to `Advertise()`. 83 uint32_t mAdvReplaced; ///< Number of advertisements that were replaced by a newer one. 84 uint32_t mAdvSkipped; ///< Number of advertisement that were skipped (DNS-SD platform not yet ready). 85 uint32_t mAdvSuccessful; ///< Number of successful adv (all requests registered successfully). 86 uint32_t mAdvRejected; ///< Number of rejected adv (at least one request was rejected by DNS-SD plat). 87 uint32_t mAdvTimeout; ///< Number of advertisements that timed out (no response from DNS-SD platform). 88 uint32_t mAdvHostRemovals; ///< Number of host removal adv, i.e., calls to `AdvertiseRemovalOf(Host &)` 89 uint32_t mAdvServiceRemovals; ///< Number of service removal adv, i.e., calls to `AdvertiseRemovalOf(Service &)` 90 uint32_t mStateChanges; ///< Number of state changes of Advertising Proxy. 91 }; 92 93 /** 94 * Initializes the `AdvertisingProxy` object. 95 * 96 * @param[in] aInstance The OpenThread instance 97 * 98 */ 99 explicit AdvertisingProxy(Instance &aInstance); 100 101 /** 102 * Indicates whether or not the Advertising Proxy is running. 103 * 104 * @retval TRUE The Advertising Proxy is running. 105 * @retval FALSE The Advertising Proxy is not running (it is stopped). 106 * 107 */ IsRunning(void) const108 bool IsRunning(void) const { return mState == kStateRunning; } 109 110 /** 111 * Requests advertisement of a newly received SRP Update message. 112 * 113 * Once advertisement is completed, `AdvertisingProxy` notifies server by invoking `Server::CommitSrpUpdate()` 114 * using the same `aHost` and `aMetadata` as input parameters along with an `Error` indicating the outcome of the 115 * advertisement. 116 * 117 * The `aHost` instance ownership is passed to `AdvertisingProxy` until it is passed back to the `Server` in the 118 * `CommitSrpUpdate()` call. The call to `CommitSrpUpdate()` may happen before this method returns, for example, 119 * if the proxy is not running and therefore the advertisement is skipped. 120 * 121 * @param[in] aHost The `aHost` instance constructed from processing a newly received SRP Update message. 122 * @param[in] aMetadata The `MessageMetadata` associated with the received SRP Update message by server. 123 * 124 */ 125 void Advertise(Host &aHost, const Server::MessageMetadata &aMetadata); 126 127 /** 128 * Requests advertisement of removal of an already committed host and all its services, for example, due to its 129 * lease expiration. 130 * 131 * The removal does not use any callback to notify the SRP server since the server always immediately commits the 132 * removed entries. 133 * 134 * If there is an outstanding advertisement request (an earlier call to `Advertise()` that has not yet completed) 135 * that is registering the same host name as @p aHost that is being removed, the outstanding advertisement is 136 * rejected using the `kErrorAbort` error. This situation can happen if the client tries to refresh or update its 137 * registration close to its lease expiration time. By rejecting any outstanding advertisements, we ensure that an 138 * expired host is not re-added by mistake due to a delay in registration by the DNS-SD platform. The error is 139 * passed back to the client, triggering it to retry its registration. 140 * 141 * @param[in] aHost The host which is being removed. 142 * 143 */ 144 void AdvertiseRemovalOf(Host &aHost); 145 146 /** 147 * Requests advertisement of removal of an already committed service, for example, due to its lease expiration. 148 * 149 * The removal does not use any callback to notify the SRP server since the server always immediately commits the 150 * removed services. 151 * 152 * If there is an outstanding advertisement request (an earlier call to `Advertise()` that has not yet completed) 153 * that is registering the same service as @p aService that is being removed, we skip the advertisement of service 154 * removal (do not unregister the service on infrastructure DNS-SD). This ensures that when the outstanding 155 * advertisement is completed, the service is re-added successfully (and it is still being advertised by proxy). 156 * This behavior is different from `AdvertiseRemovalOf(Host &)`, where the outstanding advertisement is rejected 157 * because service removals are individual, compared to when removing a host where the host and all its associated 158 * services are removed. 159 * 160 * @param[in] aHost The host which is being removed. 161 * 162 */ 163 void AdvertiseRemovalOf(Service &aService); 164 165 /** 166 * Gets the set of counters. 167 * 168 * @returns The `AdvertisingProxy` counter. 169 * 170 */ GetCounters(void) const171 const Counters &GetCounters(void) const { return mCounters; } 172 173 /** 174 * Resets the counters 175 * 176 */ ResetCounters(void)177 void ResetCounters(void) { mCounters.Clear(); } 178 179 /** 180 * Gets the advertisement timeout (in msec). 181 * 182 * The default value of `OPENTHREAD_CONFIG_SRP_SERVER_SERVICE_UPDATE_TIMEOUT` is used when not explicitly set. 183 * 184 * @returns The advertisement timeout (in msec). 185 * 186 */ GetAdvTimeout(void) const187 uint32_t GetAdvTimeout(void) const { return mAdvTimeout; } 188 189 /** 190 * Sets the advertisement timeout. 191 * 192 * Changing the timeout is intended for testing purposes only. This allows tests to use a long timeout to validate 193 * the behavior of `AdvertisingProxy` when new `Advertise()` requests replace entries in earlier requests. 194 * 195 * @param[in] aTimeout The advertisement timeout (in msec). 196 * 197 */ SetAdvTimeout(uint32_t aTimeout)198 void SetAdvTimeout(uint32_t aTimeout) { mAdvTimeout = Max(aTimeout, kAdvTimeout); } 199 200 /** 201 * Notifies `AdvertisingProxy` that SRP sever state changed. 202 * 203 */ HandleServerStateChange(void)204 void HandleServerStateChange(void) { UpdateState(); } 205 206 /** 207 * Notifies `AdvertisingProxy` that DND-SD platform state changed. 208 * 209 */ HandleDnssdPlatformStateChange(void)210 void HandleDnssdPlatformStateChange(void) { UpdateState(); } 211 212 /** 213 * Notifies `AdvertisingProxy` that `InfraIf` state changed. 214 * 215 */ HandleInfraIfStateChanged(void)216 void HandleInfraIfStateChanged(void) { UpdateState(); } 217 218 private: 219 typedef Dnssd::RequestId RequestId; 220 typedef char DnsName[Dns::Name::kMaxNameSize]; 221 222 static constexpr RequestId kInvalidRequestId = Server::kInvalidRequestId; 223 224 static constexpr uint32_t kAdvTimeout = OPENTHREAD_CONFIG_SRP_SERVER_SERVICE_UPDATE_TIMEOUT; // in msec 225 226 enum State : uint8_t 227 { 228 kStateStopped, 229 kStateRunning, 230 }; 231 232 struct AdvInfo : public Heap::Allocatable<AdvInfo>, public LinkedListEntry<AdvInfo>, public GetProvider<AdvInfo> 233 { 234 struct CompletionChecker 235 { 236 // Used in `Matches()` to check if advertisement is 237 // completed (successfully or failed). 238 }; 239 240 struct ExpirationChecker 241 { ExpirationCheckerot::Srp::AdvertisingProxy::AdvInfo::ExpirationChecker242 explicit ExpirationChecker(TimeMilli aNow) 243 : mNow(aNow) 244 { 245 } 246 247 TimeMilli mNow; 248 }; 249 250 AdvInfo(Host &aHost, const Server::MessageMetadata &aMetadata, uint32_t aTimeout); 251 void SignalServerToCommit(void); 252 bool IsCompleted(void) const; Matchesot::Srp::AdvertisingProxy::AdvInfo253 bool Matches(const CompletionChecker &) const { return IsCompleted(); } Matchesot::Srp::AdvertisingProxy::AdvInfo254 bool Matches(const ExpirationChecker &aChecker) const { return (mExpireTime <= aChecker.mNow); } GetInstanceot::Srp::AdvertisingProxy::AdvInfo255 Instance &GetInstance(void) const { return mHost.GetInstance(); } 256 257 AdvInfo *mNext; 258 AdvInfo *mBlockingAdv; 259 Host &mHost; 260 TimeMilli mExpireTime; 261 Server::MessageMetadata mMessageMetadata; 262 Ip6::MessageInfo mMessageInfo; 263 Error mError; 264 }; 265 266 template <typename Entry> void UpdateAdvIdRangeOn(Entry &aEntry); 267 template <typename Entry> bool IsRegisteredOrRegistering(const Entry &aEntry) const; 268 template <typename Entry> bool IsKeyRegisteredOrRegistering(const Entry &aEntry) const; 269 template <typename Entry> void DecideToAdvertise(Entry &aEntry, bool aUnregisterEntry, bool aUnregisterKey); 270 template <typename Entry> void UpdateKeyRegistrationStatus(Entry &aEntry, const Entry &aExistingEntry); 271 template <typename Entry> bool CompareAndUpdate(Entry &aEntry, Entry &aExistingEntry); 272 template <typename Entry> bool EntriesMatch(const Entry &aFirstEntry, const Entry &aSecondEntry); 273 274 void Start(void); 275 void Stop(void); 276 void UpdateState(void); 277 RequestId AllocateNextRequestId(void); 278 void Advertise(Host &aHost); 279 void UnregisterHostAndItsServicesAndKeys(Host &aHost); 280 bool CompareAndUpdateHostAndServices(Host &aHost, Host &aExistingHost); 281 bool CompareAndUpdateHost(Host &aHost, Host &aExistingHost); 282 bool CompareAndUpdateService(Service &aService, Service &aExistingService); 283 void RegisterHost(Host &aHost); 284 void UnregisterHost(Host &aHost); 285 void RegisterService(Service &aService); 286 void UnregisterService(Service &aService); 287 void RegisterKey(Host &aHost); 288 void RegisterKey(Service &aService); 289 void RegisterKey(const char *aName, 290 const char *aServiceType, 291 const Host::Key &aKey, 292 RequestId aRequestId, 293 uint32_t aTtl); 294 void UnregisterKey(Service &aService); 295 void UnregisterKey(Host &aHost); 296 void UnregisterKey(const char *aName, const char *aServiceType); 297 void CopyNameAndRemoveDomain(DnsName &aName, const char *aFullName); 298 static void HandleRegistered(otInstance *aInstance, otPlatDnssdRequestId aRequestId, otError aError); 299 void HandleRegistered(RequestId aRequestId, Error aError); 300 bool HandleRegisteredRequestIdOn(Host &aHost, RequestId aRequestId, Error aError); 301 void HandleTimer(void); 302 void HandleTasklet(void); 303 void SignalAdvCompleted(AdvInfo &aAdvInfo); 304 305 using AdvTimer = TimerMilliIn<AdvertisingProxy, &AdvertisingProxy::HandleTimer>; 306 using AdvTasklet = TaskletIn<AdvertisingProxy, &AdvertisingProxy::HandleTasklet>; 307 308 State mState; 309 RequestId mCurrentRequestId; 310 uint32_t mAdvTimeout; 311 OwningList<AdvInfo> mAdvInfoList; 312 AdvTimer mTimer; 313 AdvTasklet mTasklet; 314 Counters mCounters; 315 }; 316 317 } // namespace Srp 318 } // namespace ot 319 320 #endif // OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE 321 322 #endif // SRP_ADVERTISING_PROXY_HPP_ 323