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 implementation of SRP Advertising Proxy.
32  */
33 
34 #include "srp_advertising_proxy.hpp"
35 
36 #if OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE
37 
38 #include "common/as_core_type.hpp"
39 #include "common/debug.hpp"
40 #include "common/locator_getters.hpp"
41 #include "common/log.hpp"
42 #include "common/serial_number.hpp"
43 #include "common/type_traits.hpp"
44 #include "instance/instance.hpp"
45 
46 namespace ot {
47 namespace Srp {
48 
49 RegisterLogModule("SrpAdvProxy");
50 
51 //---------------------------------------------------------------------------------------------------------------------
52 // AdvertisingProxy
53 
AdvertisingProxy(Instance & aInstance)54 AdvertisingProxy::AdvertisingProxy(Instance &aInstance)
55     : InstanceLocator(aInstance)
56     , mState(kStateStopped)
57     , mCurrentRequestId(0)
58     , mAdvTimeout(kAdvTimeout)
59     , mTimer(aInstance)
60     , mTasklet(aInstance)
61 {
62     mCounters.Clear();
63 }
64 
Start(void)65 void AdvertisingProxy::Start(void)
66 {
67     VerifyOrExit(mState != kStateRunning);
68 
69     mState = kStateRunning;
70     mCounters.mStateChanges++;
71     LogInfo("Started");
72 
73     // Advertise all existing and committed entries on SRP sever.
74 
75     for (Host &host : Get<Server>().mHosts)
76     {
77         LogInfo("Adv existing host '%s'", host.GetFullName());
78         Advertise(host);
79     }
80 
81 exit:
82     return;
83 }
84 
Stop(void)85 void AdvertisingProxy::Stop(void)
86 {
87     VerifyOrExit(mState != kStateStopped);
88 
89     mState = kStateStopped;
90     mCounters.mStateChanges++;
91 
92     while (true)
93     {
94         OwnedPtr<AdvInfo> advPtr = mAdvInfoList.Pop();
95 
96         if (advPtr.IsNull())
97         {
98             break;
99         }
100 
101         mCounters.mAdvRejected++;
102 
103         UnregisterHostAndItsServicesAndKeys(advPtr->mHost);
104 
105         advPtr->mError = kErrorAbort;
106         advPtr->mHost.mAdvIdRange.Clear();
107         advPtr->mBlockingAdv = nullptr;
108         advPtr->SignalServerToCommit();
109     }
110 
111     for (Host &host : Get<Server>().GetHosts())
112     {
113         UnregisterHostAndItsServicesAndKeys(host);
114 
115         host.mAdvIdRange.Clear();
116         host.mAdvId        = kInvalidRequestId;
117         host.mIsRegistered = false;
118 
119         for (Service &service : host.mServices)
120         {
121             service.mAdvId        = kInvalidRequestId;
122             service.mIsRegistered = false;
123         }
124     }
125 
126     LogInfo("Stopped");
127 
128 exit:
129     return;
130 }
131 
UpdateState(void)132 void AdvertisingProxy::UpdateState(void)
133 {
134     if (!Get<Dnssd>().IsReady() || !Get<BorderRouter::InfraIf>().IsRunning())
135     {
136         Stop();
137         ExitNow();
138     }
139 
140     switch (Get<Server>().GetState())
141     {
142     case Server::kStateDisabled:
143     case Server::kStateStopped:
144         Stop();
145         break;
146 
147     case Server::kStateRunning:
148         Start();
149         break;
150     }
151 
152 exit:
153     return;
154 }
155 
AllocateNextRequestId(void)156 AdvertisingProxy::RequestId AdvertisingProxy::AllocateNextRequestId(void)
157 {
158     mCurrentRequestId++;
159 
160     if (kInvalidRequestId == mCurrentRequestId)
161     {
162         mCurrentRequestId++;
163     }
164 
165     return mCurrentRequestId;
166 }
167 
168 // NOLINTNEXTLINE(readability-inconsistent-declaration-parameter-name)
UpdateAdvIdRangeOn(Host & aHost)169 template <> void AdvertisingProxy::UpdateAdvIdRangeOn(Host &aHost)
170 {
171     // Determine and update `mAdvIdRange` on `aHost` based on
172     // `mAdvId` and `mKeyAdvId` of host and its services.
173 
174     aHost.mAdvIdRange.Clear();
175 
176     for (const Service &service : aHost.mServices)
177     {
178         if (service.mKeyAdvId != kInvalidRequestId)
179         {
180             aHost.mAdvIdRange.Add(service.mKeyAdvId);
181         }
182 
183         if (service.mAdvId != kInvalidRequestId)
184         {
185             aHost.mAdvIdRange.Add(service.mAdvId);
186         }
187     }
188 
189     if (aHost.mKeyAdvId != kInvalidRequestId)
190     {
191         aHost.mAdvIdRange.Add(aHost.mKeyAdvId);
192     }
193 
194     if (aHost.mAdvId != kInvalidRequestId)
195     {
196         aHost.mAdvIdRange.Add(aHost.mAdvId);
197     }
198 
199     if (aHost.mAdvIdRange.IsEmpty())
200     {
201         mTasklet.Post();
202     }
203 }
204 
205 // NOLINTNEXTLINE(readability-inconsistent-declaration-parameter-name)
UpdateAdvIdRangeOn(Service & aService)206 template <> void AdvertisingProxy::UpdateAdvIdRangeOn(Service &aService)
207 {
208     // Updates `mAdvIdRange` on the `Host` associated with
209     // `aService`.
210 
211     UpdateAdvIdRangeOn<Host>(*aService.mHost);
212 }
213 
AdvertiseRemovalOf(Host & aHost)214 void AdvertisingProxy::AdvertiseRemovalOf(Host &aHost)
215 {
216     LogInfo("Adv removal of host '%s'", aHost.GetFullName());
217     mCounters.mAdvHostRemovals++;
218 
219     VerifyOrExit(mState == kStateRunning);
220     VerifyOrExit(aHost.IsDeleted());
221 
222     aHost.mShouldAdvertise = aHost.mIsRegistered;
223 
224     for (Service &service : aHost.mServices)
225     {
226         if (!service.mIsDeleted)
227         {
228             service.mIsDeleted = true;
229         }
230 
231         service.mShouldAdvertise = service.mIsRegistered;
232     }
233 
234     // Reject any outstanding `AdvInfo` that matches `aHost` that is
235     // being removed.
236 
237     for (AdvInfo &adv : mAdvInfoList)
238     {
239         Host &advHost = adv.mHost;
240 
241         if (!aHost.Matches(advHost.GetFullName()) || advHost.IsDeleted())
242         {
243             continue;
244         }
245 
246         for (Service &advService : advHost.mServices)
247         {
248             Service *service;
249 
250             service = aHost.FindService(advService.GetInstanceName());
251 
252             if (service == nullptr)
253             {
254                 // `AdvInfo` contains a service that is not present in
255                 // `aHost`, we unregister the service and its key.
256 
257                 if (!advService.IsDeleted())
258                 {
259                     UnregisterService(advService);
260                 }
261 
262                 UnregisterKey(advService);
263             }
264             else
265             {
266                 service->mShouldAdvertise = true;
267 
268                 if (aHost.mKeyLease == 0)
269                 {
270                     advService.mIsKeyRegistered = false;
271                 }
272             }
273 
274             advService.mAdvId      = kInvalidRequestId;
275             advService.mKeyAdvId   = kInvalidRequestId;
276             advService.mIsReplaced = true;
277         }
278 
279         if (aHost.mKeyLease == 0)
280         {
281             advHost.mIsKeyRegistered = false;
282         }
283 
284         advHost.mAdvId      = kInvalidRequestId;
285         advHost.mKeyAdvId   = kInvalidRequestId;
286         advHost.mIsReplaced = true;
287         advHost.mAdvIdRange.Clear();
288 
289         adv.mError = kErrorAbort;
290         mTasklet.Post();
291     }
292 
293     for (Service &service : aHost.mServices)
294     {
295         if (service.mShouldAdvertise)
296         {
297             UnregisterService(service);
298         }
299 
300         if (aHost.mKeyLease == 0)
301         {
302             UnregisterKey(service);
303         }
304     }
305 
306     if (aHost.mShouldAdvertise)
307     {
308         UnregisterHost(aHost);
309     }
310 
311     if (aHost.mKeyLease == 0)
312     {
313         UnregisterKey(aHost);
314     }
315 
316 exit:
317     return;
318 }
319 
AdvertiseRemovalOf(Service & aService)320 void AdvertisingProxy::AdvertiseRemovalOf(Service &aService)
321 {
322     LogInfo("Adv removal of service '%s' '%s'", aService.GetInstanceLabel(), aService.GetServiceName());
323     mCounters.mAdvServiceRemovals++;
324 
325     VerifyOrExit(mState == kStateRunning);
326 
327     aService.mShouldAdvertise = aService.mIsRegistered;
328 
329     // Check if any outstanding `AdvInfo` is re-adding the `aService`
330     // (which is being removed), and if so, skip unregistering the
331     // service and its key.
332 
333     for (const AdvInfo &adv : mAdvInfoList)
334     {
335         const Host    &advHost = adv.mHost;
336         const Service *advService;
337 
338         if (!aService.mHost->Matches(advHost.GetFullName()))
339         {
340             continue;
341         }
342 
343         if (advHost.IsDeleted())
344         {
345             break;
346         }
347 
348         advService = advHost.FindService(aService.GetInstanceName());
349 
350         if ((advService != nullptr) && !advService->IsDeleted())
351         {
352             ExitNow();
353         }
354     }
355 
356     if (aService.mShouldAdvertise)
357     {
358         UnregisterService(aService);
359     }
360 
361     if (aService.mKeyLease == 0)
362     {
363         UnregisterKey(aService);
364     }
365 
366 exit:
367     return;
368 }
369 
Advertise(Host & aHost,const Server::MessageMetadata & aMetadata)370 void AdvertisingProxy::Advertise(Host &aHost, const Server::MessageMetadata &aMetadata)
371 {
372     AdvInfo *advPtr = nullptr;
373     Host    *existingHost;
374 
375     LogInfo("Adv update for '%s'", aHost.GetFullName());
376 
377     mCounters.mAdvTotal++;
378 
379     VerifyOrExit(mState == kStateRunning);
380 
381     advPtr = AdvInfo::Allocate(aHost, aMetadata, mAdvTimeout);
382     VerifyOrExit(advPtr != nullptr);
383     mAdvInfoList.Push(*advPtr);
384 
385     // Compare the new `aHost` with outstanding advertisements and
386     // already committed entries on server.
387 
388     for (AdvInfo &adv : mAdvInfoList)
389     {
390         if (!aHost.Matches(adv.mHost.GetFullName()))
391         {
392             continue;
393         }
394 
395         if (CompareAndUpdateHostAndServices(aHost, adv.mHost))
396         {
397             // If the new `aHost` replaces an entry in the outstanding
398             // `adv`, we mark the new advertisement as blocked so
399             // that it is not committed before the earlier one. This
400             // ensures that SRP Updates are committed in the order
401             // they are advertised, avoiding issues such as re-adding
402             // a removed entry due to a delay in registration on
403             // infra DNS-SD.
404 
405             if (advPtr->mBlockingAdv == nullptr)
406             {
407                 mCounters.mAdvReplaced++;
408                 advPtr->mBlockingAdv = &adv;
409             }
410         }
411     }
412 
413     existingHost = Get<Server>().mHosts.FindMatching(aHost.GetFullName());
414 
415     if (existingHost != nullptr)
416     {
417         CompareAndUpdateHostAndServices(aHost, *existingHost);
418     }
419 
420     Advertise(aHost);
421 
422 exit:
423     if (advPtr != nullptr)
424     {
425         if (advPtr->IsCompleted())
426         {
427             mTasklet.Post();
428         }
429         else
430         {
431             mTimer.FireAtIfEarlier(advPtr->mExpireTime);
432         }
433     }
434     else
435     {
436         LogInfo("Adv skipped '%s'", aHost.GetFullName());
437         mCounters.mAdvSkipped++;
438         Get<Server>().CommitSrpUpdate(kErrorNone, aHost, aMetadata);
439     }
440 }
441 
IsKeyRegisteredOrRegistering(const Entry & aEntry) const442 template <typename Entry> bool AdvertisingProxy::IsKeyRegisteredOrRegistering(const Entry &aEntry) const
443 {
444     return (aEntry.mIsKeyRegistered || (aEntry.mKeyAdvId != kInvalidRequestId));
445 }
446 
IsRegisteredOrRegistering(const Entry & aEntry) const447 template <typename Entry> bool AdvertisingProxy::IsRegisteredOrRegistering(const Entry &aEntry) const
448 {
449     return (aEntry.mIsRegistered || (aEntry.mAdvId != kInvalidRequestId));
450 }
451 
452 template <typename Entry>
DecideToAdvertise(Entry & aEntry,bool aUnregisterEntry,bool aUnregisterKey)453 void AdvertisingProxy::DecideToAdvertise(Entry &aEntry, bool aUnregisterEntry, bool aUnregisterKey)
454 {
455     // Decides whether to advertise `aEntry` or register its key.
456 
457     if (!aUnregisterKey && !IsKeyRegisteredOrRegistering(aEntry))
458     {
459         aEntry.mShouldRegisterKey = true;
460         aEntry.mKeyAdvId          = AllocateNextRequestId();
461     }
462 
463     VerifyOrExit(!aEntry.mShouldAdvertise);
464 
465     if (aUnregisterEntry || aEntry.IsDeleted())
466     {
467         aEntry.mShouldAdvertise = aEntry.mIsRegistered;
468     }
469     else if (!IsRegisteredOrRegistering(aEntry))
470     {
471         aEntry.mShouldAdvertise = true;
472         aEntry.mAdvId           = AllocateNextRequestId();
473     }
474 
475 exit:
476     return;
477 }
478 
Advertise(Host & aHost)479 void AdvertisingProxy::Advertise(Host &aHost)
480 {
481     bool shouldUnregisterHostAndServices = aHost.IsDeleted();
482     bool shouldUnregisterKeys            = (aHost.mKeyLease == 0);
483 
484     DecideToAdvertise(aHost, shouldUnregisterHostAndServices, shouldUnregisterKeys);
485 
486     for (Service &service : aHost.mServices)
487     {
488         DecideToAdvertise(service, shouldUnregisterHostAndServices, shouldUnregisterKeys);
489     }
490 
491     // We call `UpdateAdvIdRangeOn()` to determine the `mAdvIdRange`
492     // on `aHost` before we call any of `UnregisterHost()`,
493     // `UnregisterService()`, or `UnregisterKey()` methods, and
494     // and receive any `HandleRegistered()` callbacks. The DNS-SD
495     // platform may invoke `HandleRegistered()` callbacks from within
496     // the `Register{Host/Service/Key}()` calls.
497 
498     UpdateAdvIdRangeOn(aHost);
499 
500     if (shouldUnregisterKeys)
501     {
502         UnregisterKey(aHost);
503     }
504     else if (aHost.mShouldRegisterKey)
505     {
506         RegisterKey(aHost);
507     }
508 
509     // We register host first before any of its services.
510     // But if we need to unregister host, it is done after
511     // all services.
512 
513     if (aHost.mShouldAdvertise && !shouldUnregisterHostAndServices)
514     {
515         RegisterHost(aHost);
516     }
517 
518     for (Service &service : aHost.mServices)
519     {
520         if (shouldUnregisterKeys)
521         {
522             UnregisterKey(service);
523         }
524         else if (service.mShouldRegisterKey)
525         {
526             RegisterKey(service);
527         }
528 
529         if (service.mShouldAdvertise)
530         {
531             if (shouldUnregisterHostAndServices || service.IsDeleted())
532             {
533                 UnregisterService(service);
534             }
535             else
536             {
537                 RegisterService(service);
538             }
539         }
540     }
541 
542     if (aHost.mShouldAdvertise && shouldUnregisterHostAndServices)
543     {
544         UnregisterHost(aHost);
545     }
546 }
547 
UnregisterHostAndItsServicesAndKeys(Host & aHost)548 void AdvertisingProxy::UnregisterHostAndItsServicesAndKeys(Host &aHost)
549 {
550     for (Service &service : aHost.mServices)
551     {
552         if (service.mIsKeyRegistered)
553         {
554             UnregisterKey(service);
555         }
556 
557         if (!service.mIsReplaced && IsRegisteredOrRegistering(service))
558         {
559             UnregisterService(service);
560         }
561     }
562 
563     if (aHost.mIsKeyRegistered)
564     {
565         UnregisterKey(aHost);
566     }
567 
568     if (!aHost.mIsReplaced && IsRegisteredOrRegistering(aHost))
569     {
570         UnregisterHost(aHost);
571     }
572 }
573 
CompareAndUpdateHostAndServices(Host & aHost,Host & aExistingHost)574 bool AdvertisingProxy::CompareAndUpdateHostAndServices(Host &aHost, Host &aExistingHost)
575 {
576     // This method compares and updates flags used by `AdvertisingProxy`
577     // on new `aHost` and `aExistingHost` and their services.
578     //
579     // It returns a boolean indicating whether the new `aHost` replaced
580     // any of the entries on the `aExistingHost`.
581     //
582     // The `AdvertisingProxy` uses the following flags and variables
583     // on `Host` and `Service` entries:
584     //
585     // - `mIsRegistered` indicates whether or not the entry has been
586     //   successfully registered by the proxy.
587     //
588     // - `mIsKeyRegistered` indicates whether or not a key record
589     //   associated with the entry name has been successfully
590     //   registered by the proxy on infrastructure DNS-SD.
591     //
592     // - `mAdvId` specifies the ongoing registration request ID
593     //   associated with this entry by the proxy. A value of zero or
594     //   `kInvalidRequestId` indicates that there is no ongoing
595     //   registration for this entry.
596     //
597     // - `mKeyAdvId` is similar to `mAdvId` but for registering the
598     //   key record.
599     //
600     // - `mIsReplaced` tracks whether this entry has been replaced by
601     //   a newer advertisement request that changes some of its
602     //   parameters. For example, the address list could have been
603     //   changed on a `Host`, or TXT Data, or the list of sub-types,
604     //   or port number could have been changed on a `Service`.
605     //
606     // - `mShouldAdvertise` is only used in the `Advertise()` call
607     //   chain to track whether we need to advertise the entry.
608     //
609     // - `mShouldRegisterKey` is similar to `mShouldAdvertise` and
610     //   only used in `Advertise()` call chain.
611 
612     bool replaced = false;
613 
614     VerifyOrExit(&aHost != &aExistingHost);
615 
616     replaced = CompareAndUpdateHost(aHost, aExistingHost);
617 
618     // Compare services of `aHost` against services of
619     // `aExistingHost`.
620 
621     for (Service &service : aHost.mServices)
622     {
623         Service *existingService = aExistingHost.mServices.FindMatching(service.GetInstanceName());
624 
625         if (existingService != nullptr)
626         {
627             replaced |= CompareAndUpdateService(service, *existingService);
628         }
629     }
630 
631 exit:
632     return replaced;
633 }
634 
UpdateKeyRegistrationStatus(Entry & aEntry,const Entry & aExistingEntry)635 template <typename Entry> void AdvertisingProxy::UpdateKeyRegistrationStatus(Entry &aEntry, const Entry &aExistingEntry)
636 {
637     // Updates key registration status on `aEntry` based
638     // on its state on `aExistingEntry`.
639 
640     static_assert(TypeTraits::IsSame<Entry, Host>::kValue || TypeTraits::IsSame<Entry, Service>::kValue,
641                   "`Entry` must be `Host` or `Service` types");
642 
643     // If the new `aEntry` has a zero key lease, we always unregister
644     // it, just to be safe. Therefore, there is no need to check the
645     // key registration status of the existing `aExistingEntry`.
646 
647     VerifyOrExit(aEntry.GetKeyLease() != 0);
648 
649     VerifyOrExit(!IsKeyRegisteredOrRegistering(aEntry));
650 
651     if (aExistingEntry.mIsKeyRegistered)
652     {
653         aEntry.mIsKeyRegistered = true;
654     }
655     else
656     {
657         // Use the key registration request ID by `aExistingEntry` for
658         // the new `aEntry` if there is any. If there is none the
659         // `mKeyAdvId` remains as `kInvalidRequestId`.
660 
661         aEntry.mKeyAdvId = aExistingEntry.mKeyAdvId;
662     }
663 
664 exit:
665     return;
666 }
667 
668 // NOLINTNEXTLINE(readability-inconsistent-declaration-parameter-name)
EntriesMatch(const Host & aFirstHost,const Host & aSecondHost)669 template <> bool AdvertisingProxy::EntriesMatch(const Host &aFirstHost, const Host &aSecondHost)
670 {
671     bool match = false;
672 
673     VerifyOrExit(aFirstHost.IsDeleted() == aSecondHost.IsDeleted());
674 
675     if (aFirstHost.IsDeleted())
676     {
677         match = true;
678         ExitNow();
679     }
680 
681     VerifyOrExit(aFirstHost.mAddresses.GetLength() == aSecondHost.mAddresses.GetLength());
682 
683     for (const Ip6::Address &address : aFirstHost.mAddresses)
684     {
685         VerifyOrExit(aSecondHost.mAddresses.Contains(address));
686     }
687 
688     match = true;
689 
690 exit:
691     return match;
692 }
693 
694 // NOLINTNEXTLINE(readability-inconsistent-declaration-parameter-name)
EntriesMatch(const Service & aFirstService,const Service & aSecondService)695 template <> bool AdvertisingProxy::EntriesMatch(const Service &aFirstService, const Service &aSecondService)
696 {
697     bool match = false;
698 
699     VerifyOrExit(aFirstService.IsDeleted() == aSecondService.IsDeleted());
700 
701     if (aFirstService.IsDeleted())
702     {
703         match = true;
704         ExitNow();
705     }
706 
707     VerifyOrExit(aFirstService.GetPort() == aSecondService.GetPort());
708     VerifyOrExit(aFirstService.GetWeight() == aSecondService.GetWeight());
709     VerifyOrExit(aFirstService.GetPriority() == aSecondService.GetPriority());
710     VerifyOrExit(aFirstService.GetTtl() == aSecondService.GetTtl());
711 
712     VerifyOrExit(aFirstService.GetNumberOfSubTypes() == aSecondService.GetNumberOfSubTypes());
713 
714     for (uint16_t index = 0; index < aFirstService.GetNumberOfSubTypes(); index++)
715     {
716         VerifyOrExit(aSecondService.HasSubTypeServiceName(aFirstService.GetSubTypeServiceNameAt(index)));
717     }
718 
719     VerifyOrExit(aFirstService.GetTxtDataLength() == aSecondService.GetTxtDataLength());
720     VerifyOrExit(!memcmp(aFirstService.GetTxtData(), aSecondService.GetTxtData(), aFirstService.GetTxtDataLength()));
721 
722     match = true;
723 
724 exit:
725     return match;
726 }
727 
CompareAndUpdate(Entry & aEntry,Entry & aExistingEntry)728 template <typename Entry> bool AdvertisingProxy::CompareAndUpdate(Entry &aEntry, Entry &aExistingEntry)
729 {
730     // This is called when the new `aEntry` is not deleted.
731 
732     bool replaced = false;
733 
734     // If we previously determined that `aEntry` is registered,
735     // nothing else to do.
736 
737     VerifyOrExit(!aEntry.mIsRegistered);
738 
739     if (aEntry.mShouldAdvertise || aExistingEntry.mIsReplaced || !EntriesMatch(aEntry, aExistingEntry))
740     {
741         // If we previously determined that we should advertise the
742         // new `aEntry`, we enter this block to mark `aExistingEntry`
743         // as being replaced.
744         //
745         // If `aExistingEntry` was already marked as replaced, we
746         // cannot compare it to the new `aEntry`. Therefore, we assume
747         // that there may be a change and always advertise the new
748         // `aEntry`. Otherwise, we compare it to the new `aEntry` using
749         // `EntriesMatch()` and only if there are any differences, we
750         // mark that `aEntry` needs to be advertised.
751 
752         aExistingEntry.mIsReplaced = true;
753         replaced                   = true;
754 
755         if (aEntry.mAdvId == kInvalidRequestId)
756         {
757             aEntry.mShouldAdvertise = true;
758             aEntry.mAdvId           = AllocateNextRequestId();
759         }
760 
761         // If there is an outstanding registration request for
762         // `aExistingEntry` we replace it with the request ID of the
763         // new `aEntry` registration.
764 
765         if (aExistingEntry.mAdvId != kInvalidRequestId)
766         {
767             aExistingEntry.mAdvId = aEntry.mAdvId;
768             UpdateAdvIdRangeOn(aExistingEntry);
769         }
770 
771         ExitNow();
772     }
773 
774     // `aEntry` fully matches `aExistingEntry` and `aExistingEntry` was
775     // not replaced.
776 
777     VerifyOrExit(aEntry.mAdvId == kInvalidRequestId);
778 
779     if (aExistingEntry.mIsRegistered)
780     {
781         aEntry.mIsRegistered = true;
782     }
783     else if (aExistingEntry.mAdvId != kInvalidRequestId)
784     {
785         // There is an outstanding registration request for
786         // `aExistingEntry`. We use the same ID for the new `aEntry`.
787 
788         aEntry.mAdvId = aExistingEntry.mAdvId;
789     }
790     else
791     {
792         // The earlier advertisement of `aExistingEntry` seems to have
793         // failed since there is no outstanding registration request
794         // (no ID) and it is not marked as registered. We mark the
795         // new `aEntry` to be advertised (to try again).
796 
797         aEntry.mShouldAdvertise    = true;
798         aEntry.mAdvId              = AllocateNextRequestId();
799         aExistingEntry.mIsReplaced = true;
800     }
801 
802 exit:
803     return replaced;
804 }
805 
CompareAndUpdateHost(Host & aHost,Host & aExistingHost)806 bool AdvertisingProxy::CompareAndUpdateHost(Host &aHost, Host &aExistingHost)
807 {
808     bool replaced = false;
809 
810     UpdateKeyRegistrationStatus(aHost, aExistingHost);
811 
812     if (!aHost.IsDeleted())
813     {
814         replaced = CompareAndUpdate(aHost, aExistingHost);
815         ExitNow();
816     }
817 
818     // The new `aHost` is removing the host and all its services.
819 
820     if (aExistingHost.IsDeleted())
821     {
822         // If `aHost` has zero key-lease (fully removed),
823         // we need to unregister keys for any services on
824         // existing host that are not present in `aHost`.
825 
826         if (aHost.mKeyLease == 0)
827         {
828             for (Service &existingService : aExistingHost.mServices)
829             {
830                 if (!aHost.HasService(existingService.GetInstanceName()))
831                 {
832                     UnregisterKey(existingService);
833                 }
834             }
835         }
836 
837         ExitNow();
838     }
839 
840     // `aExistingHost` is updating the same host that is being
841     // removed by the new `aHost` update. We need to advertise
842     // the new `aHost` to make sure it is unregistered.
843 
844     aHost.mShouldAdvertise = true;
845 
846     // We unregister any services that were registered by
847     // `aExistingHost` but are not included in the now being
848     // removed `aHost`, and unregister any registered keys when
849     // `aHost` has zero key lease.
850 
851     for (Service &existingService : aExistingHost.mServices)
852     {
853         if (existingService.IsDeleted())
854         {
855             if (aHost.GetKeyLease() == 0)
856             {
857                 existingService.mIsReplaced = true;
858                 UnregisterKey(existingService);
859             }
860 
861             continue;
862         }
863 
864         if (aHost.HasService(existingService.GetInstanceName()))
865         {
866             // The `existingService` that are contained in `aHost`
867             // are updated in `CompareAndUpdateService()`.
868             continue;
869         }
870 
871         UnregisterService(existingService);
872 
873         existingService.mIsReplaced = true;
874 
875         if (aHost.GetKeyLease() == 0)
876         {
877             UnregisterKey(existingService);
878         }
879     }
880 
881     aExistingHost.mAdvId      = kInvalidRequestId;
882     aExistingHost.mIsReplaced = true;
883     replaced                  = true;
884 
885     if (aHost.GetKeyLease() == 0)
886     {
887         UnregisterKey(aExistingHost);
888     }
889 
890     UpdateAdvIdRangeOn(aExistingHost);
891 
892 exit:
893     return replaced;
894 }
895 
CompareAndUpdateService(Service & aService,Service & aExistingService)896 bool AdvertisingProxy::CompareAndUpdateService(Service &aService, Service &aExistingService)
897 {
898     bool replaced = false;
899 
900     UpdateKeyRegistrationStatus(aService, aExistingService);
901 
902     if (!aService.IsDeleted())
903     {
904         replaced = CompareAndUpdate(aService, aExistingService);
905         ExitNow();
906     }
907 
908     if (aExistingService.IsDeleted())
909     {
910         ExitNow();
911     }
912 
913     aService.mShouldAdvertise = true;
914 
915     aExistingService.mIsReplaced = true;
916     replaced                     = true;
917 
918     if (aExistingService.mAdvId != kInvalidRequestId)
919     {
920         // If there is an outstanding registration request for the
921         // existing service, clear its request ID.
922 
923         aExistingService.mAdvId = kInvalidRequestId;
924 
925         UpdateAdvIdRangeOn(*aExistingService.mHost);
926     }
927 
928 exit:
929     return replaced;
930 }
931 
RegisterHost(Host & aHost)932 void AdvertisingProxy::RegisterHost(Host &aHost)
933 {
934     Error                     error = kErrorNone;
935     Dnssd::Host               hostInfo;
936     DnsName                   hostName;
937     Heap::Array<Ip6::Address> hostAddresses;
938 
939     aHost.mShouldAdvertise = false;
940 
941     CopyNameAndRemoveDomain(hostName, aHost.GetFullName());
942 
943     SuccessOrExit(error = hostAddresses.ReserveCapacity(aHost.mAddresses.GetLength()));
944 
945     for (const Ip6::Address &address : aHost.mAddresses)
946     {
947         if (!address.IsLinkLocalUnicast() && !Get<Mle::Mle>().IsMeshLocalAddress(address))
948         {
949             IgnoreError(hostAddresses.PushBack(address));
950         }
951     }
952 
953     LogInfo("Registering host '%s', id:%lu", hostName, ToUlong(aHost.mAdvId));
954 
955     hostInfo.Clear();
956     hostInfo.mHostName        = hostName;
957     hostInfo.mAddresses       = hostAddresses.AsCArray();
958     hostInfo.mAddressesLength = hostAddresses.GetLength();
959     hostInfo.mTtl             = aHost.GetTtl();
960     hostInfo.mInfraIfIndex    = Get<BorderRouter::InfraIf>().GetIfIndex();
961 
962     Get<Dnssd>().RegisterHost(hostInfo, aHost.mAdvId, HandleRegistered);
963 
964 exit:
965     if (error != kErrorNone)
966     {
967         LogWarn("Error %s registering host '%s'", ErrorToString(error), hostName);
968     }
969 }
970 
UnregisterHost(Host & aHost)971 void AdvertisingProxy::UnregisterHost(Host &aHost)
972 {
973     Dnssd::Host hostInfo;
974     DnsName     hostName;
975 
976     aHost.mShouldAdvertise = false;
977     aHost.mIsRegistered    = false;
978     aHost.mAdvId           = false;
979 
980     CopyNameAndRemoveDomain(hostName, aHost.GetFullName());
981 
982     LogInfo("Unregistering host '%s'", hostName);
983 
984     hostInfo.Clear();
985     hostInfo.mHostName     = hostName;
986     hostInfo.mInfraIfIndex = Get<BorderRouter::InfraIf>().GetIfIndex();
987 
988     Get<Dnssd>().UnregisterHost(hostInfo, 0, nullptr);
989 }
990 
RegisterService(Service & aService)991 void AdvertisingProxy::RegisterService(Service &aService)
992 {
993     Error                     error = kErrorNone;
994     Dnssd::Service            serviceInfo;
995     DnsName                   hostName;
996     DnsName                   serviceName;
997     Heap::Array<Heap::String> subTypeHeapStrings;
998     Heap::Array<const char *> subTypeLabels;
999 
1000     aService.mShouldAdvertise = false;
1001 
1002     CopyNameAndRemoveDomain(hostName, aService.GetHost().GetFullName());
1003     CopyNameAndRemoveDomain(serviceName, aService.GetServiceName());
1004 
1005     SuccessOrExit(error = subTypeHeapStrings.ReserveCapacity(aService.mSubTypes.GetLength()));
1006     SuccessOrExit(error = subTypeLabels.ReserveCapacity(aService.mSubTypes.GetLength()));
1007 
1008     for (const Heap::String &subTypeName : aService.mSubTypes)
1009     {
1010         char         label[Dns::Name::kMaxLabelSize];
1011         Heap::String labelString;
1012 
1013         IgnoreError(Server::Service::ParseSubTypeServiceName(subTypeName.AsCString(), label, sizeof(label)));
1014         SuccessOrExit(error = labelString.Set(label));
1015         IgnoreError(subTypeHeapStrings.PushBack(static_cast<Heap::String &&>(labelString)));
1016         IgnoreError(subTypeLabels.PushBack(subTypeHeapStrings.Back()->AsCString()));
1017     }
1018 
1019     LogInfo("Registering service '%s' '%s' on '%s', id:%lu", aService.GetInstanceLabel(), serviceName, hostName,
1020             ToUlong(aService.mAdvId));
1021 
1022     serviceInfo.Clear();
1023     serviceInfo.mHostName            = hostName;
1024     serviceInfo.mServiceInstance     = aService.GetInstanceLabel();
1025     serviceInfo.mServiceType         = serviceName;
1026     serviceInfo.mSubTypeLabels       = subTypeLabels.AsCArray();
1027     serviceInfo.mSubTypeLabelsLength = subTypeLabels.GetLength();
1028     serviceInfo.mTxtData             = aService.GetTxtData();
1029     serviceInfo.mTxtDataLength       = aService.GetTxtDataLength();
1030     serviceInfo.mPort                = aService.GetPort();
1031     serviceInfo.mWeight              = aService.GetWeight();
1032     serviceInfo.mPriority            = aService.GetPriority();
1033     serviceInfo.mTtl                 = aService.GetTtl();
1034     serviceInfo.mInfraIfIndex        = Get<BorderRouter::InfraIf>().GetIfIndex();
1035 
1036     Get<Dnssd>().RegisterService(serviceInfo, aService.mAdvId, HandleRegistered);
1037 
1038 exit:
1039     if (error != kErrorNone)
1040     {
1041         LogWarn("Error %s registering service '%s' '%s'", ErrorToString(error), aService.GetInstanceLabel(),
1042                 serviceName);
1043     }
1044 }
1045 
UnregisterService(Service & aService)1046 void AdvertisingProxy::UnregisterService(Service &aService)
1047 {
1048     Dnssd::Service serviceInfo;
1049     DnsName        hostName;
1050     DnsName        serviceName;
1051 
1052     aService.mShouldAdvertise = false;
1053     aService.mIsRegistered    = false;
1054     aService.mAdvId           = kInvalidRequestId;
1055 
1056     CopyNameAndRemoveDomain(hostName, aService.GetHost().GetFullName());
1057     CopyNameAndRemoveDomain(serviceName, aService.GetServiceName());
1058 
1059     LogInfo("Unregistering service '%s' '%s' on '%s'", aService.GetInstanceLabel(), serviceName, hostName);
1060 
1061     serviceInfo.Clear();
1062     serviceInfo.mHostName        = hostName;
1063     serviceInfo.mServiceInstance = aService.GetInstanceLabel();
1064     serviceInfo.mServiceType     = serviceName;
1065     serviceInfo.mInfraIfIndex    = Get<BorderRouter::InfraIf>().GetIfIndex();
1066 
1067     Get<Dnssd>().UnregisterService(serviceInfo, 0, nullptr);
1068 }
1069 
RegisterKey(Host & aHost)1070 void AdvertisingProxy::RegisterKey(Host &aHost)
1071 {
1072     DnsName hostName;
1073 
1074     aHost.mShouldRegisterKey = false;
1075 
1076     CopyNameAndRemoveDomain(hostName, aHost.GetFullName());
1077 
1078     LogInfo("Registering key for host '%s', id:%lu", hostName, ToUlong(aHost.mKeyAdvId));
1079 
1080     RegisterKey(hostName, /* aServiceType */ nullptr, aHost.mKey, aHost.mKeyAdvId, aHost.GetTtl());
1081 }
1082 
RegisterKey(Service & aService)1083 void AdvertisingProxy::RegisterKey(Service &aService)
1084 {
1085     DnsName serviceType;
1086 
1087     aService.mShouldRegisterKey = false;
1088 
1089     CopyNameAndRemoveDomain(serviceType, aService.GetServiceName());
1090 
1091     LogInfo("Registering key for service '%s' '%s', id:%lu", aService.GetInstanceLabel(), serviceType,
1092             ToUlong(aService.mKeyAdvId));
1093 
1094     RegisterKey(aService.GetInstanceLabel(), serviceType, aService.mHost->mKey, aService.mKeyAdvId, aService.GetTtl());
1095 }
1096 
RegisterKey(const char * aName,const char * aServiceType,const Host::Key & aKey,RequestId aRequestId,uint32_t aTtl)1097 void AdvertisingProxy::RegisterKey(const char      *aName,
1098                                    const char      *aServiceType,
1099                                    const Host::Key &aKey,
1100                                    RequestId        aRequestId,
1101                                    uint32_t         aTtl)
1102 {
1103     Dnssd::Key             keyInfo;
1104     Dns::Ecdsa256KeyRecord keyRecord;
1105 
1106     keyRecord.Init();
1107     keyRecord.SetFlags(Dns::KeyRecord::kAuthConfidPermitted, Dns::KeyRecord::kOwnerNonZone,
1108                        Dns::KeyRecord::kSignatoryFlagGeneral);
1109     keyRecord.SetProtocol(Dns::KeyRecord::kProtocolDnsSec);
1110     keyRecord.SetAlgorithm(Dns::KeyRecord::kAlgorithmEcdsaP256Sha256);
1111     keyRecord.SetLength(sizeof(Dns::Ecdsa256KeyRecord) - sizeof(Dns::ResourceRecord));
1112     keyRecord.SetKey(aKey);
1113 
1114     keyInfo.Clear();
1115     keyInfo.mName          = aName;
1116     keyInfo.mServiceType   = aServiceType;
1117     keyInfo.mKeyData       = reinterpret_cast<uint8_t *>(&keyRecord) + sizeof(Dns::ResourceRecord);
1118     keyInfo.mKeyDataLength = keyRecord.GetLength();
1119     keyInfo.mClass         = Dns::ResourceRecord::kClassInternet;
1120     keyInfo.mTtl           = aTtl;
1121     keyInfo.mInfraIfIndex  = Get<BorderRouter::InfraIf>().GetIfIndex();
1122 
1123     Get<Dnssd>().RegisterKey(keyInfo, aRequestId, HandleRegistered);
1124 }
1125 
UnregisterKey(Host & aHost)1126 void AdvertisingProxy::UnregisterKey(Host &aHost)
1127 {
1128     DnsName hostName;
1129 
1130     aHost.mIsKeyRegistered = false;
1131     aHost.mKeyAdvId        = kInvalidRequestId;
1132 
1133     CopyNameAndRemoveDomain(hostName, aHost.GetFullName());
1134 
1135     LogInfo("Unregistering key for host '%s'", hostName);
1136 
1137     UnregisterKey(hostName, /* aServiceType */ nullptr);
1138 }
1139 
UnregisterKey(Service & aService)1140 void AdvertisingProxy::UnregisterKey(Service &aService)
1141 {
1142     DnsName serviceType;
1143 
1144     aService.mIsKeyRegistered = false;
1145     aService.mKeyAdvId        = kInvalidRequestId;
1146 
1147     CopyNameAndRemoveDomain(serviceType, aService.GetServiceName());
1148 
1149     LogInfo("Unregistering key for service '%s' '%s'", aService.GetInstanceLabel(), serviceType);
1150 
1151     UnregisterKey(aService.GetInstanceLabel(), serviceType);
1152 }
1153 
UnregisterKey(const char * aName,const char * aServiceType)1154 void AdvertisingProxy::UnregisterKey(const char *aName, const char *aServiceType)
1155 {
1156     Dnssd::Key keyInfo;
1157 
1158     keyInfo.Clear();
1159     keyInfo.mName         = aName;
1160     keyInfo.mServiceType  = aServiceType;
1161     keyInfo.mInfraIfIndex = Get<BorderRouter::InfraIf>().GetIfIndex();
1162 
1163     Get<Dnssd>().UnregisterKey(keyInfo, 0, nullptr);
1164 }
1165 
CopyNameAndRemoveDomain(DnsName & aName,const char * aFullName)1166 void AdvertisingProxy::CopyNameAndRemoveDomain(DnsName &aName, const char *aFullName)
1167 {
1168     IgnoreError(Dns::Name::ExtractLabels(aFullName, Get<Server>().GetDomain(), aName, sizeof(aName)));
1169 }
1170 
HandleRegistered(otInstance * aInstance,otPlatDnssdRequestId aRequestId,otError aError)1171 void AdvertisingProxy::HandleRegistered(otInstance *aInstance, otPlatDnssdRequestId aRequestId, otError aError)
1172 {
1173     AsCoreType(aInstance).Get<AdvertisingProxy>().HandleRegistered(aRequestId, aError);
1174 }
1175 
HandleRegistered(RequestId aRequestId,Error aError)1176 void AdvertisingProxy::HandleRegistered(RequestId aRequestId, Error aError)
1177 {
1178     LogInfo("Register callback, id:%lu, error:%s", ToUlong(aRequestId), ErrorToString(aError));
1179 
1180     VerifyOrExit(mState == kStateRunning);
1181 
1182     for (Host &host : Get<Server>().mHosts)
1183     {
1184         HandleRegisteredRequestIdOn(host, aRequestId, aError);
1185     }
1186 
1187     for (AdvInfo &adv : mAdvInfoList)
1188     {
1189         if (HandleRegisteredRequestIdOn(adv.mHost, aRequestId, aError))
1190         {
1191             if (adv.mError == kErrorNone)
1192             {
1193                 adv.mError = aError;
1194             }
1195 
1196             if (adv.IsCompleted())
1197             {
1198                 mTasklet.Post();
1199             }
1200         }
1201     }
1202 
1203 exit:
1204     return;
1205 }
1206 
HandleRegisteredRequestIdOn(Host & aHost,RequestId aRequestId,Error aError)1207 bool AdvertisingProxy::HandleRegisteredRequestIdOn(Host &aHost, RequestId aRequestId, Error aError)
1208 {
1209     // Handles "registration request callback" for `aRequestId` on a
1210     // given `aHost`. Returns `true`, if the ID matched an entry
1211     // on `aHost` and `aHost` was updated, `false` otherwise.
1212 
1213     bool didUpdate = false;
1214 
1215     VerifyOrExit(aHost.mAdvIdRange.Contains(aRequestId));
1216 
1217     if (aHost.mAdvId == aRequestId)
1218     {
1219         aHost.mAdvId        = kInvalidRequestId;
1220         aHost.mIsRegistered = (aError == kErrorNone);
1221         didUpdate           = true;
1222     }
1223 
1224     if (aHost.mKeyAdvId == aRequestId)
1225     {
1226         aHost.mKeyAdvId        = kInvalidRequestId;
1227         aHost.mIsKeyRegistered = true;
1228         didUpdate              = true;
1229     }
1230 
1231     for (Service &service : aHost.mServices)
1232     {
1233         if (service.mAdvId == aRequestId)
1234         {
1235             service.mAdvId        = kInvalidRequestId;
1236             service.mIsRegistered = (aError == kErrorNone);
1237             didUpdate             = true;
1238         }
1239 
1240         if (service.mKeyAdvId == aRequestId)
1241         {
1242             service.mKeyAdvId        = kInvalidRequestId;
1243             service.mIsKeyRegistered = true;
1244             didUpdate                = true;
1245         }
1246     }
1247 
1248     UpdateAdvIdRangeOn(aHost);
1249 
1250 exit:
1251     return didUpdate;
1252 }
1253 
HandleTimer(void)1254 void AdvertisingProxy::HandleTimer(void)
1255 {
1256     NextFireTime        nextTime;
1257     OwningList<AdvInfo> expiredList;
1258 
1259     VerifyOrExit(mState == kStateRunning);
1260 
1261     mAdvInfoList.RemoveAllMatching(AdvInfo::ExpirationChecker(nextTime.GetNow()), expiredList);
1262 
1263     for (AdvInfo &adv : mAdvInfoList)
1264     {
1265         nextTime.UpdateIfEarlier(adv.mExpireTime);
1266     }
1267 
1268     mTimer.FireAtIfEarlier(nextTime);
1269 
1270     for (AdvInfo &adv : expiredList)
1271     {
1272         adv.mError       = kErrorResponseTimeout;
1273         adv.mBlockingAdv = nullptr;
1274         adv.mHost.mAdvIdRange.Clear();
1275         SignalAdvCompleted(adv);
1276     }
1277 
1278 exit:
1279     return;
1280 }
1281 
HandleTasklet(void)1282 void AdvertisingProxy::HandleTasklet(void)
1283 {
1284     VerifyOrExit(mState == kStateRunning);
1285 
1286     while (true)
1287     {
1288         OwningList<AdvInfo> completedList;
1289 
1290         mAdvInfoList.RemoveAllMatching(AdvInfo::CompletionChecker(), completedList);
1291 
1292         VerifyOrExit(!completedList.IsEmpty());
1293 
1294         // `RemoveAllMatching()` reverses the order of removed entries
1295         // from `mAdvInfoList` (which itself keeps the later requests
1296         // towards the head of the list). This means that the
1297         // `completedList` will be sorted from earliest request to
1298         // latest and this is the order that we want to notify
1299         // `Srp::Server`.
1300 
1301         for (AdvInfo &adv : completedList)
1302         {
1303             SignalAdvCompleted(adv);
1304         }
1305 
1306         completedList.Clear();
1307     }
1308 
1309 exit:
1310     return;
1311 }
1312 
SignalAdvCompleted(AdvInfo & aAdvInfo)1313 void AdvertisingProxy::SignalAdvCompleted(AdvInfo &aAdvInfo)
1314 {
1315     // Check if any outstanding advertisements in the list
1316     // is blocked by `aAdvInfo` and unblock.
1317 
1318     for (AdvInfo &adv : mAdvInfoList)
1319     {
1320         if (adv.mBlockingAdv == &aAdvInfo)
1321         {
1322             adv.mBlockingAdv = nullptr;
1323 
1324             if (adv.IsCompleted())
1325             {
1326                 mTasklet.Post();
1327             }
1328         }
1329     }
1330 
1331     switch (aAdvInfo.mError)
1332     {
1333     case kErrorNone:
1334         mCounters.mAdvSuccessful++;
1335         break;
1336     case kErrorResponseTimeout:
1337         mCounters.mAdvTimeout++;
1338         break;
1339     default:
1340         mCounters.mAdvRejected++;
1341         break;
1342     }
1343 
1344     aAdvInfo.SignalServerToCommit();
1345 }
1346 
1347 //---------------------------------------------------------------------------------------------------------------------
1348 // AdvertisingProxy::AdvInfo
1349 
AdvInfo(Host & aHost,const Server::MessageMetadata & aMetadata,uint32_t aTimeout)1350 AdvertisingProxy::AdvInfo::AdvInfo(Host &aHost, const Server::MessageMetadata &aMetadata, uint32_t aTimeout)
1351     : mNext(nullptr)
1352     , mBlockingAdv(nullptr)
1353     , mHost(aHost)
1354     , mExpireTime(TimerMilli::GetNow() + aTimeout)
1355     , mMessageMetadata(aMetadata)
1356     , mError(kErrorNone)
1357 {
1358     if (aMetadata.mMessageInfo != nullptr)
1359     {
1360         // If `mMessageInfo` is not null in the given `aMetadata` keep
1361         // a copy of it in `AdvInfo` structure and update the
1362         // `mMessageMetadata` to point to the local copy instead.
1363 
1364         mMessageInfo                  = *aMetadata.mMessageInfo;
1365         mMessageMetadata.mMessageInfo = &mMessageInfo;
1366     }
1367 }
1368 
SignalServerToCommit(void)1369 void AdvertisingProxy::AdvInfo::SignalServerToCommit(void)
1370 {
1371     LogInfo("Adv done '%s', error:%s", mHost.GetFullName(), ErrorToString(mError));
1372     Get<Server>().CommitSrpUpdate(mError, mHost, mMessageMetadata);
1373 }
1374 
IsCompleted(void) const1375 bool AdvertisingProxy::AdvInfo::IsCompleted(void) const
1376 {
1377     bool isCompleted = false;
1378 
1379     VerifyOrExit(mBlockingAdv == nullptr);
1380     isCompleted = (mError != kErrorNone) || mHost.mAdvIdRange.IsEmpty();
1381 
1382 exit:
1383     return isCompleted;
1384 }
1385 
1386 } // namespace Srp
1387 } // namespace ot
1388 
1389 #endif // OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE
1390