• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright (c) 2020, 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 #include "srp_client.hpp"
30 
31 #if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE
32 
33 #include "instance/instance.hpp"
34 
35 /**
36  * @file
37  *   This file implements the SRP client.
38  */
39 
40 namespace ot {
41 namespace Srp {
42 
43 RegisterLogModule("SrpClient");
44 
45 //---------------------------------------------------------------------
46 // Client::HostInfo
47 
Init(void)48 void Client::HostInfo::Init(void)
49 {
50     Clearable<HostInfo>::Clear();
51 
52     // State is directly set on `mState` instead of using `SetState()`
53     // to avoid logging.
54     mState = OT_SRP_CLIENT_ITEM_STATE_REMOVED;
55 }
56 
Clear(void)57 void Client::HostInfo::Clear(void)
58 {
59     Clearable<HostInfo>::Clear();
60     SetState(kRemoved);
61 }
62 
SetState(ItemState aState)63 bool Client::HostInfo::SetState(ItemState aState)
64 {
65     bool didChange;
66 
67     VerifyOrExit(aState != GetState(), didChange = false);
68 
69     LogInfo("HostInfo %s -> %s", ItemStateToString(GetState()), ItemStateToString(aState));
70 
71     mState    = MapEnum(aState);
72     didChange = true;
73 
74 exit:
75     return didChange;
76 }
77 
EnableAutoAddress(void)78 void Client::HostInfo::EnableAutoAddress(void)
79 {
80     mAddresses    = nullptr;
81     mNumAddresses = 0;
82     mAutoAddress  = true;
83 
84     LogInfo("HostInfo enabled auto address");
85 }
86 
SetAddresses(const Ip6::Address * aAddresses,uint8_t aNumAddresses)87 void Client::HostInfo::SetAddresses(const Ip6::Address *aAddresses, uint8_t aNumAddresses)
88 {
89     mAddresses    = aAddresses;
90     mNumAddresses = aNumAddresses;
91     mAutoAddress  = false;
92 
93     LogInfo("HostInfo set %d addrs", GetNumAddresses());
94 
95     for (uint8_t index = 0; index < GetNumAddresses(); index++)
96     {
97         LogInfo("%s", GetAddress(index).ToString().AsCString());
98     }
99 }
100 
101 //---------------------------------------------------------------------
102 // Client::Service
103 
Init(void)104 Error Client::Service::Init(void)
105 {
106     Error error = kErrorNone;
107 
108     VerifyOrExit((GetName() != nullptr) && (GetInstanceName() != nullptr), error = kErrorInvalidArgs);
109     VerifyOrExit((GetTxtEntries() != nullptr) || (GetNumTxtEntries() == 0), error = kErrorInvalidArgs);
110 
111     // State is directly set on `mState` instead of using `SetState()`
112     // to avoid logging.
113     mState = OT_SRP_CLIENT_ITEM_STATE_REMOVED;
114 
115     mLease    = Min(mLease, kMaxLease);
116     mKeyLease = Min(mKeyLease, kMaxLease);
117 
118 exit:
119     return error;
120 }
121 
SetState(ItemState aState)122 bool Client::Service::SetState(ItemState aState)
123 {
124     bool didChange;
125 
126     VerifyOrExit(GetState() != aState, didChange = false);
127 
128     LogInfo("Service %s -> %s, \"%s\" \"%s\"", ItemStateToString(GetState()), ItemStateToString(aState),
129             GetInstanceName(), GetName());
130 
131     if (aState == kToAdd)
132     {
133         constexpr uint16_t kSubTypeLabelStringSize = 80;
134 
135         String<kSubTypeLabelStringSize> string;
136 
137         // Log more details only when entering `kToAdd` state.
138 
139         if (HasSubType())
140         {
141             const char *label;
142 
143             for (uint16_t index = 0; (label = GetSubTypeLabelAt(index)) != nullptr; index++)
144             {
145                 string.Append("%s\"%s\"", (index != 0) ? ", " : "", label);
146             }
147         }
148 
149         LogInfo("subtypes:[%s] port:%d weight:%d prio:%d txts:%d", string.AsCString(), GetPort(), GetWeight(),
150                 GetPriority(), GetNumTxtEntries());
151     }
152 
153     mState    = MapEnum(aState);
154     didChange = true;
155 
156 exit:
157     return didChange;
158 }
159 
Matches(const Service & aOther) const160 bool Client::Service::Matches(const Service &aOther) const
161 {
162     // This method indicates whether or not two service entries match,
163     // i.e., have the same service and instance names. This is intended
164     // for use by `LinkedList::FindMatching()` to search within the
165     // `mServices` list.
166 
167     return StringMatch(GetName(), aOther.GetName()) && StringMatch(GetInstanceName(), aOther.GetInstanceName());
168 }
169 
170 //---------------------------------------------------------------------
171 // Client::TxJitter
172 
173 const uint32_t Client::TxJitter::kMaxJitters[] = {
174     Client::kMaxTxJitterOnDeviceReboot,    // (0) kOnDeviceReboot
175     Client::kMaxTxJitterOnServerStart,     // (1) kOnServerStart
176     Client::kMaxTxJitterOnServerRestart,   // (2) kOnServerRestart
177     Client::kMaxTxJitterOnServerSwitch,    // (3) kOnServerSwitch
178     Client::kMaxTxJitterOnSlaacAddrAdd,    // (4) kOnSlaacAddrAdd
179     Client::kMaxTxJitterOnSlaacAddrRemove, // (5) kOnSlaacAddrRemove
180 };
181 
Request(Reason aReason)182 void Client::TxJitter::Request(Reason aReason)
183 {
184     struct EnumCheck
185     {
186         InitEnumValidatorCounter();
187         ValidateNextEnum(kOnDeviceReboot);
188         ValidateNextEnum(kOnServerStart);
189         ValidateNextEnum(kOnServerRestart);
190         ValidateNextEnum(kOnServerSwitch);
191         ValidateNextEnum(kOnSlaacAddrAdd);
192         ValidateNextEnum(kOnSlaacAddrRemove);
193     };
194 
195     uint32_t maxJitter = kMaxJitters[aReason];
196 
197     LogInfo("Requesting max tx jitter %lu (%s)", ToUlong(maxJitter), ReasonToString(aReason));
198 
199     if (mRequestedMax != 0)
200     {
201         // If we have a previous request, adjust the `mRequestedMax`
202         // based on the time elapsed since that request was made.
203 
204         uint32_t duration = TimerMilli::GetNow() - mRequestTime;
205 
206         mRequestedMax = (mRequestedMax > duration) ? mRequestedMax - duration : 0;
207     }
208 
209     mRequestedMax = Max(mRequestedMax, maxJitter);
210     mRequestTime  = TimerMilli::GetNow();
211 }
212 
DetermineDelay(void)213 uint32_t Client::TxJitter::DetermineDelay(void)
214 {
215     uint32_t delay;
216     uint32_t maxJitter = kMaxTxJitterDefault;
217 
218     if (mRequestedMax != 0)
219     {
220         uint32_t duration = TimerMilli::GetNow() - mRequestTime;
221 
222         if (duration >= mRequestedMax)
223         {
224             LogInfo("Requested max tx jitter %lu already expired", ToUlong(mRequestedMax));
225         }
226         else
227         {
228             maxJitter = Max(mRequestedMax - duration, kMaxTxJitterDefault);
229             LogInfo("Applying remaining max jitter %lu", ToUlong(maxJitter));
230         }
231 
232         mRequestedMax = 0;
233     }
234 
235     delay = Random::NonCrypto::GetUint32InRange(kMinTxJitter, maxJitter);
236     LogInfo("Use random tx jitter %lu from [%lu, %lu]", ToUlong(delay), ToUlong(kMinTxJitter), ToUlong(maxJitter));
237 
238     return delay;
239 }
240 
241 #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
ReasonToString(Reason aReason)242 const char *Client::TxJitter::ReasonToString(Reason aReason)
243 {
244     static const char *const kReasonStrings[] = {
245         "OnDeviceReboot",    // (0) kOnDeviceReboot
246         "OnServerStart",     // (1) kOnServerStart
247         "OnServerRestart",   // (2) kOnServerRestart
248         "OnServerSwitch",    // (3) kOnServerSwitch
249         "OnSlaacAddrAdd",    // (4) kOnSlaacAddrAdd
250         "OnSlaacAddrRemove", // (5) kOnSlaacAddrRemove
251     };
252 
253     struct EnumCheck
254     {
255         InitEnumValidatorCounter();
256         ValidateNextEnum(kOnDeviceReboot);
257         ValidateNextEnum(kOnServerStart);
258         ValidateNextEnum(kOnServerRestart);
259         ValidateNextEnum(kOnServerSwitch);
260         ValidateNextEnum(kOnSlaacAddrAdd);
261         ValidateNextEnum(kOnSlaacAddrRemove);
262     };
263 
264     return kReasonStrings[aReason];
265 }
266 #endif
267 
268 //---------------------------------------------------------------------
269 // Client::AutoStart
270 
271 #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
272 
AutoStart(void)273 Client::AutoStart::AutoStart(void)
274 {
275     Clear();
276     mState = kDefaultMode ? kFirstTimeSelecting : kDisabled;
277 }
278 
HasSelectedServer(void) const279 bool Client::AutoStart::HasSelectedServer(void) const
280 {
281     bool hasSelected = false;
282 
283     switch (mState)
284     {
285     case kDisabled:
286     case kFirstTimeSelecting:
287     case kReselecting:
288         break;
289 
290     case kSelectedUnicastPreferred:
291     case kSelectedUnicast:
292     case kSelectedAnycast:
293         hasSelected = true;
294         break;
295     }
296 
297     return hasSelected;
298 }
299 
SetState(State aState)300 void Client::AutoStart::SetState(State aState)
301 {
302     if (mState != aState)
303     {
304         LogInfo("AutoStartState %s -> %s", StateToString(mState), StateToString(aState));
305         mState = aState;
306     }
307 }
308 
InvokeCallback(const Ip6::SockAddr * aServerSockAddr) const309 void Client::AutoStart::InvokeCallback(const Ip6::SockAddr *aServerSockAddr) const
310 {
311     mCallback.InvokeIfSet(aServerSockAddr);
312 }
313 
314 #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
StateToString(State aState)315 const char *Client::AutoStart::StateToString(State aState)
316 {
317     static const char *const kStateStrings[] = {
318         "Disabled",      // (0) kDisabled
319         "1stTimeSelect", // (1) kFirstTimeSelecting
320         "Reselect",      // (2) kReselecting
321         "Unicast-prf",   // (3) kSelectedUnicastPreferred
322         "Anycast",       // (4) kSelectedAnycast
323         "Unicast",       // (5) kSelectedUnicast
324     };
325 
326     struct EnumCheck
327     {
328         InitEnumValidatorCounter();
329         ValidateNextEnum(kDisabled);
330         ValidateNextEnum(kFirstTimeSelecting);
331         ValidateNextEnum(kReselecting);
332         ValidateNextEnum(kSelectedUnicastPreferred);
333         ValidateNextEnum(kSelectedAnycast);
334         ValidateNextEnum(kSelectedUnicast);
335     };
336 
337     return kStateStrings[aState];
338 }
339 #endif
340 
341 #endif // OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
342 
343 //---------------------------------------------------------------------
344 // Client
345 
346 const char Client::kDefaultDomainName[] = "default.service.arpa";
347 
Client(Instance & aInstance)348 Client::Client(Instance &aInstance)
349     : InstanceLocator(aInstance)
350     , mState(kStateStopped)
351     , mTxFailureRetryCount(0)
352     , mShouldRemoveKeyLease(false)
353     , mSingleServiceMode(false)
354 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
355     , mServiceKeyRecordEnabled(false)
356     , mUseShortLeaseOption(false)
357 #endif
358     , mNextMessageId(0)
359     , mResponseMessageId(0)
360     , mAutoHostAddressCount(0)
361     , mRetryWaitInterval(kMinRetryWaitInterval)
362     , mTtl(0)
363     , mLease(0)
364     , mKeyLease(0)
365     , mDefaultLease(kDefaultLease)
366     , mDefaultKeyLease(kDefaultKeyLease)
367     , mSocket(aInstance, *this)
368     , mDomainName(kDefaultDomainName)
369     , mTimer(aInstance)
370 #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
371     , mGuardTimer(aInstance)
372 #endif
373 {
374     // The `Client` implementation uses different constant array of
375     // `ItemState` to define transitions between states in `Pause()`,
376     // `Stop()`, `SendUpdate`, and `ProcessResponse()`, or to convert
377     // an `ItemState` to string. Here, we assert that the enumeration
378     // values are correct.
379 
380     struct EnumCheck
381     {
382         InitEnumValidatorCounter();
383         ValidateNextEnum(kToAdd);
384         ValidateNextEnum(kAdding);
385         ValidateNextEnum(kToRefresh);
386         ValidateNextEnum(kRefreshing);
387         ValidateNextEnum(kToRemove);
388         ValidateNextEnum(kRemoving);
389         ValidateNextEnum(kRegistered);
390         ValidateNextEnum(kRemoved);
391     };
392 
393     mHostInfo.Init();
394 }
395 
Start(const Ip6::SockAddr & aServerSockAddr,Requester aRequester)396 Error Client::Start(const Ip6::SockAddr &aServerSockAddr, Requester aRequester)
397 {
398     Error error;
399 
400     VerifyOrExit(GetState() == kStateStopped,
401                  error = (aServerSockAddr == GetServerAddress()) ? kErrorNone : kErrorBusy);
402 
403     SuccessOrExit(error = mSocket.Open(Ip6::kNetifThreadInternal));
404 
405     error = mSocket.Connect(aServerSockAddr);
406 
407     if (error != kErrorNone)
408     {
409         LogInfo("Failed to connect to server %s: %s", aServerSockAddr.GetAddress().ToString().AsCString(),
410                 ErrorToString(error));
411         IgnoreError(mSocket.Close());
412         ExitNow();
413     }
414 
415     LogInfo("%starting, server %s", (aRequester == kRequesterUser) ? "S" : "Auto-s",
416             aServerSockAddr.ToString().AsCString());
417 
418     Resume();
419 
420 #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
421     if (aRequester == kRequesterAuto)
422     {
423 #if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE && OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVER_ADDRESS_AUTO_SET_ENABLE
424         Get<Dns::Client>().UpdateDefaultConfigAddress();
425 #endif
426         mAutoStart.InvokeCallback(&aServerSockAddr);
427     }
428 #endif
429 
430 exit:
431     return error;
432 }
433 
Stop(Requester aRequester,StopMode aMode)434 void Client::Stop(Requester aRequester, StopMode aMode)
435 {
436     // Change the state of host info and services so that they are
437     // added/removed again once the client is started back. In the
438     // case of `kAdding`, we intentionally move to `kToRefresh`
439     // instead of `kToAdd` since the server may receive our add
440     // request and the item may be registered on the server. This
441     // ensures that if we are later asked to remove the item, we do
442     // notify server.
443 
444     static const ItemState kNewStateOnStop[]{
445         /* (0) kToAdd      -> */ kToAdd,
446         /* (1) kAdding     -> */ kToRefresh,
447         /* (2) kToRefresh  -> */ kToRefresh,
448         /* (3) kRefreshing -> */ kToRefresh,
449         /* (4) kToRemove   -> */ kToRemove,
450         /* (5) kRemoving   -> */ kToRemove,
451         /* (6) kRegistered -> */ kToRefresh,
452         /* (7) kRemoved    -> */ kRemoved,
453     };
454 
455     VerifyOrExit(GetState() != kStateStopped);
456 
457     mSingleServiceMode = false;
458 
459     // State changes:
460     //   kAdding     -> kToRefresh
461     //   kRefreshing -> kToRefresh
462     //   kRemoving   -> kToRemove
463     //   kRegistered -> kToRefresh
464 
465     ChangeHostAndServiceStates(kNewStateOnStop, kForAllServices);
466 
467     IgnoreError(mSocket.Close());
468 
469     mShouldRemoveKeyLease = false;
470     mTxFailureRetryCount  = 0;
471     mResponseMessageId    = mNextMessageId;
472 
473     if (aMode == kResetRetryInterval)
474     {
475         ResetRetryWaitInterval();
476     }
477 
478     SetState(kStateStopped);
479 
480 #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
481 #if OPENTHREAD_CONFIG_SRP_CLIENT_SWITCH_SERVER_ON_FAILURE
482     mAutoStart.ResetTimeoutFailureCount();
483 #endif
484     if (aRequester == kRequesterAuto)
485     {
486         mAutoStart.InvokeCallback(nullptr);
487     }
488 #endif
489 
490 exit:
491 #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
492     if (aRequester == kRequesterUser)
493     {
494         DisableAutoStartMode();
495     }
496 #endif
497 }
498 
Resume(void)499 void Client::Resume(void)
500 {
501     SetState(kStateUpdated);
502     UpdateState();
503 }
504 
Pause(void)505 void Client::Pause(void)
506 {
507     // Change the state of host info and services that are are being
508     // added or removed so that they are added/removed again once the
509     // client is resumed or started back.
510 
511     static const ItemState kNewStateOnPause[]{
512         /* (0) kToAdd      -> */ kToAdd,
513         /* (1) kAdding     -> */ kToRefresh,
514         /* (2) kToRefresh  -> */ kToRefresh,
515         /* (3) kRefreshing -> */ kToRefresh,
516         /* (4) kToRemove   -> */ kToRemove,
517         /* (5) kRemoving   -> */ kToRemove,
518         /* (6) kRegistered -> */ kRegistered,
519         /* (7) kRemoved    -> */ kRemoved,
520     };
521 
522     mSingleServiceMode = false;
523 
524     // State changes:
525     //   kAdding     -> kToRefresh
526     //   kRefreshing -> kToRefresh
527     //   kRemoving   -> kToRemove
528 
529     ChangeHostAndServiceStates(kNewStateOnPause, kForAllServices);
530 
531     SetState(kStatePaused);
532 }
533 
HandleNotifierEvents(Events aEvents)534 void Client::HandleNotifierEvents(Events aEvents)
535 {
536     if (aEvents.Contains(kEventThreadRoleChanged))
537     {
538         HandleRoleChanged();
539     }
540 
541 #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
542     if (aEvents.ContainsAny(kEventThreadNetdataChanged | kEventThreadMeshLocalAddrChanged))
543     {
544         ProcessAutoStart();
545     }
546 #endif
547 
548     if (aEvents.ContainsAny(kEventIp6AddressAdded | kEventIp6AddressRemoved | kEventThreadMeshLocalAddrChanged) &&
549         ShouldUpdateHostAutoAddresses())
550     {
551         IgnoreError(UpdateHostInfoStateOnAddressChange());
552         UpdateState();
553     }
554 }
555 
HandleRoleChanged(void)556 void Client::HandleRoleChanged(void)
557 {
558     if (Get<Mle::Mle>().IsAttached())
559     {
560 #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
561         ApplyAutoStartGuardOnAttach();
562 #endif
563 
564         VerifyOrExit(GetState() == kStatePaused);
565         Resume();
566     }
567     else
568     {
569         VerifyOrExit(GetState() != kStateStopped);
570         Pause();
571     }
572 
573 exit:
574     return;
575 }
576 
577 #if OPENTHREAD_CONFIG_SRP_CLIENT_DOMAIN_NAME_API_ENABLE
SetDomainName(const char * aName)578 Error Client::SetDomainName(const char *aName)
579 {
580     Error error = kErrorNone;
581 
582     VerifyOrExit((mHostInfo.GetState() == kToAdd) || (mHostInfo.GetState() == kRemoved), error = kErrorInvalidState);
583 
584     mDomainName = (aName != nullptr) ? aName : kDefaultDomainName;
585     LogInfo("Domain name \"%s\"", mDomainName);
586 
587 exit:
588     return error;
589 }
590 #endif
591 
SetHostName(const char * aName)592 Error Client::SetHostName(const char *aName)
593 {
594     Error error = kErrorNone;
595 
596     VerifyOrExit(aName != nullptr, error = kErrorInvalidArgs);
597 
598     VerifyOrExit((mHostInfo.GetState() == kToAdd) || (mHostInfo.GetState() == kRemoved), error = kErrorInvalidState);
599 
600     LogInfo("Host name \"%s\"", aName);
601     mHostInfo.SetName(aName);
602     mHostInfo.SetState(kToAdd);
603     UpdateState();
604 
605 exit:
606     return error;
607 }
608 
EnableAutoHostAddress(void)609 Error Client::EnableAutoHostAddress(void)
610 {
611     Error error = kErrorNone;
612 
613     VerifyOrExit(!mHostInfo.IsAutoAddressEnabled());
614     SuccessOrExit(error = UpdateHostInfoStateOnAddressChange());
615 
616     for (Ip6::Netif::UnicastAddress &unicastAddress : Get<ThreadNetif>().GetUnicastAddresses())
617     {
618         unicastAddress.mSrpRegistered = false;
619     }
620 
621     mAutoHostAddressCount = 0;
622 
623     mHostInfo.EnableAutoAddress();
624     UpdateState();
625 
626 exit:
627     return error;
628 }
629 
SetHostAddresses(const Ip6::Address * aAddresses,uint8_t aNumAddresses)630 Error Client::SetHostAddresses(const Ip6::Address *aAddresses, uint8_t aNumAddresses)
631 {
632     Error error = kErrorNone;
633 
634     VerifyOrExit((aAddresses != nullptr) && (aNumAddresses > 0), error = kErrorInvalidArgs);
635     SuccessOrExit(error = UpdateHostInfoStateOnAddressChange());
636 
637     mHostInfo.SetAddresses(aAddresses, aNumAddresses);
638     UpdateState();
639 
640 exit:
641     return error;
642 }
643 
HandleUnicastAddressEvent(Ip6::Netif::AddressEvent aEvent,const Ip6::Netif::UnicastAddress & aAddress)644 void Client::HandleUnicastAddressEvent(Ip6::Netif::AddressEvent aEvent, const Ip6::Netif::UnicastAddress &aAddress)
645 {
646     // This callback from `Netif` signals an impending addition or
647     // removal of a unicast address, occurring before `Notifier`
648     // events. If `AutoAddress` is enabled, we check whether the
649     // address origin is SLAAC (e.g., an OMR address) and request a
650     // longer `TxJitter`. This helps randomize the next SRP
651     // update transmission time when triggered by an OMR prefix
652     // change.
653 
654     VerifyOrExit(IsRunning());
655     VerifyOrExit(mHostInfo.IsAutoAddressEnabled());
656 
657     VerifyOrExit(aAddress.GetOrigin() == Ip6::Netif::kOriginSlaac);
658 
659 #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
660     // The `mGuardTimer`, started by `ApplyAutoStartGuardOnAttach()`,
661     // tracks a guard interval after the attach event. If an
662     // address change occurs within this short window, we do not
663     // apply a longer TX jitter, as this likely indicates a device
664     // reboot.
665     VerifyOrExit(!mGuardTimer.IsRunning());
666 #endif
667 
668     mTxJitter.Request((aEvent == Ip6::Netif::kAddressAdded) ? TxJitter::kOnSlaacAddrAdd : TxJitter::kOnSlaacAddrRemove);
669 
670 exit:
671     return;
672 }
673 
ShouldUpdateHostAutoAddresses(void) const674 bool Client::ShouldUpdateHostAutoAddresses(void) const
675 {
676     // Determine if any changes to the addresses on `ThreadNetif`
677     // require registration with the server when `AutoHostAddress` is
678     // enabled. This includes registering all preferred addresses,
679     // excluding link-local and mesh-local addresses. If no eligible
680     // address is available, the ML-EID will be registered.
681 
682     bool                        shouldUpdate    = false;
683     uint16_t                    registeredCount = 0;
684     Ip6::Netif::UnicastAddress &mlEid           = Get<Mle::Mle>().GetMeshLocalEidUnicastAddress();
685 
686     VerifyOrExit(mHostInfo.IsAutoAddressEnabled());
687 
688     // We iterate through all eligible addresses on the `ThreadNetif`.
689     // If we encounter a new address that should be registered but
690     // isn't, or a previously registered address has been removed, we
691     // trigger an SRP update to reflect these changes. However, if a
692     // previously registered address is being deprecated (e.g., due
693     // to an OMR prefix removal from Network Data), we defer the SRP
694     // update. The client will re-register after the deprecation
695     // time has elapsed and the address is removed. In the meantime,
696     // if any other event triggers the client to send an SRP update,
697     // the updated address list will be included in that update.
698 
699     for (const Ip6::Netif::UnicastAddress &unicastAddress : Get<ThreadNetif>().GetUnicastAddresses())
700     {
701         if (&unicastAddress == &mlEid)
702         {
703             continue;
704         }
705 
706         if (ShouldHostAutoAddressRegister(unicastAddress) != unicastAddress.mSrpRegistered)
707         {
708             // If this address was previously registered but is no
709             // longer eligible, we skip sending an immediate update
710             // only if the address is currently being deprecated
711             // (it's still valid but no longer preferred).
712 
713             bool skip = unicastAddress.mSrpRegistered && unicastAddress.mValid && !unicastAddress.mPreferred;
714 
715             if (!skip)
716             {
717                 ExitNow(shouldUpdate = true);
718             }
719         }
720 
721         if (unicastAddress.mSrpRegistered)
722         {
723             registeredCount++;
724         }
725     }
726 
727     if (registeredCount == 0)
728     {
729         ExitNow(shouldUpdate = !mlEid.mSrpRegistered);
730     }
731 
732     // Compare the current number of addresses that are marked as
733     // registered with the previous value `mAutoHostAddressCount`.
734     // This check handles the case where a previously registered address
735     // has been removed.
736 
737     shouldUpdate = (registeredCount != mAutoHostAddressCount);
738 
739 exit:
740     return shouldUpdate;
741 }
742 
ShouldHostAutoAddressRegister(const Ip6::Netif::UnicastAddress & aUnicastAddress) const743 bool Client::ShouldHostAutoAddressRegister(const Ip6::Netif::UnicastAddress &aUnicastAddress) const
744 {
745     bool shouldRegister = false;
746 
747     VerifyOrExit(aUnicastAddress.mValid);
748     VerifyOrExit(aUnicastAddress.mPreferred);
749     VerifyOrExit(!aUnicastAddress.GetAddress().IsLinkLocalUnicast());
750     VerifyOrExit(!Get<Mle::Mle>().IsMeshLocalAddress(aUnicastAddress.GetAddress()));
751 
752     shouldRegister = true;
753 
754 exit:
755     return shouldRegister;
756 }
757 
UpdateHostInfoStateOnAddressChange(void)758 Error Client::UpdateHostInfoStateOnAddressChange(void)
759 {
760     Error error = kErrorNone;
761 
762     VerifyOrExit((mHostInfo.GetState() != kToRemove) && (mHostInfo.GetState() != kRemoving),
763                  error = kErrorInvalidState);
764 
765     if (mHostInfo.GetState() == kRemoved)
766     {
767         mHostInfo.SetState(kToAdd);
768     }
769     else if (mHostInfo.GetState() != kToAdd)
770     {
771         mHostInfo.SetState(kToRefresh);
772     }
773 
774 exit:
775     return error;
776 }
777 
AddService(Service & aService)778 Error Client::AddService(Service &aService)
779 {
780     Error error;
781 
782     VerifyOrExit(mServices.FindMatching(aService) == nullptr, error = kErrorAlready);
783 
784     SuccessOrExit(error = aService.Init());
785     mServices.Push(aService);
786 
787     aService.SetState(kToAdd);
788     UpdateState();
789 
790 exit:
791     return error;
792 }
793 
RemoveService(Service & aService)794 Error Client::RemoveService(Service &aService)
795 {
796     Error               error = kErrorNone;
797     LinkedList<Service> removedServices;
798 
799     VerifyOrExit(mServices.Contains(aService), error = kErrorNotFound);
800 
801     UpdateServiceStateToRemove(aService);
802     UpdateState();
803 
804 exit:
805     return error;
806 }
807 
UpdateServiceStateToRemove(Service & aService)808 void Client::UpdateServiceStateToRemove(Service &aService)
809 {
810     if (aService.GetState() != kRemoving)
811     {
812         aService.SetState(kToRemove);
813     }
814 }
815 
ClearService(Service & aService)816 Error Client::ClearService(Service &aService)
817 {
818     Error error;
819 
820     SuccessOrExit(error = mServices.Remove(aService));
821     aService.SetNext(nullptr);
822     aService.SetState(kRemoved);
823     UpdateState();
824 
825 exit:
826     return error;
827 }
828 
RemoveHostAndServices(bool aShouldRemoveKeyLease,bool aSendUnregToServer)829 Error Client::RemoveHostAndServices(bool aShouldRemoveKeyLease, bool aSendUnregToServer)
830 {
831     Error error = kErrorNone;
832 
833     LogInfo("Remove host & services");
834 
835     VerifyOrExit(mHostInfo.GetState() != kRemoved, error = kErrorAlready);
836 
837     if ((mHostInfo.GetState() == kToRemove) || (mHostInfo.GetState() == kRemoving))
838     {
839         // Host info remove is already ongoing, if "key lease" remove mode is
840         // the same, there is no need to send a new update message.
841         VerifyOrExit(mShouldRemoveKeyLease != aShouldRemoveKeyLease);
842     }
843 
844     mShouldRemoveKeyLease = aShouldRemoveKeyLease;
845 
846     for (Service &service : mServices)
847     {
848         UpdateServiceStateToRemove(service);
849     }
850 
851     if ((mHostInfo.GetState() == kToAdd) && !aSendUnregToServer)
852     {
853         // Host info is not added yet (not yet registered with
854         // server), so we can remove it and all services immediately.
855         mHostInfo.SetState(kRemoved);
856         HandleUpdateDone();
857         ExitNow();
858     }
859 
860     mHostInfo.SetState(kToRemove);
861     UpdateState();
862 
863 exit:
864     return error;
865 }
866 
ClearHostAndServices(void)867 void Client::ClearHostAndServices(void)
868 {
869     LogInfo("Clear host & services");
870 
871     switch (GetState())
872     {
873     case kStateStopped:
874     case kStatePaused:
875         break;
876 
877     case kStateToUpdate:
878     case kStateUpdating:
879     case kStateUpdated:
880     case kStateToRetry:
881         SetState(kStateUpdated);
882         break;
883     }
884 
885     mTxFailureRetryCount = 0;
886     ResetRetryWaitInterval();
887 
888     mServices.Clear();
889     mHostInfo.Clear();
890 }
891 
SetState(State aState)892 void Client::SetState(State aState)
893 {
894     VerifyOrExit(aState != mState);
895 
896     LogInfo("State %s -> %s", StateToString(mState), StateToString(aState));
897     mState = aState;
898 
899     switch (mState)
900     {
901     case kStateStopped:
902     case kStatePaused:
903     case kStateUpdated:
904         mTimer.Stop();
905         break;
906 
907     case kStateToUpdate:
908         mTimer.Start(mTxJitter.DetermineDelay());
909         break;
910 
911     case kStateUpdating:
912         mTimer.Start(GetRetryWaitInterval());
913         break;
914 
915     case kStateToRetry:
916         break;
917     }
918 exit:
919     return;
920 }
921 
ChangeHostAndServiceStates(const ItemState * aNewStates,ServiceStateChangeMode aMode)922 bool Client::ChangeHostAndServiceStates(const ItemState *aNewStates, ServiceStateChangeMode aMode)
923 {
924     bool anyChanged;
925 
926 #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE && OPENTHREAD_CONFIG_SRP_CLIENT_SAVE_SELECTED_SERVER_ENABLE
927     ItemState oldHostState = mHostInfo.GetState();
928 #endif
929 
930     anyChanged = mHostInfo.SetState(aNewStates[mHostInfo.GetState()]);
931 
932     for (Service &service : mServices)
933     {
934         if ((aMode == kForServicesAppendedInMessage) && !service.IsAppendedInMessage())
935         {
936             continue;
937         }
938 
939         anyChanged |= service.SetState(aNewStates[service.GetState()]);
940     }
941 
942 #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE && OPENTHREAD_CONFIG_SRP_CLIENT_SAVE_SELECTED_SERVER_ENABLE
943     if ((oldHostState != kRegistered) && (mHostInfo.GetState() == kRegistered))
944     {
945         Settings::SrpClientInfo info;
946 
947         switch (mAutoStart.GetState())
948         {
949         case AutoStart::kDisabled:
950         case AutoStart::kFirstTimeSelecting:
951         case AutoStart::kReselecting:
952             break;
953 
954         case AutoStart::kSelectedUnicastPreferred:
955         case AutoStart::kSelectedUnicast:
956             info.SetServerAddress(GetServerAddress().GetAddress());
957             info.SetServerPort(GetServerAddress().GetPort());
958             IgnoreError(Get<Settings>().Save(info));
959             break;
960 
961         case AutoStart::kSelectedAnycast:
962             IgnoreError(Get<Settings>().Delete<Settings::SrpClientInfo>());
963             break;
964         }
965     }
966 #endif // OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE && OPENTHREAD_CONFIG_SRP_CLIENT_SAVE_SELECTED_SERVER_ENABLE
967 
968     return anyChanged;
969 }
970 
InvokeCallback(Error aError) const971 void Client::InvokeCallback(Error aError) const { InvokeCallback(aError, mHostInfo, nullptr); }
972 
InvokeCallback(Error aError,const HostInfo & aHostInfo,const Service * aRemovedServices) const973 void Client::InvokeCallback(Error aError, const HostInfo &aHostInfo, const Service *aRemovedServices) const
974 {
975     mCallback.InvokeIfSet(aError, &aHostInfo, mServices.GetHead(), aRemovedServices);
976 }
977 
SendUpdate(void)978 void Client::SendUpdate(void)
979 {
980     static const ItemState kNewStateOnMessageTx[]{
981         /* (0) kToAdd      -> */ kAdding,
982         /* (1) kAdding     -> */ kAdding,
983         /* (2) kToRefresh  -> */ kRefreshing,
984         /* (3) kRefreshing -> */ kRefreshing,
985         /* (4) kToRemove   -> */ kRemoving,
986         /* (5) kRemoving   -> */ kRemoving,
987         /* (6) kRegistered -> */ kRegistered,
988         /* (7) kRemoved    -> */ kRemoved,
989     };
990 
991     Error    error = kErrorNone;
992     MsgInfo  info;
993     uint32_t length;
994     bool     anyChanged;
995 
996     info.mMessage.Reset(mSocket.NewMessage());
997     VerifyOrExit(info.mMessage != nullptr, error = kErrorNoBufs);
998 
999     SuccessOrExit(error = PrepareUpdateMessage(info));
1000 
1001     length = info.mMessage->GetLength() + sizeof(Ip6::Udp::Header) + sizeof(Ip6::Header);
1002 
1003     if (length >= Ip6::kMaxDatagramLength)
1004     {
1005         LogInfo("Msg len %lu is larger than MTU, enabling single service mode", ToUlong(length));
1006         mSingleServiceMode = true;
1007         IgnoreError(info.mMessage->SetLength(0));
1008         SuccessOrExit(error = PrepareUpdateMessage(info));
1009     }
1010 
1011     SuccessOrExit(error = mSocket.SendTo(*info.mMessage, Ip6::MessageInfo()));
1012 
1013     // Ownership of the message is transferred to the socket upon a
1014     // successful `SendTo()` call.
1015 
1016     info.mMessage.Release();
1017 
1018     LogInfo("Send update, msg-id:0x%x", mNextMessageId);
1019 
1020     // State changes:
1021     //   kToAdd     -> kAdding
1022     //   kToRefresh -> kRefreshing
1023     //   kToRemove  -> kRemoving
1024 
1025     anyChanged = ChangeHostAndServiceStates(kNewStateOnMessageTx, kForServicesAppendedInMessage);
1026 
1027     // `mNextMessageId` tracks the message ID used in the prepared
1028     // update message. It is incremented after a successful
1029     // `mSocket.SendTo()` call. If unsuccessful, the same ID can be
1030     // reused for the next update.
1031     //
1032     // Acceptable response message IDs fall within the range starting
1033     // at `mResponseMessageId ` and ending before `mNextMessageId`.
1034     //
1035     // `anyChanged` tracks if any host or service states have changed.
1036     // If not, the prepared message is identical to the last one with
1037     // the same hosts/services, allowing us to accept earlier message
1038     // IDs. If changes occur, `mResponseMessageId ` is updated to
1039     // ensure only responses to the latest message are accepted.
1040 
1041     if (anyChanged)
1042     {
1043         mResponseMessageId = mNextMessageId;
1044     }
1045 
1046     mNextMessageId++;
1047 
1048     // Remember the update message tx time to use later to determine the
1049     // lease renew time.
1050     mLeaseRenewTime      = TimerMilli::GetNow();
1051     mTxFailureRetryCount = 0;
1052 
1053     SetState(kStateUpdating);
1054 
1055     if (!Get<Mle::Mle>().IsRxOnWhenIdle())
1056     {
1057         // If device is sleepy send fast polls while waiting for
1058         // the response from server.
1059         Get<DataPollSender>().SendFastPolls(kFastPollsAfterUpdateTx);
1060     }
1061 
1062 exit:
1063     if (error != kErrorNone)
1064     {
1065         // If there is an error in preparation or transmission of the
1066         // update message (e.g., no buffer to allocate message), up to
1067         // `kMaxTxFailureRetries` times, we wait for a short interval
1068         // `kTxFailureRetryInterval` and try again. After this, we
1069         // continue to retry using the `mRetryWaitInterval` (which keeps
1070         // growing on each failure).
1071 
1072         LogInfo("Failed to send update: %s", ErrorToString(error));
1073 
1074         mSingleServiceMode = false;
1075 
1076         SetState(kStateToRetry);
1077 
1078         if (mTxFailureRetryCount < kMaxTxFailureRetries)
1079         {
1080             uint32_t interval;
1081 
1082             mTxFailureRetryCount++;
1083             interval = Random::NonCrypto::AddJitter(kTxFailureRetryInterval, kTxFailureRetryJitter);
1084             mTimer.Start(interval);
1085 
1086             LogInfo("Quick retry %u in %lu msec", mTxFailureRetryCount, ToUlong(interval));
1087 
1088             // Do not report message preparation errors to user
1089             // until `kMaxTxFailureRetries` are exhausted.
1090         }
1091         else
1092         {
1093             LogRetryWaitInterval();
1094             mTimer.Start(Random::NonCrypto::AddJitter(GetRetryWaitInterval(), kRetryIntervalJitter));
1095             GrowRetryWaitInterval();
1096             InvokeCallback(error);
1097         }
1098     }
1099 }
1100 
PrepareUpdateMessage(MsgInfo & aInfo)1101 Error Client::PrepareUpdateMessage(MsgInfo &aInfo)
1102 {
1103     constexpr uint16_t kHeaderOffset = 0;
1104 
1105     Error             error = kErrorNone;
1106     Dns::UpdateHeader header;
1107 
1108     aInfo.mDomainNameOffset = MsgInfo::kUnknownOffset;
1109     aInfo.mHostNameOffset   = MsgInfo::kUnknownOffset;
1110     aInfo.mRecordCount      = 0;
1111 
1112 #if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
1113     aInfo.mKeyInfo.SetKeyRef(Get<Crypto::Storage::KeyRefManager>().KeyRefFor(Crypto::Storage::KeyRefManager::kEcdsa));
1114 #endif
1115 
1116     SuccessOrExit(error = ReadOrGenerateKey(aInfo.mKeyInfo));
1117 
1118     header.SetMessageId(mNextMessageId);
1119 
1120     // SRP Update (DNS Update) message must have exactly one record in
1121     // Zone section, no records in Prerequisite Section, can have
1122     // multiple records in Update Section (tracked as they are added),
1123     // and two records in Additional Data Section (OPT and SIG records).
1124     // The SIG record itself should not be included in calculation of
1125     // SIG(0) signature, so the addition record count is set to one
1126     // here. After signature calculation and appending of SIG record,
1127     // the additional record count is updated to two and the header is
1128     // rewritten in the message.
1129 
1130     header.SetZoneRecordCount(1);
1131     header.SetAdditionalRecordCount(1);
1132     SuccessOrExit(error = aInfo.mMessage->Append(header));
1133 
1134     // Prepare Zone section
1135 
1136     aInfo.mDomainNameOffset = aInfo.mMessage->GetLength();
1137     SuccessOrExit(error = Dns::Name::AppendName(mDomainName, *aInfo.mMessage));
1138     SuccessOrExit(error = aInfo.mMessage->Append(Dns::Zone()));
1139 
1140     // Prepare Update section
1141 
1142     SuccessOrExit(error = AppendServiceInstructions(aInfo));
1143     SuccessOrExit(error = AppendHostDescriptionInstruction(aInfo));
1144 
1145     header.SetUpdateRecordCount(aInfo.mRecordCount);
1146     aInfo.mMessage->Write(kHeaderOffset, header);
1147 
1148     // Prepare Additional Data section
1149 
1150     SuccessOrExit(error = AppendUpdateLeaseOptRecord(aInfo));
1151     SuccessOrExit(error = AppendSignature(aInfo));
1152 
1153     header.SetAdditionalRecordCount(2); // Lease OPT and SIG RRs
1154     aInfo.mMessage->Write(kHeaderOffset, header);
1155 
1156 exit:
1157     return error;
1158 }
1159 
1160 #if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
ReadOrGenerateKey(KeyInfo & aKeyInfo)1161 Error Client::ReadOrGenerateKey(KeyInfo &aKeyInfo)
1162 {
1163     Error                        error = kErrorNone;
1164     Crypto::Ecdsa::P256::KeyPair keyPair;
1165 
1166     VerifyOrExit(!Crypto::Storage::HasKey(aKeyInfo.GetKeyRef()));
1167     error = Get<Settings>().Read<Settings::SrpEcdsaKey>(keyPair);
1168 
1169     if (error == kErrorNone)
1170     {
1171         if (aKeyInfo.ImportKeyPair(keyPair) != kErrorNone)
1172         {
1173             SuccessOrExit(error = aKeyInfo.Generate());
1174         }
1175         IgnoreError(Get<Settings>().Delete<Settings::SrpEcdsaKey>());
1176     }
1177     else
1178     {
1179         SuccessOrExit(error = aKeyInfo.Generate());
1180     }
1181 exit:
1182     return error;
1183 }
1184 #else
ReadOrGenerateKey(KeyInfo & aKeyInfo)1185 Error Client::ReadOrGenerateKey(KeyInfo &aKeyInfo)
1186 {
1187     Error error;
1188 
1189     error = Get<Settings>().Read<Settings::SrpEcdsaKey>(aKeyInfo);
1190 
1191     if (error == kErrorNone)
1192     {
1193         Crypto::Ecdsa::P256::PublicKey publicKey;
1194 
1195         if (aKeyInfo.GetPublicKey(publicKey) == kErrorNone)
1196         {
1197             ExitNow();
1198         }
1199     }
1200 
1201     SuccessOrExit(error = aKeyInfo.Generate());
1202     IgnoreError(Get<Settings>().Save<Settings::SrpEcdsaKey>(aKeyInfo));
1203 
1204 exit:
1205     return error;
1206 }
1207 #endif //  OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
1208 
AppendServiceInstructions(MsgInfo & aInfo)1209 Error Client::AppendServiceInstructions(MsgInfo &aInfo)
1210 {
1211     Error error = kErrorNone;
1212 
1213     if ((mHostInfo.GetState() == kToRemove) || (mHostInfo.GetState() == kRemoving))
1214     {
1215         // When host is being removed, there is no need to include
1216         // services in the message (server is expected to remove any
1217         // previously registered services by this client). However, we
1218         // still mark all services as if they are appended in the message
1219         // so to ensure to update their state after sending the message.
1220 
1221         for (Service &service : mServices)
1222         {
1223             service.MarkAsAppendedInMessage();
1224         }
1225 
1226         mLease    = 0;
1227         mKeyLease = mShouldRemoveKeyLease ? 0 : mDefaultKeyLease;
1228         ExitNow();
1229     }
1230 
1231     mLease    = kUnspecifiedInterval;
1232     mKeyLease = kUnspecifiedInterval;
1233 
1234     // We first go through all services which are being updated (in any
1235     // of `...ing` states) and determine the lease and key lease intervals
1236     // associated with them. By the end of the loop either of `mLease` or
1237     // `mKeyLease` may be set or may still remain `kUnspecifiedInterval`.
1238 
1239     for (Service &service : mServices)
1240     {
1241         uint32_t lease    = DetermineLeaseInterval(service.GetLease(), mDefaultLease);
1242         uint32_t keyLease = Max(DetermineLeaseInterval(service.GetKeyLease(), mDefaultKeyLease), lease);
1243 
1244         service.ClearAppendedInMessageFlag();
1245 
1246         switch (service.GetState())
1247         {
1248         case kAdding:
1249         case kRefreshing:
1250             OT_ASSERT((mLease == kUnspecifiedInterval) || (mLease == lease));
1251             mLease = lease;
1252 
1253             OT_FALL_THROUGH;
1254 
1255         case kRemoving:
1256             OT_ASSERT((mKeyLease == kUnspecifiedInterval) || (mKeyLease == keyLease));
1257             mKeyLease = keyLease;
1258             break;
1259 
1260         case kToAdd:
1261         case kToRefresh:
1262         case kToRemove:
1263         case kRegistered:
1264         case kRemoved:
1265             break;
1266         }
1267     }
1268 
1269     // We go through all services again and append the services that
1270     // match the selected `mLease` and `mKeyLease`. If the lease intervals
1271     // are not yet set, the first appended service will determine them.
1272 
1273     for (Service &service : mServices)
1274     {
1275         // Skip over services that are already registered in this loop.
1276         // They may be added from the loop below once the lease intervals
1277         // are determined.
1278 
1279         if ((service.GetState() != kRegistered) && CanAppendService(service))
1280         {
1281             SuccessOrExit(error = AppendServiceInstruction(service, aInfo));
1282 
1283             if (mSingleServiceMode)
1284             {
1285                 // In "single service mode", we allow only one service
1286                 // to be appended in the message.
1287                 break;
1288             }
1289         }
1290     }
1291 
1292     if (!mSingleServiceMode)
1293     {
1294         for (Service &service : mServices)
1295         {
1296             if ((service.GetState() == kRegistered) && CanAppendService(service) && ShouldRenewEarly(service))
1297             {
1298                 // If the lease needs to be renewed or if we are close to the
1299                 // renewal time of a registered service, we refresh the service
1300                 // early and include it in this update. This helps put more
1301                 // services on the same lease refresh schedule.
1302 
1303                 service.SetState(kToRefresh);
1304                 SuccessOrExit(error = AppendServiceInstruction(service, aInfo));
1305             }
1306         }
1307     }
1308 
1309     // `mLease` or `mKeylease` may be determined from the set of
1310     // services included in the message. If they are not yet set we
1311     // use the default intervals.
1312 
1313     mLease    = DetermineLeaseInterval(mLease, mDefaultLease);
1314     mKeyLease = DetermineLeaseInterval(mKeyLease, mDefaultKeyLease);
1315 
1316     // When message only contains removal of a previously registered
1317     // service, then `mKeyLease` is set but `mLease` remains unspecified.
1318     // In such a case, we end up using `mDefaultLease` but then we need
1319     // to make sure it is not greater than the selected `mKeyLease`.
1320 
1321     mLease = Min(mLease, mKeyLease);
1322 
1323 exit:
1324     return error;
1325 }
1326 
CanAppendService(const Service & aService)1327 bool Client::CanAppendService(const Service &aService)
1328 {
1329     // Check the lease intervals associated with `aService` to see if
1330     // it can be included in this message. When removing a service,
1331     // only key lease interval should match. In all other cases, both
1332     // lease and key lease should match. The `mLease` and/or `mKeyLease`
1333     // may be updated if they were unspecified.
1334 
1335     bool     canAppend = false;
1336     uint32_t lease     = DetermineLeaseInterval(aService.GetLease(), mDefaultLease);
1337     uint32_t keyLease  = Max(DetermineLeaseInterval(aService.GetKeyLease(), mDefaultKeyLease), lease);
1338 
1339     switch (aService.GetState())
1340     {
1341     case kToAdd:
1342     case kAdding:
1343     case kToRefresh:
1344     case kRefreshing:
1345     case kRegistered:
1346         VerifyOrExit((mLease == kUnspecifiedInterval) || (mLease == lease));
1347         VerifyOrExit((mKeyLease == kUnspecifiedInterval) || (mKeyLease == keyLease));
1348         mLease    = lease;
1349         mKeyLease = keyLease;
1350         canAppend = true;
1351         break;
1352 
1353     case kToRemove:
1354     case kRemoving:
1355         VerifyOrExit((mKeyLease == kUnspecifiedInterval) || (mKeyLease == keyLease));
1356         mKeyLease = keyLease;
1357         canAppend = true;
1358         break;
1359 
1360     case kRemoved:
1361         break;
1362     }
1363 
1364 exit:
1365     return canAppend;
1366 }
1367 
AppendServiceInstruction(Service & aService,MsgInfo & aInfo)1368 Error Client::AppendServiceInstruction(Service &aService, MsgInfo &aInfo)
1369 {
1370     Error               error    = kErrorNone;
1371     bool                removing = ((aService.GetState() == kToRemove) || (aService.GetState() == kRemoving));
1372     Dns::ResourceRecord rr;
1373     Dns::SrvRecord      srv;
1374     uint16_t            serviceNameOffset;
1375     uint16_t            instanceNameOffset;
1376     uint16_t            offset;
1377 
1378     aService.MarkAsAppendedInMessage();
1379 
1380     //----------------------------------
1381     // Service Discovery Instruction
1382 
1383     // PTR record
1384 
1385     // "service name labels" + (pointer to) domain name.
1386     serviceNameOffset = aInfo.mMessage->GetLength();
1387     SuccessOrExit(error = Dns::Name::AppendMultipleLabels(aService.GetName(), *aInfo.mMessage));
1388     SuccessOrExit(error = Dns::Name::AppendPointerLabel(aInfo.mDomainNameOffset, *aInfo.mMessage));
1389 
1390     // On remove, we use "Delete an RR from an RRSet" where class is set
1391     // to NONE and TTL to zero (RFC 2136 - section 2.5.4).
1392 
1393     rr.Init(Dns::ResourceRecord::kTypePtr, removing ? Dns::PtrRecord::kClassNone : Dns::PtrRecord::kClassInternet);
1394     rr.SetTtl(removing ? 0 : DetermineTtl());
1395     offset = aInfo.mMessage->GetLength();
1396     SuccessOrExit(error = aInfo.mMessage->Append(rr));
1397 
1398     // "Instance name" + (pointer to) service name.
1399     instanceNameOffset = aInfo.mMessage->GetLength();
1400     SuccessOrExit(error = Dns::Name::AppendLabel(aService.GetInstanceName(), *aInfo.mMessage));
1401     SuccessOrExit(error = Dns::Name::AppendPointerLabel(serviceNameOffset, *aInfo.mMessage));
1402 
1403     UpdateRecordLengthInMessage(rr, offset, *aInfo.mMessage);
1404     aInfo.mRecordCount++;
1405 
1406     if (aService.HasSubType() && !removing)
1407     {
1408         const char *subTypeLabel;
1409         uint16_t    subServiceNameOffset = 0;
1410 
1411         for (uint16_t index = 0; (subTypeLabel = aService.GetSubTypeLabelAt(index)) != nullptr; ++index)
1412         {
1413             // subtype label + "_sub" label + (pointer to) service name.
1414 
1415             SuccessOrExit(error = Dns::Name::AppendLabel(subTypeLabel, *aInfo.mMessage));
1416 
1417             if (index == 0)
1418             {
1419                 subServiceNameOffset = aInfo.mMessage->GetLength();
1420                 SuccessOrExit(error = Dns::Name::AppendLabel("_sub", *aInfo.mMessage));
1421                 SuccessOrExit(error = Dns::Name::AppendPointerLabel(serviceNameOffset, *aInfo.mMessage));
1422             }
1423             else
1424             {
1425                 SuccessOrExit(error = Dns::Name::AppendPointerLabel(subServiceNameOffset, *aInfo.mMessage));
1426             }
1427 
1428             // `rr` is already initialized as PTR.
1429             offset = aInfo.mMessage->GetLength();
1430             SuccessOrExit(error = aInfo.mMessage->Append(rr));
1431 
1432             SuccessOrExit(error = Dns::Name::AppendPointerLabel(instanceNameOffset, *aInfo.mMessage));
1433             UpdateRecordLengthInMessage(rr, offset, *aInfo.mMessage);
1434             aInfo.mRecordCount++;
1435         }
1436     }
1437 
1438     //----------------------------------
1439     // Service Description Instruction
1440 
1441     // "Delete all RRsets from a name" for Instance Name.
1442 
1443     SuccessOrExit(error = Dns::Name::AppendPointerLabel(instanceNameOffset, *aInfo.mMessage));
1444     SuccessOrExit(error = AppendDeleteAllRrsets(aInfo));
1445     aInfo.mRecordCount++;
1446 
1447     VerifyOrExit(!removing);
1448 
1449     // SRV RR
1450 
1451     SuccessOrExit(error = Dns::Name::AppendPointerLabel(instanceNameOffset, *aInfo.mMessage));
1452     srv.Init();
1453     srv.SetTtl(DetermineTtl());
1454     srv.SetPriority(aService.GetPriority());
1455     srv.SetWeight(aService.GetWeight());
1456     srv.SetPort(aService.GetPort());
1457     offset = aInfo.mMessage->GetLength();
1458     SuccessOrExit(error = aInfo.mMessage->Append(srv));
1459     SuccessOrExit(error = AppendHostName(aInfo));
1460     UpdateRecordLengthInMessage(srv, offset, *aInfo.mMessage);
1461     aInfo.mRecordCount++;
1462 
1463     // TXT RR
1464 
1465     SuccessOrExit(error = Dns::Name::AppendPointerLabel(instanceNameOffset, *aInfo.mMessage));
1466     rr.Init(Dns::ResourceRecord::kTypeTxt);
1467     offset = aInfo.mMessage->GetLength();
1468     SuccessOrExit(error = aInfo.mMessage->Append(rr));
1469     SuccessOrExit(
1470         error = Dns::TxtEntry::AppendEntries(aService.GetTxtEntries(), aService.GetNumTxtEntries(), *aInfo.mMessage));
1471     UpdateRecordLengthInMessage(rr, offset, *aInfo.mMessage);
1472     aInfo.mRecordCount++;
1473 
1474 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
1475     if (mServiceKeyRecordEnabled)
1476     {
1477         // KEY RR is optional in "Service Description Instruction". It
1478         // is added here under `REFERENCE_DEVICE` config and is intended
1479         // for testing only.
1480 
1481         SuccessOrExit(error = Dns::Name::AppendPointerLabel(instanceNameOffset, *aInfo.mMessage));
1482         SuccessOrExit(error = AppendKeyRecord(aInfo));
1483     }
1484 #endif
1485 
1486 exit:
1487     return error;
1488 }
1489 
AppendHostDescriptionInstruction(MsgInfo & aInfo)1490 Error Client::AppendHostDescriptionInstruction(MsgInfo &aInfo)
1491 {
1492     Error error = kErrorNone;
1493 
1494     //----------------------------------
1495     // Host Description Instruction
1496 
1497     // "Delete all RRsets from a name" for Host Name.
1498 
1499     SuccessOrExit(error = AppendHostName(aInfo));
1500     SuccessOrExit(error = AppendDeleteAllRrsets(aInfo));
1501     aInfo.mRecordCount++;
1502 
1503     // AAAA RRs
1504 
1505     if (mHostInfo.IsAutoAddressEnabled())
1506     {
1507         // Append all preferred addresses on Thread netif excluding link-local
1508         // and mesh-local addresses. If no address is appended, we include
1509         // the mesh local EID.
1510 
1511         mAutoHostAddressCount = 0;
1512 
1513         for (Ip6::Netif::UnicastAddress &unicastAddress : Get<ThreadNetif>().GetUnicastAddresses())
1514         {
1515             if (ShouldHostAutoAddressRegister(unicastAddress))
1516             {
1517                 SuccessOrExit(error = AppendAaaaRecord(unicastAddress.GetAddress(), aInfo));
1518                 unicastAddress.mSrpRegistered = true;
1519                 mAutoHostAddressCount++;
1520             }
1521             else
1522             {
1523                 unicastAddress.mSrpRegistered = false;
1524             }
1525         }
1526 
1527         if (mAutoHostAddressCount == 0)
1528         {
1529             Ip6::Netif::UnicastAddress &mlEid = Get<Mle::Mle>().GetMeshLocalEidUnicastAddress();
1530 
1531             SuccessOrExit(error = AppendAaaaRecord(mlEid.GetAddress(), aInfo));
1532             mlEid.mSrpRegistered = true;
1533             mAutoHostAddressCount++;
1534         }
1535     }
1536     else
1537     {
1538         for (uint8_t index = 0; index < mHostInfo.GetNumAddresses(); index++)
1539         {
1540             SuccessOrExit(error = AppendAaaaRecord(mHostInfo.GetAddress(index), aInfo));
1541         }
1542     }
1543 
1544     // KEY RR
1545 
1546     SuccessOrExit(error = AppendHostName(aInfo));
1547     SuccessOrExit(error = AppendKeyRecord(aInfo));
1548 
1549 exit:
1550     return error;
1551 }
1552 
AppendAaaaRecord(const Ip6::Address & aAddress,MsgInfo & aInfo) const1553 Error Client::AppendAaaaRecord(const Ip6::Address &aAddress, MsgInfo &aInfo) const
1554 {
1555     Error               error;
1556     Dns::ResourceRecord rr;
1557 
1558     rr.Init(Dns::ResourceRecord::kTypeAaaa);
1559     rr.SetTtl(DetermineTtl());
1560     rr.SetLength(sizeof(Ip6::Address));
1561 
1562     SuccessOrExit(error = AppendHostName(aInfo));
1563     SuccessOrExit(error = aInfo.mMessage->Append(rr));
1564     SuccessOrExit(error = aInfo.mMessage->Append(aAddress));
1565     aInfo.mRecordCount++;
1566 
1567 exit:
1568     return error;
1569 }
1570 
AppendKeyRecord(MsgInfo & aInfo) const1571 Error Client::AppendKeyRecord(MsgInfo &aInfo) const
1572 {
1573     Error                          error;
1574     Dns::KeyRecord                 key;
1575     Crypto::Ecdsa::P256::PublicKey publicKey;
1576 
1577     key.Init();
1578     key.SetTtl(DetermineTtl());
1579     key.SetFlags(Dns::KeyRecord::kAuthConfidPermitted, Dns::KeyRecord::kOwnerNonZone,
1580                  Dns::KeyRecord::kSignatoryFlagGeneral);
1581     key.SetProtocol(Dns::KeyRecord::kProtocolDnsSec);
1582     key.SetAlgorithm(Dns::KeyRecord::kAlgorithmEcdsaP256Sha256);
1583     key.SetLength(sizeof(Dns::KeyRecord) - sizeof(Dns::ResourceRecord) + sizeof(Crypto::Ecdsa::P256::PublicKey));
1584     SuccessOrExit(error = aInfo.mMessage->Append(key));
1585     SuccessOrExit(error = aInfo.mKeyInfo.GetPublicKey(publicKey));
1586     SuccessOrExit(error = aInfo.mMessage->Append(publicKey));
1587     aInfo.mRecordCount++;
1588 
1589 exit:
1590     return error;
1591 }
1592 
AppendDeleteAllRrsets(MsgInfo & aInfo) const1593 Error Client::AppendDeleteAllRrsets(MsgInfo &aInfo) const
1594 {
1595     // "Delete all RRsets from a name" (RFC 2136 - 2.5.3)
1596     // Name should be already appended in the message.
1597 
1598     Dns::ResourceRecord rr;
1599 
1600     rr.Init(Dns::ResourceRecord::kTypeAny, Dns::ResourceRecord::kClassAny);
1601     rr.SetTtl(0);
1602     rr.SetLength(0);
1603 
1604     return aInfo.mMessage->Append(rr);
1605 }
1606 
AppendHostName(MsgInfo & aInfo,bool aDoNotCompress) const1607 Error Client::AppendHostName(MsgInfo &aInfo, bool aDoNotCompress) const
1608 {
1609     Error error;
1610 
1611     if (aDoNotCompress)
1612     {
1613         // Uncompressed (canonical form) of host name is used for SIG(0)
1614         // calculation.
1615         SuccessOrExit(error = Dns::Name::AppendMultipleLabels(mHostInfo.GetName(), *aInfo.mMessage));
1616         error = Dns::Name::AppendName(mDomainName, *aInfo.mMessage);
1617         ExitNow();
1618     }
1619 
1620     // If host name was previously added in the message, add it
1621     // compressed as pointer to the previous one. Otherwise,
1622     // append it and remember the offset.
1623 
1624     if (aInfo.mHostNameOffset != MsgInfo::kUnknownOffset)
1625     {
1626         ExitNow(error = Dns::Name::AppendPointerLabel(aInfo.mHostNameOffset, *aInfo.mMessage));
1627     }
1628 
1629     aInfo.mHostNameOffset = aInfo.mMessage->GetLength();
1630     SuccessOrExit(error = Dns::Name::AppendMultipleLabels(mHostInfo.GetName(), *aInfo.mMessage));
1631     error = Dns::Name::AppendPointerLabel(aInfo.mDomainNameOffset, *aInfo.mMessage);
1632 
1633 exit:
1634     return error;
1635 }
1636 
AppendUpdateLeaseOptRecord(MsgInfo & aInfo)1637 Error Client::AppendUpdateLeaseOptRecord(MsgInfo &aInfo)
1638 {
1639     Error            error;
1640     Dns::OptRecord   optRecord;
1641     Dns::LeaseOption leaseOption;
1642     uint16_t         optionSize;
1643 
1644     // Append empty (root domain) as OPT RR name.
1645     SuccessOrExit(error = Dns::Name::AppendTerminator(*aInfo.mMessage));
1646 
1647     // `Init()` sets the type and clears (set to zero) the extended
1648     // Response Code, version and all flags.
1649     optRecord.Init();
1650     optRecord.SetUdpPayloadSize(kUdpPayloadSize);
1651     optRecord.SetDnsSecurityFlag();
1652 
1653 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
1654     if (mUseShortLeaseOption)
1655     {
1656         LogInfo("Test mode - appending short variant of Lease Option");
1657         mKeyLease = mLease;
1658         leaseOption.InitAsShortVariant(mLease);
1659     }
1660     else
1661 #endif
1662     {
1663         leaseOption.InitAsLongVariant(mLease, mKeyLease);
1664     }
1665 
1666     optionSize = static_cast<uint16_t>(leaseOption.GetSize());
1667 
1668     optRecord.SetLength(optionSize);
1669 
1670     SuccessOrExit(error = aInfo.mMessage->Append(optRecord));
1671     error = aInfo.mMessage->AppendBytes(&leaseOption, optionSize);
1672 
1673 exit:
1674     return error;
1675 }
1676 
AppendSignature(MsgInfo & aInfo)1677 Error Client::AppendSignature(MsgInfo &aInfo)
1678 {
1679     Error                          error;
1680     Dns::SigRecord                 sig;
1681     Crypto::Sha256                 sha256;
1682     Crypto::Sha256::Hash           hash;
1683     Crypto::Ecdsa::P256::Signature signature;
1684     uint16_t                       offset;
1685     uint16_t                       len;
1686 
1687     // Prepare SIG RR: TTL, type covered, labels count should be set
1688     // to zero. Since we have no clock, inception and expiration time
1689     // are also set to zero. The RDATA length will be set later (not
1690     // yet known due to variably (and possible compression) of signer's
1691     // name.
1692 
1693     sig.Clear();
1694     sig.Init(Dns::ResourceRecord::kClassAny);
1695     sig.SetAlgorithm(Dns::KeyRecord::kAlgorithmEcdsaP256Sha256);
1696 
1697     // Append the SIG RR with full uncompressed form of the host name
1698     // as the signer's name. This is used for SIG(0) calculation only.
1699     // It will be overwritten with host name compressed.
1700 
1701     offset = aInfo.mMessage->GetLength();
1702     SuccessOrExit(error = aInfo.mMessage->Append(sig));
1703     SuccessOrExit(error = AppendHostName(aInfo, /* aDoNotCompress */ true));
1704 
1705     // Calculate signature (RFC 2931): Calculated over "data" which is
1706     // concatenation of (1) the SIG RR RDATA wire format (including
1707     // the canonical form of the signer's name), entirely omitting the
1708     // signature subfield, (2) DNS query message, including DNS header
1709     // but not UDP/IP header before the header RR counts have been
1710     // adjusted for the inclusion of SIG(0).
1711 
1712     sha256.Start();
1713 
1714     // (1) SIG RR RDATA wire format
1715     len = aInfo.mMessage->GetLength() - offset - sizeof(Dns::ResourceRecord);
1716     sha256.Update(*aInfo.mMessage, offset + sizeof(Dns::ResourceRecord), len);
1717 
1718     // (2) Message from DNS header before SIG
1719     sha256.Update(*aInfo.mMessage, 0, offset);
1720 
1721     sha256.Finish(hash);
1722     SuccessOrExit(error = aInfo.mKeyInfo.Sign(hash, signature));
1723 
1724     // Move back in message and append SIG RR now with compressed host
1725     // name (as signer's name) along with the calculated signature.
1726 
1727     IgnoreError(aInfo.mMessage->SetLength(offset));
1728 
1729     // SIG(0) uses owner name of root (single zero byte).
1730     SuccessOrExit(error = Dns::Name::AppendTerminator(*aInfo.mMessage));
1731 
1732     offset = aInfo.mMessage->GetLength();
1733     SuccessOrExit(error = aInfo.mMessage->Append(sig));
1734     SuccessOrExit(error = AppendHostName(aInfo));
1735     SuccessOrExit(error = aInfo.mMessage->Append(signature));
1736     UpdateRecordLengthInMessage(sig, offset, *aInfo.mMessage);
1737 
1738 exit:
1739     return error;
1740 }
1741 
UpdateRecordLengthInMessage(Dns::ResourceRecord & aRecord,uint16_t aOffset,Message & aMessage) const1742 void Client::UpdateRecordLengthInMessage(Dns::ResourceRecord &aRecord, uint16_t aOffset, Message &aMessage) const
1743 {
1744     // This method is used to calculate an RR DATA length and update
1745     // (rewrite) it in a message. This should be called immediately
1746     // after all the fields in the record are written in the message.
1747     // `aOffset` gives the offset in the message to the start of the
1748     // record.
1749 
1750     aRecord.SetLength(aMessage.GetLength() - aOffset - sizeof(Dns::ResourceRecord));
1751     aMessage.Write(aOffset, aRecord);
1752 }
1753 
HandleUdpReceive(Message & aMessage,const Ip6::MessageInfo & aMessageInfo)1754 void Client::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
1755 {
1756     OT_UNUSED_VARIABLE(aMessageInfo);
1757 
1758     ProcessResponse(aMessage);
1759 }
1760 
ProcessResponse(Message & aMessage)1761 void Client::ProcessResponse(Message &aMessage)
1762 {
1763     static const ItemState kNewStateOnUpdateDone[]{
1764         /* (0) kToAdd      -> */ kToAdd,
1765         /* (1) kAdding     -> */ kRegistered,
1766         /* (2) kToRefresh  -> */ kToRefresh,
1767         /* (3) kRefreshing -> */ kRegistered,
1768         /* (4) kToRemove   -> */ kToRemove,
1769         /* (5) kRemoving   -> */ kRemoved,
1770         /* (6) kRegistered -> */ kRegistered,
1771         /* (7) kRemoved    -> */ kRemoved,
1772     };
1773 
1774     Error               error = kErrorNone;
1775     Dns::UpdateHeader   header;
1776     uint16_t            offset = aMessage.GetOffset();
1777     uint16_t            recordCount;
1778     LinkedList<Service> removedServices;
1779 
1780     switch (GetState())
1781     {
1782     case kStateToUpdate:
1783     case kStateUpdating:
1784     case kStateToRetry:
1785         break;
1786     case kStateStopped:
1787     case kStatePaused:
1788     case kStateUpdated:
1789         ExitNow();
1790     }
1791 
1792     SuccessOrExit(error = aMessage.Read(offset, header));
1793 
1794     VerifyOrExit(header.GetType() == Dns::Header::kTypeResponse, error = kErrorParse);
1795     VerifyOrExit(header.GetQueryType() == Dns::Header::kQueryTypeUpdate, error = kErrorParse);
1796 
1797     VerifyOrExit(IsResponseMessageIdValid(header.GetMessageId()), error = kErrorDrop);
1798     mResponseMessageId = header.GetMessageId() + 1;
1799 
1800     if (!Get<Mle::Mle>().IsRxOnWhenIdle())
1801     {
1802         Get<DataPollSender>().StopFastPolls();
1803     }
1804 
1805     LogInfo("Received response, msg-id:0x%x", header.GetMessageId());
1806 
1807 #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE && OPENTHREAD_CONFIG_SRP_CLIENT_SWITCH_SERVER_ON_FAILURE
1808     mAutoStart.ResetTimeoutFailureCount();
1809 #endif
1810 
1811     error = Dns::Header::ResponseCodeToError(header.GetResponseCode());
1812 
1813     if (error != kErrorNone)
1814     {
1815         LogInfo("Server rejected %s code:%d", ErrorToString(error), header.GetResponseCode());
1816 
1817         if (mHostInfo.GetState() == kAdding)
1818         {
1819             // Since server rejected the update message, we go back to
1820             // `kToAdd` state to allow user to give a new name using
1821             // `SetHostName()`.
1822             mHostInfo.SetState(kToAdd);
1823         }
1824 
1825         // Wait for the timer to expire to retry. Note that timer is
1826         // already scheduled for the current wait interval when state
1827         // was changed to `kStateUpdating`.
1828 
1829         LogRetryWaitInterval();
1830         GrowRetryWaitInterval();
1831         SetState(kStateToRetry);
1832         InvokeCallback(error);
1833 
1834 #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE && OPENTHREAD_CONFIG_SRP_CLIENT_SWITCH_SERVER_ON_FAILURE
1835         if ((error == kErrorDuplicated) || (error == kErrorSecurity))
1836         {
1837             // If the server rejects the update with specific errors
1838             // (indicating duplicate name and/or security error), we
1839             // try to switch the server (we check if another can be
1840             // found in the Network Data).
1841             //
1842             // Note that this is done after invoking the callback and
1843             // notifying the user of the error from server. This works
1844             // correctly even if user makes changes from callback
1845             // (e.g., calls SRP client APIs like `Stop` or disables
1846             // auto-start), since we have a guard check at the top of
1847             // `SelectNextServer()` to verify that client is still
1848             // running and auto-start is enabled and selected the
1849             // server.
1850 
1851             SelectNextServer(/* aDisallowSwitchOnRegisteredHost */ true);
1852         }
1853 #endif
1854         ExitNow(error = kErrorNone);
1855     }
1856 
1857     offset += sizeof(header);
1858 
1859     // Skip over all sections till Additional Data section
1860     // SPEC ENHANCEMENT: Server can echo the request back or not
1861     // include any of RRs. Would be good to explicitly require SRP server
1862     // to not echo back RRs.
1863 
1864     if (header.GetZoneRecordCount() != 0)
1865     {
1866         VerifyOrExit(header.GetZoneRecordCount() == 1, error = kErrorParse);
1867         SuccessOrExit(error = Dns::Name::ParseName(aMessage, offset));
1868         VerifyOrExit(offset + sizeof(Dns::Zone) <= aMessage.GetLength(), error = kErrorParse);
1869         offset += sizeof(Dns::Zone);
1870     }
1871 
1872     // Check for Update Lease OPT RR. This determines the lease
1873     // interval accepted by server. If not present, then use the
1874     // transmitted lease interval from the update request message.
1875 
1876     recordCount =
1877         header.GetPrerequisiteRecordCount() + header.GetUpdateRecordCount() + header.GetAdditionalRecordCount();
1878 
1879     while (recordCount > 0)
1880     {
1881         uint16_t            startOffset = offset;
1882         Dns::ResourceRecord rr;
1883 
1884         SuccessOrExit(error = ReadResourceRecord(aMessage, offset, rr));
1885         recordCount--;
1886 
1887         if (rr.GetType() == Dns::ResourceRecord::kTypeOpt)
1888         {
1889             SuccessOrExit(error = ProcessOptRecord(aMessage, startOffset, static_cast<Dns::OptRecord &>(rr)));
1890         }
1891     }
1892 
1893     // Calculate the lease renew time based on update message tx time
1894     // and the lease time. `kLeaseRenewGuardInterval` is used to
1895     // ensure that we renew the lease before server expires it. In the
1896     // unlikely (but maybe useful for testing) case where the accepted
1897     // lease interval is too short (shorter than twice the guard time)
1898     // we just use half of the accepted lease interval.
1899 
1900     if (mLease > 2 * kLeaseRenewGuardInterval)
1901     {
1902         uint32_t interval = Time::SecToMsec(mLease - kLeaseRenewGuardInterval);
1903 
1904         mLeaseRenewTime += Random::NonCrypto::AddJitter(interval, kLeaseRenewJitter);
1905     }
1906     else
1907     {
1908         mLeaseRenewTime += Time::SecToMsec(mLease) / 2;
1909     }
1910 
1911     for (Service &service : mServices)
1912     {
1913         if ((service.GetState() == kAdding) || (service.GetState() == kRefreshing))
1914         {
1915             service.SetLeaseRenewTime(mLeaseRenewTime);
1916         }
1917     }
1918 
1919     // State changes:
1920     //   kAdding     -> kRegistered
1921     //   kRefreshing -> kRegistered
1922     //   kRemoving   -> kRemoved
1923 
1924     ChangeHostAndServiceStates(kNewStateOnUpdateDone, kForServicesAppendedInMessage);
1925 
1926     HandleUpdateDone();
1927     UpdateState();
1928 
1929 exit:
1930     if (error != kErrorNone)
1931     {
1932         LogInfo("Failed to process response %s", ErrorToString(error));
1933     }
1934 }
1935 
IsResponseMessageIdValid(uint16_t aId) const1936 bool Client::IsResponseMessageIdValid(uint16_t aId) const
1937 {
1938     // Semantically equivalent to `(aId >= mResponseMessageId) && (aId < mNextMessageId)`
1939 
1940     return !SerialNumber::IsLess(aId, mResponseMessageId) && SerialNumber::IsLess(aId, mNextMessageId);
1941 }
1942 
HandleUpdateDone(void)1943 void Client::HandleUpdateDone(void)
1944 {
1945     HostInfo            hostInfoCopy = mHostInfo;
1946     LinkedList<Service> removedServices;
1947 
1948     if (mHostInfo.GetState() == kRemoved)
1949     {
1950         mHostInfo.Clear();
1951     }
1952 
1953     ResetRetryWaitInterval();
1954     SetState(kStateUpdated);
1955 
1956     GetRemovedServices(removedServices);
1957     InvokeCallback(kErrorNone, hostInfoCopy, removedServices.GetHead());
1958 }
1959 
GetRemovedServices(LinkedList<Service> & aRemovedServices)1960 void Client::GetRemovedServices(LinkedList<Service> &aRemovedServices)
1961 {
1962     mServices.RemoveAllMatching(aRemovedServices, kRemoved);
1963 }
1964 
ReadResourceRecord(const Message & aMessage,uint16_t & aOffset,Dns::ResourceRecord & aRecord)1965 Error Client::ReadResourceRecord(const Message &aMessage, uint16_t &aOffset, Dns::ResourceRecord &aRecord)
1966 {
1967     // Reads and skips over a Resource Record (RR) from message at
1968     // given offset. On success, `aOffset` is updated to point to end
1969     // of RR.
1970 
1971     Error error;
1972 
1973     SuccessOrExit(error = Dns::Name::ParseName(aMessage, aOffset));
1974     SuccessOrExit(error = aMessage.Read(aOffset, aRecord));
1975     VerifyOrExit(aOffset + aRecord.GetSize() <= aMessage.GetLength(), error = kErrorParse);
1976     aOffset += static_cast<uint16_t>(aRecord.GetSize());
1977 
1978 exit:
1979     return error;
1980 }
1981 
ProcessOptRecord(const Message & aMessage,uint16_t aOffset,const Dns::OptRecord & aOptRecord)1982 Error Client::ProcessOptRecord(const Message &aMessage, uint16_t aOffset, const Dns::OptRecord &aOptRecord)
1983 {
1984     // Read and process all options (in an OPT RR) from a message.
1985     // The `aOffset` points to beginning of record in `aMessage`.
1986 
1987     Error            error = kErrorNone;
1988     Dns::LeaseOption leaseOption;
1989 
1990     IgnoreError(Dns::Name::ParseName(aMessage, aOffset));
1991     aOffset += sizeof(Dns::OptRecord);
1992 
1993     switch (error = leaseOption.ReadFrom(aMessage, aOffset, aOptRecord.GetLength()))
1994     {
1995     case kErrorNone:
1996         mLease    = Min(leaseOption.GetLeaseInterval(), kMaxLease);
1997         mKeyLease = Min(leaseOption.GetKeyLeaseInterval(), kMaxLease);
1998         break;
1999 
2000     case kErrorNotFound:
2001         // If server does not include a lease option in its response, it
2002         // indicates that it accepted what we requested.
2003         error = kErrorNone;
2004         break;
2005 
2006     default:
2007         ExitNow();
2008     }
2009 
2010 exit:
2011     return error;
2012 }
2013 
UpdateState(void)2014 void Client::UpdateState(void)
2015 {
2016     NextFireTime nextRenewTime;
2017     bool         shouldUpdate = false;
2018 
2019     VerifyOrExit((GetState() != kStateStopped) && (GetState() != kStatePaused));
2020     VerifyOrExit(mHostInfo.GetName() != nullptr);
2021 
2022     // Go through the host info and all the services to check if there
2023     // are any new changes (i.e., anything new to add or remove). This
2024     // is used to determine whether to send an SRP update message or
2025     // not. Also keep track of the earliest renew time among the
2026     // previously registered services. This is used to schedule the
2027     // timer for next refresh.
2028 
2029     switch (mHostInfo.GetState())
2030     {
2031     case kAdding:
2032     case kRefreshing:
2033     case kRemoving:
2034         break;
2035 
2036     case kRegistered:
2037         if (nextRenewTime.GetNow() < mLeaseRenewTime)
2038         {
2039             break;
2040         }
2041 
2042         mHostInfo.SetState(kToRefresh);
2043 
2044         // Fall through
2045 
2046     case kToAdd:
2047     case kToRefresh:
2048         // Make sure we have at least one service and at least one
2049         // host address, otherwise no need to send SRP update message.
2050         // The exception is when removing host info where we allow
2051         // for empty service list.
2052         VerifyOrExit(!mServices.IsEmpty() && (mHostInfo.IsAutoAddressEnabled() || (mHostInfo.GetNumAddresses() > 0)));
2053 
2054         // Fall through
2055 
2056     case kToRemove:
2057         shouldUpdate = true;
2058         break;
2059 
2060     case kRemoved:
2061         ExitNow();
2062     }
2063 
2064     // If host info is being removed, we skip over checking service list
2065     // for new adds (or removes). This handles the situation where while
2066     // remove is ongoing and before we get a response from the server,
2067     // user adds a new service to be registered. We wait for remove to
2068     // finish (receive response from server) before starting with a new
2069     // service adds.
2070 
2071     if (mHostInfo.GetState() != kRemoving)
2072     {
2073         for (Service &service : mServices)
2074         {
2075             switch (service.GetState())
2076             {
2077             case kToAdd:
2078             case kToRefresh:
2079             case kToRemove:
2080                 shouldUpdate = true;
2081                 break;
2082 
2083             case kRegistered:
2084                 if (service.GetLeaseRenewTime() <= nextRenewTime.GetNow())
2085                 {
2086                     service.SetState(kToRefresh);
2087                     shouldUpdate = true;
2088                 }
2089                 else
2090                 {
2091                     nextRenewTime.UpdateIfEarlier(service.GetLeaseRenewTime());
2092                 }
2093 
2094                 break;
2095 
2096             case kAdding:
2097             case kRefreshing:
2098             case kRemoving:
2099             case kRemoved:
2100                 break;
2101             }
2102         }
2103     }
2104 
2105     if (shouldUpdate)
2106     {
2107         SetState(kStateToUpdate);
2108         ExitNow();
2109     }
2110 
2111     if (GetState() == kStateUpdated)
2112     {
2113         mTimer.FireAt(nextRenewTime);
2114     }
2115 
2116 exit:
2117     return;
2118 }
2119 
GrowRetryWaitInterval(void)2120 void Client::GrowRetryWaitInterval(void)
2121 {
2122     mRetryWaitInterval =
2123         mRetryWaitInterval / kRetryIntervalGrowthFactorDenominator * kRetryIntervalGrowthFactorNumerator;
2124     mRetryWaitInterval = Min(mRetryWaitInterval, kMaxRetryWaitInterval);
2125 }
2126 
DetermineLeaseInterval(uint32_t aInterval,uint32_t aDefaultInterval) const2127 uint32_t Client::DetermineLeaseInterval(uint32_t aInterval, uint32_t aDefaultInterval) const
2128 {
2129     // Determine the lease or key lease interval.
2130     //
2131     // We use `aInterval` if it is non-zero, otherwise, use the
2132     // `aDefaultInterval`. We also ensure that the returned value is
2133     // never greater than `kMaxLease`. The `kMaxLease` is selected
2134     // such the lease intervals in msec can still fit in a `uint32_t`
2135     // `Time` variable (`kMaxLease` is ~ 24.8 days).
2136 
2137     return Min(kMaxLease, (aInterval != kUnspecifiedInterval) ? aInterval : aDefaultInterval);
2138 }
2139 
DetermineTtl(void) const2140 uint32_t Client::DetermineTtl(void) const
2141 {
2142     // Determine the TTL to use based on current `mLease`.
2143     // If `mLease == 0`, it indicates we are removing host
2144     // and so we use `mDefaultLease` instead.
2145 
2146     uint32_t lease = (mLease == 0) ? mDefaultLease : mLease;
2147 
2148     return (mTtl == kUnspecifiedInterval) ? lease : Min(mTtl, lease);
2149 }
2150 
ShouldRenewEarly(const Service & aService) const2151 bool Client::ShouldRenewEarly(const Service &aService) const
2152 {
2153     // Check if we reached the service renew time or close to it. The
2154     // "early renew interval" is used to allow early refresh. It is
2155     // calculated as a factor of the service requested lease interval.
2156     // The  "early lease renew factor" is given as a fraction (numerator
2157     // and denominator). If the denominator is set to zero (i.e., factor
2158     // is set to infinity), then service is always included in all SRP
2159     // update messages.
2160 
2161     bool shouldRenew;
2162 
2163 #if OPENTHREAD_CONFIG_SRP_CLIENT_EARLY_LEASE_RENEW_FACTOR_DENOMINATOR != 0
2164     uint32_t earlyRenewInterval;
2165 
2166     earlyRenewInterval = Time::SecToMsec(DetermineLeaseInterval(aService.GetLease(), mDefaultLease));
2167     earlyRenewInterval = earlyRenewInterval / kEarlyLeaseRenewFactorDenominator * kEarlyLeaseRenewFactorNumerator;
2168 
2169     shouldRenew = (aService.GetLeaseRenewTime() <= TimerMilli::GetNow() + earlyRenewInterval);
2170 #else
2171     OT_UNUSED_VARIABLE(aService);
2172     shouldRenew = true;
2173 #endif
2174 
2175     return shouldRenew;
2176 }
2177 
HandleTimer(void)2178 void Client::HandleTimer(void)
2179 {
2180     switch (GetState())
2181     {
2182     case kStateStopped:
2183     case kStatePaused:
2184         break;
2185 
2186     case kStateToUpdate:
2187     case kStateToRetry:
2188         SendUpdate();
2189         break;
2190 
2191     case kStateUpdating:
2192         mSingleServiceMode = false;
2193         LogRetryWaitInterval();
2194         LogInfo("Timed out, no response");
2195         GrowRetryWaitInterval();
2196         SetState(kStateToUpdate);
2197         InvokeCallback(kErrorResponseTimeout);
2198 
2199 #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE && OPENTHREAD_CONFIG_SRP_CLIENT_SWITCH_SERVER_ON_FAILURE
2200 
2201         // After certain number of back-to-back timeout failures, we try
2202         // to switch the server. This is again done after invoking the
2203         // callback. It works correctly due to the guard check at the
2204         // top of `SelectNextServer()`.
2205 
2206         mAutoStart.IncrementTimeoutFailureCount();
2207 
2208         if (mAutoStart.GetTimeoutFailureCount() >= kMaxTimeoutFailuresToSwitchServer)
2209         {
2210             SelectNextServer(kDisallowSwitchOnRegisteredHost);
2211         }
2212 #endif
2213         break;
2214 
2215     case kStateUpdated:
2216         UpdateState();
2217         break;
2218     }
2219 }
2220 
2221 #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
2222 
EnableAutoStartMode(AutoStartCallback aCallback,void * aContext)2223 void Client::EnableAutoStartMode(AutoStartCallback aCallback, void *aContext)
2224 {
2225     mAutoStart.SetCallback(aCallback, aContext);
2226 
2227     VerifyOrExit(mAutoStart.GetState() == AutoStart::kDisabled);
2228 
2229     mAutoStart.SetState(AutoStart::kFirstTimeSelecting);
2230     ApplyAutoStartGuardOnAttach();
2231 
2232     ProcessAutoStart();
2233 
2234 exit:
2235     return;
2236 }
2237 
ApplyAutoStartGuardOnAttach(void)2238 void Client::ApplyAutoStartGuardOnAttach(void)
2239 {
2240     VerifyOrExit(Get<Mle::Mle>().IsAttached());
2241     VerifyOrExit(!IsRunning());
2242     VerifyOrExit(mAutoStart.GetState() == AutoStart::kFirstTimeSelecting);
2243 
2244     // The `mGuardTimer` tracks a guard interval after the attach
2245     // event while `AutoStart` has yet to select a server for the
2246     // first time.
2247     //
2248     // This is used by `ProcessAutoStart()` to apply different TX
2249     // jitter values. If server selection occurs within this short
2250     // window, a shorter TX jitter is used. This typically represents
2251     // the device rebooting or being paired.
2252     //
2253     // The guard time is also checked when handling SLAAC address change
2254     // events, to decide whether or not to request longer TX jitter.
2255 
2256     mGuardTimer.Start(kGuardTimeAfterAttachToUseShorterTxJitter);
2257 
2258 exit:
2259     return;
2260 }
2261 
ProcessAutoStart(void)2262 void Client::ProcessAutoStart(void)
2263 {
2264     Ip6::SockAddr     serverSockAddr;
2265     DnsSrpAnycastInfo anycastInfo;
2266     DnsSrpUnicastInfo unicastInfo;
2267     AutoStart::State  oldAutoStartState = mAutoStart.GetState();
2268     bool              shouldRestart     = false;
2269 
2270     // If auto start mode is enabled, we check the Network Data entries
2271     // to discover and select the preferred SRP server to register with.
2272     // If we currently have a selected server, we ensure that it is
2273     // still present in the Network Data and is still the preferred one.
2274 
2275     VerifyOrExit(mAutoStart.GetState() != AutoStart::kDisabled);
2276 
2277     // If SRP client is running, we check to make sure that auto-start
2278     // did select the current server, and server was not specified by
2279     // user directly.
2280 
2281     if (IsRunning())
2282     {
2283         VerifyOrExit(mAutoStart.HasSelectedServer());
2284     }
2285 
2286     // There are three types of entries in Network Data:
2287     //
2288     // 1) Preferred unicast entries with address included in service data.
2289     // 2) Anycast entries (each having a seq number).
2290     // 3) Unicast entries with address info included in server data.
2291 
2292     serverSockAddr.Clear();
2293 
2294     if (SelectUnicastEntry(NetworkData::Service::kAddrInServiceData, unicastInfo) == kErrorNone)
2295     {
2296         mAutoStart.SetState(AutoStart::kSelectedUnicastPreferred);
2297         serverSockAddr = unicastInfo.mSockAddr;
2298     }
2299     else if (Get<NetworkData::Service::Manager>().FindPreferredDnsSrpAnycastInfo(anycastInfo) == kErrorNone)
2300     {
2301         serverSockAddr.SetAddress(anycastInfo.mAnycastAddress);
2302         serverSockAddr.SetPort(kAnycastServerPort);
2303 
2304         // We check if we are selecting an anycast entry for first
2305         // time, or if the seq number has changed. Even if the
2306         // anycast address remains the same as before, on a seq
2307         // number change, the client still needs to restart to
2308         // re-register its info.
2309 
2310         if ((mAutoStart.GetState() != AutoStart::kSelectedAnycast) ||
2311             (mAutoStart.GetAnycastSeqNum() != anycastInfo.mSequenceNumber))
2312         {
2313             shouldRestart = true;
2314             mAutoStart.SetAnycastSeqNum(anycastInfo.mSequenceNumber);
2315         }
2316 
2317         mAutoStart.SetState(AutoStart::kSelectedAnycast);
2318     }
2319     else if (SelectUnicastEntry(NetworkData::Service::kAddrInServerData, unicastInfo) == kErrorNone)
2320     {
2321         mAutoStart.SetState(AutoStart::kSelectedUnicast);
2322         serverSockAddr = unicastInfo.mSockAddr;
2323     }
2324 
2325     if (IsRunning())
2326     {
2327         VerifyOrExit((GetServerAddress() != serverSockAddr) || shouldRestart);
2328         Stop(kRequesterAuto, kResetRetryInterval);
2329     }
2330 
2331     if (serverSockAddr.GetAddress().IsUnspecified())
2332     {
2333         if (mAutoStart.HasSelectedServer())
2334         {
2335             mAutoStart.SetState(AutoStart::kReselecting);
2336         }
2337 
2338         ExitNow();
2339     }
2340 
2341     // Before calling `Start()`, determine the trigger reason for
2342     // starting the client with the newly discovered server based on
2343     // `AutoStart` state transitions. This reason is then used to
2344     // select the appropriate TX jitter interval (randomizing the
2345     // initial SRP update transmission to the new server).
2346 
2347     switch (oldAutoStartState)
2348     {
2349     case AutoStart::kDisabled:
2350         break;
2351 
2352     case AutoStart::kFirstTimeSelecting:
2353 
2354         // If the device is attaching to an established Thread mesh
2355         // (e.g., after a reboot or pairing), the Network Data it
2356         // receives should already include a server entry, leading to
2357         // a quick server selection after attachment. The `mGuardTimer`,
2358         // started by `ApplyAutoStartGuardOnAttach()`, tracks a guard
2359         // interval after the attach event. If server selection
2360         // occurs within this short window, a shorter TX jitter is
2361         // used (`TxJitter::kOnDeviceReboot`), allowing the device to
2362         // register quickly and become discoverable.
2363         //
2364         // If server discovery takes longer, a longer TX jitter
2365         // is used (`TxJitter::kOnServerStart`). This situation
2366         // can indicate a server/BR starting up or a network-wide
2367         // restart of many nodes (e.g., due to a power outage).
2368 
2369         if (mGuardTimer.IsRunning())
2370         {
2371             mTxJitter.Request(TxJitter::kOnDeviceReboot);
2372         }
2373         else
2374         {
2375             mTxJitter.Request(TxJitter::kOnServerStart);
2376         }
2377 
2378         break;
2379 
2380     case AutoStart::kReselecting:
2381         // Server is restarted (or possibly a new server started).
2382         mTxJitter.Request(TxJitter::kOnServerRestart);
2383         break;
2384 
2385     case AutoStart::kSelectedUnicastPreferred:
2386     case AutoStart::kSelectedAnycast:
2387     case AutoStart::kSelectedUnicast:
2388         mTxJitter.Request(TxJitter::kOnServerSwitch);
2389         break;
2390     }
2391 
2392     IgnoreError(Start(serverSockAddr, kRequesterAuto));
2393 
2394 exit:
2395     return;
2396 }
2397 
SelectUnicastEntry(DnsSrpUnicastType aType,DnsSrpUnicastInfo & aInfo) const2398 Error Client::SelectUnicastEntry(DnsSrpUnicastType aType, DnsSrpUnicastInfo &aInfo) const
2399 {
2400     Error                                   error = kErrorNotFound;
2401     DnsSrpUnicastInfo                       unicastInfo;
2402     NetworkData::Service::Manager::Iterator iterator;
2403 #if OPENTHREAD_CONFIG_SRP_CLIENT_SAVE_SELECTED_SERVER_ENABLE
2404     Settings::SrpClientInfo savedInfo;
2405     bool                    hasSavedServerInfo = false;
2406 
2407     if (!IsRunning())
2408     {
2409         hasSavedServerInfo = (Get<Settings>().Read(savedInfo) == kErrorNone);
2410     }
2411 #endif
2412 
2413     while (Get<NetworkData::Service::Manager>().GetNextDnsSrpUnicastInfo(iterator, aType, unicastInfo) == kErrorNone)
2414     {
2415         bool preferNewEntry;
2416 
2417         if (mAutoStart.HasSelectedServer() && (GetServerAddress() == unicastInfo.mSockAddr))
2418         {
2419             aInfo = unicastInfo;
2420             error = kErrorNone;
2421             ExitNow();
2422         }
2423 
2424 #if OPENTHREAD_CONFIG_SRP_CLIENT_SAVE_SELECTED_SERVER_ENABLE
2425         if (hasSavedServerInfo && (unicastInfo.mSockAddr.GetAddress() == savedInfo.GetServerAddress()) &&
2426             (unicastInfo.mSockAddr.GetPort() == savedInfo.GetServerPort()))
2427         {
2428             // Stop the search if we see a match for the previously
2429             // saved server info in the network data entries.
2430 
2431             aInfo = unicastInfo;
2432             error = kErrorNone;
2433             ExitNow();
2434         }
2435 #endif
2436         // Prefer the server with higher version number, if equal
2437         // then pick the one with numerically smaller IPv6 address.
2438 
2439         preferNewEntry = (error == kErrorNotFound) || (unicastInfo.mVersion > aInfo.mVersion);
2440 
2441         if (!preferNewEntry && (unicastInfo.mVersion == aInfo.mVersion))
2442         {
2443             preferNewEntry = (unicastInfo.mSockAddr.GetAddress() < aInfo.mSockAddr.GetAddress());
2444         }
2445 
2446         if (preferNewEntry)
2447         {
2448             aInfo = unicastInfo;
2449             error = kErrorNone;
2450         }
2451     }
2452 
2453 exit:
2454     return error;
2455 }
2456 
2457 #if OPENTHREAD_CONFIG_SRP_CLIENT_SWITCH_SERVER_ON_FAILURE
SelectNextServer(bool aDisallowSwitchOnRegisteredHost)2458 void Client::SelectNextServer(bool aDisallowSwitchOnRegisteredHost)
2459 {
2460     // This method tries to find the next unicast server info entry in the
2461     // Network Data after the current one selected. If found, it
2462     // restarts the client with the new server (keeping the retry wait
2463     // interval as before).
2464 
2465     Ip6::SockAddr     serverSockAddr;
2466     bool              selectNext = false;
2467     DnsSrpUnicastType type       = NetworkData::Service::kAddrInServiceData;
2468 
2469     serverSockAddr.Clear();
2470 
2471     // Ensure that client is running, auto-start is enabled and
2472     // auto-start selected the server and it is a unicast entry.
2473 
2474     VerifyOrExit(IsRunning());
2475 
2476     switch (mAutoStart.GetState())
2477     {
2478     case AutoStart::kSelectedUnicastPreferred:
2479         type = NetworkData::Service::kAddrInServiceData;
2480         break;
2481 
2482     case AutoStart::kSelectedUnicast:
2483         type = NetworkData::Service::kAddrInServerData;
2484         break;
2485 
2486     case AutoStart::kSelectedAnycast:
2487     case AutoStart::kDisabled:
2488     case AutoStart::kFirstTimeSelecting:
2489     case AutoStart::kReselecting:
2490         ExitNow();
2491     }
2492 
2493     if (aDisallowSwitchOnRegisteredHost)
2494     {
2495         // Ensure that host info is not yet registered (indicating that no
2496         // service has yet been registered either).
2497         VerifyOrExit((mHostInfo.GetState() == kAdding) || (mHostInfo.GetState() == kToAdd));
2498     }
2499 
2500     // We go through all entries to find the one matching the currently
2501     // selected one, then set `selectNext` to `true` so to select the
2502     // next one.
2503 
2504     do
2505     {
2506         DnsSrpUnicastInfo                       unicastInfo;
2507         NetworkData::Service::Manager::Iterator iterator;
2508 
2509         while (Get<NetworkData::Service::Manager>().GetNextDnsSrpUnicastInfo(iterator, type, unicastInfo) == kErrorNone)
2510         {
2511             if (selectNext)
2512             {
2513                 serverSockAddr = unicastInfo.mSockAddr;
2514                 ExitNow();
2515             }
2516 
2517             if (GetServerAddress() == unicastInfo.mSockAddr)
2518             {
2519                 selectNext = true;
2520             }
2521         }
2522 
2523         // We loop back to handle the case where the current entry
2524         // is the last one.
2525 
2526     } while (selectNext);
2527 
2528     // If we reach here it indicates we could not find the entry
2529     // associated with currently selected server in the list. This
2530     // situation is rather unlikely but can still happen if Network
2531     // Data happens to be changed and the entry removed but
2532     // the "changed" event from `Notifier` may have not yet been
2533     // processed (note that events are emitted from their own
2534     // tasklet). In such a case we keep `serverSockAddr` as empty.
2535 
2536 exit:
2537     if (!serverSockAddr.GetAddress().IsUnspecified() && (GetServerAddress() != serverSockAddr))
2538     {
2539         // We specifically update `mHostInfo` to `kToAdd` state. This
2540         // ensures that `Stop()` will keep it as kToAdd` and we detect
2541         // that the host info has not been registered yet and allow the
2542         // `SelectNextServer()` to happen again if the timeouts/failures
2543         // continue to happen with the new server.
2544 
2545         mHostInfo.SetState(kToAdd);
2546         Stop(kRequesterAuto, kKeepRetryInterval);
2547         IgnoreError(Start(serverSockAddr, kRequesterAuto));
2548     }
2549 }
2550 #endif // OPENTHREAD_CONFIG_SRP_CLIENT_SWITCH_SERVER_ON_FAILURE
2551 
2552 #endif // OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
2553 
ItemStateToString(ItemState aState)2554 const char *Client::ItemStateToString(ItemState aState)
2555 {
2556     static const char *const kItemStateStrings[] = {
2557         "ToAdd",      // kToAdd      (0)
2558         "Adding",     // kAdding     (1)
2559         "ToRefresh",  // kToRefresh  (2)
2560         "Refreshing", // kRefreshing (3)
2561         "ToRemove",   // kToRemove   (4)
2562         "Removing",   // kRemoving   (5)
2563         "Registered", // kRegistered (6)
2564         "Removed",    // kRemoved    (7)
2565     };
2566 
2567     return kItemStateStrings[aState];
2568 }
2569 
2570 #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
2571 
StateToString(State aState)2572 const char *Client::StateToString(State aState)
2573 {
2574     static const char *const kStateStrings[] = {
2575         "Stopped",  // kStateStopped  (0)
2576         "Paused",   // kStatePaused   (1)
2577         "ToUpdate", // kStateToUpdate (2)
2578         "Updating", // kStateUpdating (3)
2579         "Updated",  // kStateUpdated  (4)
2580         "ToRetry",  // kStateToRetry  (5)
2581     };
2582 
2583     struct EnumCheck
2584     {
2585         InitEnumValidatorCounter();
2586         ValidateNextEnum(kStateStopped);
2587         ValidateNextEnum(kStatePaused);
2588         ValidateNextEnum(kStateToUpdate);
2589         ValidateNextEnum(kStateUpdating);
2590         ValidateNextEnum(kStateUpdated);
2591         ValidateNextEnum(kStateToRetry);
2592     };
2593 
2594     return kStateStrings[aState];
2595 }
2596 
LogRetryWaitInterval(void) const2597 void Client::LogRetryWaitInterval(void) const
2598 {
2599     constexpr uint16_t kLogInMsecLimit = 5000; // Max interval (in msec) to log the value in msec unit
2600 
2601     uint32_t interval = GetRetryWaitInterval();
2602 
2603     LogInfo("Retry interval %lu %s", ToUlong((interval < kLogInMsecLimit) ? interval : Time::MsecToSec(interval)),
2604             (interval < kLogInMsecLimit) ? "ms" : "sec");
2605 }
2606 
2607 #endif // #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
2608 
2609 } // namespace Srp
2610 } // namespace ot
2611 
2612 #endif // OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE
2613