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