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