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