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/instance.hpp"
37 #include "common/locator_getters.hpp"
38 #include "common/random.hpp"
39 #include "common/settings.hpp"
40 #include "common/string.hpp"
41
42 /**
43 * @file
44 * This file implements the SRP client.
45 */
46
47 namespace ot {
48 namespace Srp {
49
50 RegisterLogModule("SrpClient");
51
52 //---------------------------------------------------------------------
53 // Client::HostInfo
54
Init(void)55 void Client::HostInfo::Init(void)
56 {
57 Clearable<HostInfo>::Clear();
58
59 // State is directly set on `mState` instead of using `SetState()`
60 // to avoid logging.
61 mState = OT_SRP_CLIENT_ITEM_STATE_REMOVED;
62 }
63
Clear(void)64 void Client::HostInfo::Clear(void)
65 {
66 Clearable<HostInfo>::Clear();
67 SetState(kRemoved);
68 }
69
SetState(ItemState aState)70 void Client::HostInfo::SetState(ItemState aState)
71 {
72 if (aState != GetState())
73 {
74 LogInfo("HostInfo %s -> %s", ItemStateToString(GetState()), ItemStateToString(aState));
75 mState = MapEnum(aState);
76 }
77 }
78
EnableAutoAddress(void)79 void Client::HostInfo::EnableAutoAddress(void)
80 {
81 mAddresses = nullptr;
82 mNumAddresses = 0;
83 mAutoAddress = true;
84
85 LogInfo("HostInfo enabled auto address", GetNumAddresses());
86 }
87
SetAddresses(const Ip6::Address * aAddresses,uint8_t aNumAddresses)88 void Client::HostInfo::SetAddresses(const Ip6::Address *aAddresses, uint8_t aNumAddresses)
89 {
90 mAddresses = aAddresses;
91 mNumAddresses = aNumAddresses;
92 mAutoAddress = false;
93
94 LogInfo("HostInfo set %d addrs", GetNumAddresses());
95
96 for (uint8_t index = 0; index < GetNumAddresses(); index++)
97 {
98 LogInfo("%s", GetAddress(index).ToString().AsCString());
99 }
100 }
101
102 //---------------------------------------------------------------------
103 // Client::Service
104
Init(void)105 Error Client::Service::Init(void)
106 {
107 Error error = kErrorNone;
108
109 VerifyOrExit((GetName() != nullptr) && (GetInstanceName() != nullptr), error = kErrorInvalidArgs);
110 VerifyOrExit((GetTxtEntries() != nullptr) || (GetNumTxtEntries() == 0), error = kErrorInvalidArgs);
111
112 // State is directly set on `mState` instead of using `SetState()`
113 // to avoid logging.
114 mState = OT_SRP_CLIENT_ITEM_STATE_REMOVED;
115
116 exit:
117 return error;
118 }
119
SetState(ItemState aState)120 void Client::Service::SetState(ItemState aState)
121 {
122 VerifyOrExit(GetState() != aState);
123
124 LogInfo("Service %s -> %s, \"%s\" \"%s\"", ItemStateToString(GetState()), ItemStateToString(aState),
125 GetInstanceName(), GetName());
126
127 if (aState == kToAdd)
128 {
129 constexpr uint16_t kSubTypeLabelStringSize = 80;
130
131 String<kSubTypeLabelStringSize> string;
132
133 // Log more details only when entering `kToAdd` state.
134
135 if (HasSubType())
136 {
137 const char *label;
138
139 for (uint16_t index = 0; (label = GetSubTypeLabelAt(index)) != nullptr; index++)
140 {
141 string.Append("%s\"%s\"", (index != 0) ? ", " : "", label);
142 }
143 }
144
145 LogInfo("subtypes:[%s] port:%d weight:%d prio:%d txts:%d", string.AsCString(), GetPort(), GetWeight(),
146 GetPriority(), GetNumTxtEntries());
147 }
148
149 mState = MapEnum(aState);
150
151 exit:
152 return;
153 }
154
Matches(const Service & aOther) const155 bool Client::Service::Matches(const Service &aOther) const
156 {
157 // This method indicates whether or not two service entries match,
158 // i.e., have the same service and instance names. This is intended
159 // for use by `LinkedList::FindMatching()` to search within the
160 // `mServices` list.
161
162 return (strcmp(GetName(), aOther.GetName()) == 0) && (strcmp(GetInstanceName(), aOther.GetInstanceName()) == 0);
163 }
164
165 //---------------------------------------------------------------------
166 // Client::AutoStart
167
168 #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
169
AutoStart(void)170 Client::AutoStart::AutoStart(void)
171 {
172 Clear();
173 mState = kDefaultMode ? kSelectedNone : kDisabled;
174 }
175
HasSelectedServer(void) const176 bool Client::AutoStart::HasSelectedServer(void) const
177 {
178 bool hasSelected = false;
179
180 switch (mState)
181 {
182 case kDisabled:
183 case kSelectedNone:
184 break;
185
186 case kSelectedUnicastPreferred:
187 case kSelectedUnicast:
188 case kSelectedAnycast:
189 hasSelected = true;
190 break;
191 }
192
193 return hasSelected;
194 }
195
SetState(State aState)196 void Client::AutoStart::SetState(State aState)
197 {
198 if (mState != aState)
199 {
200 LogInfo("AutoStartState %s -> %s", StateToString(mState), StateToString(aState));
201 mState = aState;
202 }
203 }
204
SetCallback(AutoStartCallback aCallback,void * aContext)205 void Client::AutoStart::SetCallback(AutoStartCallback aCallback, void *aContext)
206 {
207 mCallback = aCallback;
208 mContext = aContext;
209 }
210
InvokeCallback(const Ip6::SockAddr * aServerSockAddr) const211 void Client::AutoStart::InvokeCallback(const Ip6::SockAddr *aServerSockAddr) const
212 {
213 if (mCallback != nullptr)
214 {
215 mCallback(aServerSockAddr, mContext);
216 }
217 }
218
219 #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
StateToString(State aState)220 const char *Client::AutoStart::StateToString(State aState)
221 {
222 static const char *const kStateStrings[] = {
223 "Disabled", // (0) kDisabled
224 "Idle", // (1) kSelectedNone
225 "Unicast-prf", // (2) kSelectedUnicastPreferred
226 "Anycast", // (3) kSelectedAnycast
227 "Unicast", // (4) kSelectedUnicast
228 };
229
230 static_assert(0 == kDisabled, "kDisabled value is incorrect");
231 static_assert(1 == kSelectedNone, "kSelectedNone value is incorrect");
232 static_assert(2 == kSelectedUnicastPreferred, "kSelectedUnicastPreferred value is incorrect");
233 static_assert(3 == kSelectedAnycast, "kSelectedAnycast value is incorrect");
234 static_assert(4 == kSelectedUnicast, "kSelectedUnicast value is incorrect");
235
236 return kStateStrings[aState];
237 }
238 #endif
239
240 #endif // OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
241
242 //---------------------------------------------------------------------
243 // Client
244
245 const char Client::kDefaultDomainName[] = "default.service.arpa";
246
Client(Instance & aInstance)247 Client::Client(Instance &aInstance)
248 : InstanceLocator(aInstance)
249 , mState(kStateStopped)
250 , mTxFailureRetryCount(0)
251 , mShouldRemoveKeyLease(false)
252 , mAutoHostAddressAddedMeshLocal(false)
253 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
254 , mServiceKeyRecordEnabled(false)
255 #endif
256 , mUpdateMessageId(0)
257 , mRetryWaitInterval(kMinRetryWaitInterval)
258 , mAcceptedLeaseInterval(0)
259 , mTtl(0)
260 , mLeaseInterval(kDefaultLease)
261 , mKeyLeaseInterval(kDefaultKeyLease)
262 , mSocket(aInstance)
263 , mCallback(nullptr)
264 , mCallbackContext(nullptr)
265 , mDomainName(kDefaultDomainName)
266 , mTimer(aInstance, Client::HandleTimer)
267 {
268 mHostInfo.Init();
269
270 // The `Client` implementation uses different constant array of
271 // `ItemState` to define transitions between states in `Pause()`,
272 // `Stop()`, `SendUpdate`, and `ProcessResponse()`, or to convert
273 // an `ItemState` to string. Here, we assert that the enumeration
274 // values are correct.
275
276 static_assert(kToAdd == 0, "kToAdd value is not correct");
277 static_assert(kAdding == 1, "kAdding value is not correct");
278 static_assert(kToRefresh == 2, "kToRefresh value is not correct");
279 static_assert(kRefreshing == 3, "kRefreshing value is not correct");
280 static_assert(kToRemove == 4, "kToRemove value is not correct");
281 static_assert(kRemoving == 5, "kRemoving value is not correct");
282 static_assert(kRegistered == 6, "kRegistered value is not correct");
283 static_assert(kRemoved == 7, "kRemoved value is not correct");
284 }
285
Start(const Ip6::SockAddr & aServerSockAddr,Requester aRequester)286 Error Client::Start(const Ip6::SockAddr &aServerSockAddr, Requester aRequester)
287 {
288 Error error;
289
290 VerifyOrExit(GetState() == kStateStopped,
291 error = (aServerSockAddr == GetServerAddress()) ? kErrorNone : kErrorBusy);
292
293 SuccessOrExit(error = mSocket.Open(Client::HandleUdpReceive, this));
294 SuccessOrExit(error = mSocket.Connect(aServerSockAddr));
295
296 LogInfo("%starting, server %s", (aRequester == kRequesterUser) ? "S" : "Auto-s",
297 aServerSockAddr.ToString().AsCString());
298
299 Resume();
300
301 #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
302 if (aRequester == kRequesterAuto)
303 {
304 #if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE && OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVER_ADDRESS_AUTO_SET_ENABLE
305 Get<Dns::Client>().UpdateDefaultConfigAddress();
306 #endif
307 mAutoStart.InvokeCallback(&aServerSockAddr);
308 }
309 #endif
310
311 exit:
312 return error;
313 }
314
Stop(Requester aRequester,StopMode aMode)315 void Client::Stop(Requester aRequester, StopMode aMode)
316 {
317 // Change the state of host info and services so that they are
318 // added/removed again once the client is started back. In the
319 // case of `kAdding`, we intentionally move to `kToRefresh`
320 // instead of `kToAdd` since the server may receive our add
321 // request and the item may be registered on the server. This
322 // ensures that if we are later asked to remove the item, we do
323 // notify server.
324
325 static const ItemState kNewStateOnStop[]{
326 /* (0) kToAdd -> */ kToAdd,
327 /* (1) kAdding -> */ kToRefresh,
328 /* (2) kToRefresh -> */ kToRefresh,
329 /* (3) kRefreshing -> */ kToRefresh,
330 /* (4) kToRemove -> */ kToRemove,
331 /* (5) kRemoving -> */ kToRemove,
332 /* (6) kRegistered -> */ kToRefresh,
333 /* (7) kRemoved -> */ kRemoved,
334 };
335
336 VerifyOrExit(GetState() != kStateStopped);
337
338 mSingleServiceMode.Disable();
339
340 // State changes:
341 // kAdding -> kToRefresh
342 // kRefreshing -> kToRefresh
343 // kRemoving -> kToRemove
344 // kRegistered -> kToRefresh
345
346 ChangeHostAndServiceStates(kNewStateOnStop);
347
348 IgnoreError(mSocket.Close());
349
350 mShouldRemoveKeyLease = false;
351 mTxFailureRetryCount = 0;
352
353 if (aMode == kResetRetryInterval)
354 {
355 ResetRetryWaitInterval();
356 }
357
358 SetState(kStateStopped);
359
360 #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
361 #if OPENTHREAD_CONFIG_SRP_CLIENT_SWITCH_SERVER_ON_FAILURE
362 mAutoStart.ResetTimoutFailureCount();
363 #endif
364 if (aRequester == kRequesterAuto)
365 {
366 mAutoStart.InvokeCallback(nullptr);
367 }
368 #endif
369
370 exit:
371 #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
372 if (aRequester == kRequesterUser)
373 {
374 DisableAutoStartMode();
375 }
376 #endif
377 }
378
SetCallback(Callback aCallback,void * aContext)379 void Client::SetCallback(Callback aCallback, void *aContext)
380 {
381 mCallback = aCallback;
382 mCallbackContext = aContext;
383 }
384
Resume(void)385 void Client::Resume(void)
386 {
387 SetState(kStateUpdated);
388 UpdateState();
389 }
390
Pause(void)391 void Client::Pause(void)
392 {
393 // Change the state of host info and services that are are being
394 // added or removed so that they are added/removed again once the
395 // client is resumed or started back.
396
397 static const ItemState kNewStateOnPause[]{
398 /* (0) kToAdd -> */ kToAdd,
399 /* (1) kAdding -> */ kToRefresh,
400 /* (2) kToRefresh -> */ kToRefresh,
401 /* (3) kRefreshing -> */ kToRefresh,
402 /* (4) kToRemove -> */ kToRemove,
403 /* (5) kRemoving -> */ kToRemove,
404 /* (6) kRegistered -> */ kRegistered,
405 /* (7) kRemoved -> */ kRemoved,
406 };
407
408 mSingleServiceMode.Disable();
409
410 // State changes:
411 // kAdding -> kToRefresh
412 // kRefreshing -> kToRefresh
413 // kRemoving -> kToRemove
414
415 ChangeHostAndServiceStates(kNewStateOnPause);
416
417 SetState(kStatePaused);
418 }
419
HandleNotifierEvents(Events aEvents)420 void Client::HandleNotifierEvents(Events aEvents)
421 {
422 if (aEvents.Contains(kEventThreadRoleChanged))
423 {
424 HandleRoleChanged();
425 }
426
427 #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
428 if (aEvents.ContainsAny(kEventThreadNetdataChanged | kEventThreadMeshLocalAddrChanged))
429 {
430 ProcessAutoStart();
431 }
432 #endif
433
434 if (mHostInfo.IsAutoAddressEnabled())
435 {
436 Events::Flags eventFlags = (kEventIp6AddressAdded | kEventIp6AddressRemoved);
437
438 if (mAutoHostAddressAddedMeshLocal)
439 {
440 eventFlags |= kEventThreadMeshLocalAddrChanged;
441 }
442
443 if (aEvents.ContainsAny(eventFlags))
444 {
445 IgnoreError(UpdateHostInfoStateOnAddressChange());
446 UpdateState();
447 }
448 }
449 }
450
HandleRoleChanged(void)451 void Client::HandleRoleChanged(void)
452 {
453 if (Get<Mle::Mle>().IsAttached())
454 {
455 VerifyOrExit(GetState() == kStatePaused);
456 Resume();
457 }
458 else
459 {
460 VerifyOrExit(GetState() != kStateStopped);
461 Pause();
462 }
463
464 exit:
465 return;
466 }
467
468 #if OPENTHREAD_CONFIG_SRP_CLIENT_DOMAIN_NAME_API_ENABLE
SetDomainName(const char * aName)469 Error Client::SetDomainName(const char *aName)
470 {
471 Error error = kErrorNone;
472
473 VerifyOrExit((mHostInfo.GetState() == kToAdd) || (mHostInfo.GetState() == kRemoved), error = kErrorInvalidState);
474
475 mDomainName = (aName != nullptr) ? aName : kDefaultDomainName;
476 LogInfo("Domain name \"%s\"", mDomainName);
477
478 exit:
479 return error;
480 }
481 #endif
482
SetHostName(const char * aName)483 Error Client::SetHostName(const char *aName)
484 {
485 Error error = kErrorNone;
486
487 VerifyOrExit(aName != nullptr, error = kErrorInvalidArgs);
488
489 VerifyOrExit((mHostInfo.GetState() == kToAdd) || (mHostInfo.GetState() == kRemoved), error = kErrorInvalidState);
490
491 LogInfo("Host name \"%s\"", aName);
492 mHostInfo.SetName(aName);
493 mHostInfo.SetState(kToAdd);
494 UpdateState();
495
496 exit:
497 return error;
498 }
499
EnableAutoHostAddress(void)500 Error Client::EnableAutoHostAddress(void)
501 {
502 Error error = kErrorNone;
503
504 VerifyOrExit(!mHostInfo.IsAutoAddressEnabled());
505 SuccessOrExit(error = UpdateHostInfoStateOnAddressChange());
506
507 mHostInfo.EnableAutoAddress();
508 UpdateState();
509
510 exit:
511 return error;
512 }
513
SetHostAddresses(const Ip6::Address * aAddresses,uint8_t aNumAddresses)514 Error Client::SetHostAddresses(const Ip6::Address *aAddresses, uint8_t aNumAddresses)
515 {
516 Error error = kErrorNone;
517
518 VerifyOrExit((aAddresses != nullptr) && (aNumAddresses > 0), error = kErrorInvalidArgs);
519 SuccessOrExit(error = UpdateHostInfoStateOnAddressChange());
520
521 mHostInfo.SetAddresses(aAddresses, aNumAddresses);
522 UpdateState();
523
524 exit:
525 return error;
526 }
527
UpdateHostInfoStateOnAddressChange(void)528 Error Client::UpdateHostInfoStateOnAddressChange(void)
529 {
530 Error error = kErrorNone;
531
532 VerifyOrExit((mHostInfo.GetState() != kToRemove) && (mHostInfo.GetState() != kRemoving),
533 error = kErrorInvalidState);
534
535 if (mHostInfo.GetState() == kRemoved)
536 {
537 mHostInfo.SetState(kToAdd);
538 }
539 else if (mHostInfo.GetState() != kToAdd)
540 {
541 mHostInfo.SetState(kToRefresh);
542 }
543
544 exit:
545 return error;
546 }
547
AddService(Service & aService)548 Error Client::AddService(Service &aService)
549 {
550 Error error;
551
552 VerifyOrExit(mServices.FindMatching(aService) == nullptr, error = kErrorAlready);
553
554 SuccessOrExit(error = aService.Init());
555 mServices.Push(aService);
556
557 aService.SetState(kToAdd);
558 UpdateState();
559
560 exit:
561 return error;
562 }
563
RemoveService(Service & aService)564 Error Client::RemoveService(Service &aService)
565 {
566 Error error = kErrorNone;
567 LinkedList<Service> removedServices;
568
569 VerifyOrExit(mServices.Contains(aService), error = kErrorNotFound);
570
571 UpdateServiceStateToRemove(aService);
572
573 // Check if the service was removed immediately, if so
574 // invoke the callback to report the removed service.
575 GetRemovedServices(removedServices);
576
577 if (!removedServices.IsEmpty())
578 {
579 InvokeCallback(kErrorNone, mHostInfo, removedServices.GetHead());
580 }
581
582 UpdateState();
583
584 exit:
585 return error;
586 }
587
UpdateServiceStateToRemove(Service & aService)588 void Client::UpdateServiceStateToRemove(Service &aService)
589 {
590 if (aService.GetState() == kToAdd)
591 {
592 // If the service has not been added yet, we can remove it immediately.
593 aService.SetState(kRemoved);
594 }
595 else if (aService.GetState() != kRemoving)
596 {
597 aService.SetState(kToRemove);
598 }
599 }
600
ClearService(Service & aService)601 Error Client::ClearService(Service &aService)
602 {
603 Error error;
604
605 SuccessOrExit(error = mServices.Remove(aService));
606 aService.SetNext(nullptr);
607 aService.SetState(kRemoved);
608 UpdateState();
609
610 exit:
611 return error;
612 }
613
RemoveHostAndServices(bool aShouldRemoveKeyLease,bool aSendUnregToServer)614 Error Client::RemoveHostAndServices(bool aShouldRemoveKeyLease, bool aSendUnregToServer)
615 {
616 Error error = kErrorNone;
617
618 LogInfo("Remove host & services");
619
620 VerifyOrExit(mHostInfo.GetState() != kRemoved, error = kErrorAlready);
621
622 if ((mHostInfo.GetState() == kToRemove) || (mHostInfo.GetState() == kRemoving))
623 {
624 // Host info remove is already ongoing, if "key lease" remove mode is
625 // the same, there is no need to send a new update message.
626 VerifyOrExit(mShouldRemoveKeyLease != aShouldRemoveKeyLease);
627 }
628
629 mShouldRemoveKeyLease = aShouldRemoveKeyLease;
630
631 for (Service &service : mServices)
632 {
633 UpdateServiceStateToRemove(service);
634 }
635
636 if ((mHostInfo.GetState() == kToAdd) && !aSendUnregToServer)
637 {
638 // Host info is not added yet (not yet registered with
639 // server), so we can remove it and all services immediately.
640 mHostInfo.SetState(kRemoved);
641 HandleUpdateDone();
642 ExitNow();
643 }
644
645 mHostInfo.SetState(kToRemove);
646 UpdateState();
647
648 exit:
649 return error;
650 }
651
ClearHostAndServices(void)652 void Client::ClearHostAndServices(void)
653 {
654 LogInfo("Clear host & services");
655
656 switch (GetState())
657 {
658 case kStateStopped:
659 case kStatePaused:
660 break;
661
662 case kStateToUpdate:
663 case kStateUpdating:
664 case kStateUpdated:
665 case kStateToRetry:
666 SetState(kStateUpdated);
667 break;
668 }
669
670 mTxFailureRetryCount = 0;
671 ResetRetryWaitInterval();
672
673 mServices.Clear();
674 mHostInfo.Clear();
675 }
676
SetState(State aState)677 void Client::SetState(State aState)
678 {
679 VerifyOrExit(aState != mState);
680
681 LogInfo("State %s -> %s", StateToString(mState), StateToString(aState));
682 mState = aState;
683
684 switch (mState)
685 {
686 case kStateStopped:
687 case kStatePaused:
688 case kStateUpdated:
689 mTimer.Stop();
690 break;
691
692 case kStateToUpdate:
693 mTimer.Start(Random::NonCrypto::GetUint32InRange(kUpdateTxMinDelay, kUpdateTxMaxDelay));
694 break;
695
696 case kStateUpdating:
697 mTimer.Start(GetRetryWaitInterval());
698 break;
699
700 case kStateToRetry:
701 break;
702 }
703 exit:
704 return;
705 }
706
ChangeHostAndServiceStates(const ItemState * aNewStates)707 void Client::ChangeHostAndServiceStates(const ItemState *aNewStates)
708 {
709 #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE && OPENTHREAD_CONFIG_SRP_CLIENT_SAVE_SELECTED_SERVER_ENABLE
710 ItemState oldHostState = mHostInfo.GetState();
711 #endif
712
713 mHostInfo.SetState(aNewStates[mHostInfo.GetState()]);
714
715 for (Service &service : mServices)
716 {
717 if (mSingleServiceMode.IsEnabled() && mSingleServiceMode.GetService() != &service)
718 {
719 continue;
720 }
721
722 service.SetState(aNewStates[service.GetState()]);
723 }
724
725 #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE && OPENTHREAD_CONFIG_SRP_CLIENT_SAVE_SELECTED_SERVER_ENABLE
726 if ((oldHostState != kRegistered) && (mHostInfo.GetState() == kRegistered))
727 {
728 Settings::SrpClientInfo info;
729
730 switch (mAutoStart.GetState())
731 {
732 case AutoStart::kDisabled:
733 case AutoStart::kSelectedNone:
734 break;
735
736 case AutoStart::kSelectedUnicastPreferred:
737 case AutoStart::kSelectedUnicast:
738 info.SetServerAddress(GetServerAddress().GetAddress());
739 info.SetServerPort(GetServerAddress().GetPort());
740 IgnoreError(Get<Settings>().Save(info));
741 break;
742
743 case AutoStart::kSelectedAnycast:
744 IgnoreError(Get<Settings>().Delete<Settings::SrpClientInfo>());
745 break;
746 }
747 }
748 #endif // OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE && OPENTHREAD_CONFIG_SRP_CLIENT_SAVE_SELECTED_SERVER_ENABLE
749 }
750
InvokeCallback(Error aError) const751 void Client::InvokeCallback(Error aError) const
752 {
753 InvokeCallback(aError, mHostInfo, nullptr);
754 }
755
InvokeCallback(Error aError,const HostInfo & aHostInfo,const Service * aRemovedServices) const756 void Client::InvokeCallback(Error aError, const HostInfo &aHostInfo, const Service *aRemovedServices) const
757 {
758 VerifyOrExit(mCallback != nullptr);
759 mCallback(aError, &aHostInfo, mServices.GetHead(), aRemovedServices, mCallbackContext);
760
761 exit:
762 return;
763 }
764
SendUpdate(void)765 void Client::SendUpdate(void)
766 {
767 static const ItemState kNewStateOnMessageTx[]{
768 /* (0) kToAdd -> */ kAdding,
769 /* (1) kAdding -> */ kAdding,
770 /* (2) kToRefresh -> */ kRefreshing,
771 /* (3) kRefreshing -> */ kRefreshing,
772 /* (4) kToRemove -> */ kRemoving,
773 /* (5) kRemoving -> */ kRemoving,
774 /* (6) kRegistered -> */ kRegistered,
775 /* (7) kRemoved -> */ kRemoved,
776 };
777
778 Error error = kErrorNone;
779 Message *message = mSocket.NewMessage(0);
780 uint32_t length;
781
782 VerifyOrExit(message != nullptr, error = kErrorNoBufs);
783 SuccessOrExit(error = PrepareUpdateMessage(*message));
784
785 length = message->GetLength() + sizeof(Ip6::Udp::Header) + sizeof(Ip6::Header);
786
787 if (length >= Ip6::kMaxDatagramLength)
788 {
789 LogInfo("Msg len %u is larger than MTU, enabling single service mode", length);
790 mSingleServiceMode.Enable();
791 IgnoreError(message->SetLength(0));
792 SuccessOrExit(error = PrepareUpdateMessage(*message));
793 }
794
795 SuccessOrExit(error = mSocket.SendTo(*message, Ip6::MessageInfo()));
796
797 LogInfo("Send update");
798
799 // State changes:
800 // kToAdd -> kAdding
801 // kToRefresh -> kRefreshing
802 // kToRemove -> kRemoving
803
804 ChangeHostAndServiceStates(kNewStateOnMessageTx);
805
806 // Remember the update message tx time to use later to determine the
807 // lease renew time.
808 mLeaseRenewTime = TimerMilli::GetNow();
809 mTxFailureRetryCount = 0;
810
811 SetState(kStateUpdating);
812
813 if (!Get<Mle::Mle>().IsRxOnWhenIdle())
814 {
815 // If device is sleepy send fast polls while waiting for
816 // the response from server.
817 Get<DataPollSender>().SendFastPolls(kFastPollsAfterUpdateTx);
818 }
819
820 exit:
821 if (error != kErrorNone)
822 {
823 // If there is an error in preparation or transmission of the
824 // update message (e.g., no buffer to allocate message), up to
825 // `kMaxTxFailureRetries` times, we wait for a short interval
826 // `kTxFailureRetryInterval` and try again. After this, we
827 // continue to retry using the `mRetryWaitInterval` (which keeps
828 // growing on each failure).
829
830 LogInfo("Failed to send update: %s", ErrorToString(error));
831
832 mSingleServiceMode.Disable();
833 FreeMessage(message);
834
835 SetState(kStateToRetry);
836
837 if (mTxFailureRetryCount < kMaxTxFailureRetries)
838 {
839 uint32_t interval;
840
841 mTxFailureRetryCount++;
842 interval = Random::NonCrypto::AddJitter(kTxFailureRetryInterval, kTxFailureRetryJitter);
843 mTimer.Start(interval);
844
845 LogInfo("Quick retry %d in %u msec", mTxFailureRetryCount, interval);
846
847 // Do not report message preparation errors to user
848 // until `kMaxTxFailureRetries` are exhausted.
849 }
850 else
851 {
852 LogRetryWaitInterval();
853 mTimer.Start(Random::NonCrypto::AddJitter(GetRetryWaitInterval(), kRetryIntervalJitter));
854 GrowRetryWaitInterval();
855 InvokeCallback(error);
856 }
857 }
858 }
859
PrepareUpdateMessage(Message & aMessage)860 Error Client::PrepareUpdateMessage(Message &aMessage)
861 {
862 constexpr uint16_t kHeaderOffset = 0;
863
864 Error error = kErrorNone;
865 Dns::UpdateHeader header;
866 Info info;
867
868 info.Clear();
869
870 SuccessOrExit(error = ReadOrGenerateKey(info.mKeyPair));
871
872 // Generate random Message ID and ensure it is different from last one
873 do
874 {
875 SuccessOrExit(error = header.SetRandomMessageId());
876 } while (header.GetMessageId() == mUpdateMessageId);
877
878 mUpdateMessageId = header.GetMessageId();
879
880 // SRP Update (DNS Update) message must have exactly one record in
881 // Zone section, no records in Prerequisite Section, can have
882 // multiple records in Update Section (tracked as they are added),
883 // and two records in Additional Data Section (OPT and SIG records).
884 // The SIG record itself should not be included in calculation of
885 // SIG(0) signature, so the addition record count is set to one
886 // here. After signature calculation and appending of SIG record,
887 // the additional record count is updated to two and the header is
888 // rewritten in the message.
889
890 header.SetZoneRecordCount(1);
891 header.SetAdditionalRecordCount(1);
892 SuccessOrExit(error = aMessage.Append(header));
893
894 // Prepare Zone section
895
896 info.mDomainNameOffset = aMessage.GetLength();
897 SuccessOrExit(error = Dns::Name::AppendName(mDomainName, aMessage));
898 SuccessOrExit(error = aMessage.Append(Dns::Zone()));
899
900 // Prepare Update section
901
902 if ((mHostInfo.GetState() != kToRemove) && (mHostInfo.GetState() != kRemoving))
903 {
904 for (Service &service : mServices)
905 {
906 SuccessOrExit(error = AppendServiceInstructions(service, aMessage, info));
907
908 if (mSingleServiceMode.IsEnabled() && (mSingleServiceMode.GetService() != nullptr))
909 {
910 break;
911 }
912 }
913 }
914
915 SuccessOrExit(error = AppendHostDescriptionInstruction(aMessage, info));
916
917 header.SetUpdateRecordCount(info.mRecordCount);
918 aMessage.Write(kHeaderOffset, header);
919
920 // Prepare Additional Data section
921
922 SuccessOrExit(error = AppendUpdateLeaseOptRecord(aMessage));
923 SuccessOrExit(error = AppendSignature(aMessage, info));
924
925 header.SetAdditionalRecordCount(2); // Lease OPT and SIG RRs
926 aMessage.Write(kHeaderOffset, header);
927
928 exit:
929 return error;
930 }
931
ReadOrGenerateKey(Crypto::Ecdsa::P256::KeyPair & aKeyPair)932 Error Client::ReadOrGenerateKey(Crypto::Ecdsa::P256::KeyPair &aKeyPair)
933 {
934 Error error;
935
936 error = Get<Settings>().Read<Settings::SrpEcdsaKey>(aKeyPair);
937
938 if (error == kErrorNone)
939 {
940 Crypto::Ecdsa::P256::PublicKey publicKey;
941
942 if (aKeyPair.GetPublicKey(publicKey) == kErrorNone)
943 {
944 ExitNow();
945 }
946 }
947
948 SuccessOrExit(error = aKeyPair.Generate());
949 IgnoreError(Get<Settings>().Save<Settings::SrpEcdsaKey>(aKeyPair));
950
951 exit:
952 return error;
953 }
954
AppendServiceInstructions(Service & aService,Message & aMessage,Info & aInfo)955 Error Client::AppendServiceInstructions(Service &aService, Message &aMessage, Info &aInfo)
956 {
957 Error error = kErrorNone;
958 Dns::ResourceRecord rr;
959 Dns::SrvRecord srv;
960 bool removing;
961 uint16_t serviceNameOffset;
962 uint16_t instanceNameOffset;
963 uint16_t offset;
964
965 if (aService.GetState() == kRegistered)
966 {
967 // If the lease needs to be renewed or if we are close to the
968 // renewal time of a registered service, we refresh the service
969 // early and include it in this update. This helps put more
970 // services on the same lease refresh schedule.
971
972 VerifyOrExit(ShouldRenewEarly(aService));
973 aService.SetState(kToRefresh);
974 }
975
976 removing = ((aService.GetState() == kToRemove) || (aService.GetState() == kRemoving));
977
978 //----------------------------------
979 // Service Discovery Instruction
980
981 // PTR record
982
983 // "service name labels" + (pointer to) domain name.
984 serviceNameOffset = aMessage.GetLength();
985 SuccessOrExit(error = Dns::Name::AppendMultipleLabels(aService.GetName(), aMessage));
986 SuccessOrExit(error = Dns::Name::AppendPointerLabel(aInfo.mDomainNameOffset, aMessage));
987
988 // On remove, we use "Delete an RR from an RRSet" where class is set
989 // to NONE and TTL to zero (RFC 2136 - section 2.5.4).
990
991 rr.Init(Dns::ResourceRecord::kTypePtr, removing ? Dns::PtrRecord::kClassNone : Dns::PtrRecord::kClassInternet);
992 rr.SetTtl(removing ? 0 : GetTtl());
993 offset = aMessage.GetLength();
994 SuccessOrExit(error = aMessage.Append(rr));
995
996 // "Instance name" + (pointer to) service name.
997 instanceNameOffset = aMessage.GetLength();
998 SuccessOrExit(error = Dns::Name::AppendLabel(aService.GetInstanceName(), aMessage));
999 SuccessOrExit(error = Dns::Name::AppendPointerLabel(serviceNameOffset, aMessage));
1000
1001 UpdateRecordLengthInMessage(rr, offset, aMessage);
1002 aInfo.mRecordCount++;
1003
1004 if (aService.HasSubType() && !removing)
1005 {
1006 const char *subTypeLabel;
1007 uint16_t subServiceNameOffset = 0;
1008
1009 for (uint16_t index = 0; (subTypeLabel = aService.GetSubTypeLabelAt(index)) != nullptr; ++index)
1010 {
1011 // subtype label + "_sub" label + (pointer to) service name.
1012
1013 SuccessOrExit(error = Dns::Name::AppendLabel(subTypeLabel, aMessage));
1014
1015 if (index == 0)
1016 {
1017 subServiceNameOffset = aMessage.GetLength();
1018 SuccessOrExit(error = Dns::Name::AppendLabel("_sub", aMessage));
1019 SuccessOrExit(error = Dns::Name::AppendPointerLabel(serviceNameOffset, aMessage));
1020 }
1021 else
1022 {
1023 SuccessOrExit(error = Dns::Name::AppendPointerLabel(subServiceNameOffset, aMessage));
1024 }
1025
1026 // `rr` is already initialized as PTR.
1027 offset = aMessage.GetLength();
1028 SuccessOrExit(error = aMessage.Append(rr));
1029
1030 SuccessOrExit(error = Dns::Name::AppendPointerLabel(instanceNameOffset, aMessage));
1031 UpdateRecordLengthInMessage(rr, offset, aMessage);
1032 aInfo.mRecordCount++;
1033 }
1034 }
1035
1036 //----------------------------------
1037 // Service Description Instruction
1038
1039 // "Delete all RRsets from a name" for Instance Name.
1040
1041 SuccessOrExit(error = Dns::Name::AppendPointerLabel(instanceNameOffset, aMessage));
1042 SuccessOrExit(error = AppendDeleteAllRrsets(aMessage));
1043 aInfo.mRecordCount++;
1044
1045 VerifyOrExit(!removing);
1046
1047 // SRV RR
1048
1049 SuccessOrExit(error = Dns::Name::AppendPointerLabel(instanceNameOffset, aMessage));
1050 srv.Init();
1051 srv.SetTtl(GetTtl());
1052 srv.SetPriority(aService.GetPriority());
1053 srv.SetWeight(aService.GetWeight());
1054 srv.SetPort(aService.GetPort());
1055 offset = aMessage.GetLength();
1056 SuccessOrExit(error = aMessage.Append(srv));
1057 SuccessOrExit(error = AppendHostName(aMessage, aInfo));
1058 UpdateRecordLengthInMessage(srv, offset, aMessage);
1059 aInfo.mRecordCount++;
1060
1061 // TXT RR
1062
1063 SuccessOrExit(error = Dns::Name::AppendPointerLabel(instanceNameOffset, aMessage));
1064 rr.Init(Dns::ResourceRecord::kTypeTxt);
1065 offset = aMessage.GetLength();
1066 SuccessOrExit(error = aMessage.Append(rr));
1067 SuccessOrExit(error =
1068 Dns::TxtEntry::AppendEntries(aService.GetTxtEntries(), aService.GetNumTxtEntries(), aMessage));
1069 UpdateRecordLengthInMessage(rr, offset, aMessage);
1070 aInfo.mRecordCount++;
1071
1072 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
1073 if (mServiceKeyRecordEnabled)
1074 {
1075 // KEY RR is optional in "Service Description Instruction". It
1076 // is added here under `REFERENCE_DEVICE` config and is intended
1077 // for testing only.
1078
1079 SuccessOrExit(error = Dns::Name::AppendPointerLabel(instanceNameOffset, aMessage));
1080 SuccessOrExit(error = AppendKeyRecord(aMessage, aInfo));
1081 }
1082 #endif
1083
1084 if (mSingleServiceMode.IsEnabled())
1085 {
1086 mSingleServiceMode.SetService(aService);
1087 }
1088
1089 exit:
1090 return error;
1091 }
1092
AppendHostDescriptionInstruction(Message & aMessage,Info & aInfo)1093 Error Client::AppendHostDescriptionInstruction(Message &aMessage, Info &aInfo)
1094 {
1095 Error error = kErrorNone;
1096
1097 //----------------------------------
1098 // Host Description Instruction
1099
1100 // "Delete all RRsets from a name" for Host Name.
1101
1102 SuccessOrExit(error = AppendHostName(aMessage, aInfo));
1103 SuccessOrExit(error = AppendDeleteAllRrsets(aMessage));
1104 aInfo.mRecordCount++;
1105
1106 // AAAA RRs
1107
1108 if (mHostInfo.IsAutoAddressEnabled())
1109 {
1110 // Append all addresses on Thread netif excluding link-local and
1111 // mesh-local addresses. If no address is appended, we include
1112 // the mesh local address.
1113
1114 mAutoHostAddressAddedMeshLocal = true;
1115
1116 for (const Ip6::Netif::UnicastAddress &unicastAddress : Get<ThreadNetif>().GetUnicastAddresses())
1117 {
1118 if (unicastAddress.GetAddress().IsLinkLocal() ||
1119 Get<Mle::Mle>().IsMeshLocalAddress(unicastAddress.GetAddress()))
1120 {
1121 continue;
1122 }
1123
1124 SuccessOrExit(error = AppendAaaaRecord(unicastAddress.GetAddress(), aMessage, aInfo));
1125 mAutoHostAddressAddedMeshLocal = false;
1126 }
1127
1128 if (mAutoHostAddressAddedMeshLocal)
1129 {
1130 SuccessOrExit(error = AppendAaaaRecord(Get<Mle::Mle>().GetMeshLocal64(), aMessage, aInfo));
1131 }
1132 }
1133 else
1134 {
1135 for (uint8_t index = 0; index < mHostInfo.GetNumAddresses(); index++)
1136 {
1137 SuccessOrExit(error = AppendAaaaRecord(mHostInfo.GetAddress(index), aMessage, aInfo));
1138 }
1139 }
1140
1141 // KEY RR
1142
1143 SuccessOrExit(error = AppendHostName(aMessage, aInfo));
1144 SuccessOrExit(error = AppendKeyRecord(aMessage, aInfo));
1145
1146 exit:
1147 return error;
1148 }
1149
AppendAaaaRecord(const Ip6::Address & aAddress,Message & aMessage,Info & aInfo) const1150 Error Client::AppendAaaaRecord(const Ip6::Address &aAddress, Message &aMessage, Info &aInfo) const
1151 {
1152 Error error;
1153 Dns::ResourceRecord rr;
1154
1155 rr.Init(Dns::ResourceRecord::kTypeAaaa);
1156 rr.SetTtl(GetTtl());
1157 rr.SetLength(sizeof(Ip6::Address));
1158
1159 SuccessOrExit(error = AppendHostName(aMessage, aInfo));
1160 SuccessOrExit(error = aMessage.Append(rr));
1161 SuccessOrExit(error = aMessage.Append(aAddress));
1162 aInfo.mRecordCount++;
1163
1164 exit:
1165 return error;
1166 }
1167
AppendKeyRecord(Message & aMessage,Info & aInfo) const1168 Error Client::AppendKeyRecord(Message &aMessage, Info &aInfo) const
1169 {
1170 Error error;
1171 Dns::KeyRecord key;
1172 Crypto::Ecdsa::P256::PublicKey publicKey;
1173
1174 key.Init();
1175 key.SetTtl(GetTtl());
1176 key.SetFlags(Dns::KeyRecord::kAuthConfidPermitted, Dns::KeyRecord::kOwnerNonZone,
1177 Dns::KeyRecord::kSignatoryFlagGeneral);
1178 key.SetProtocol(Dns::KeyRecord::kProtocolDnsSec);
1179 key.SetAlgorithm(Dns::KeyRecord::kAlgorithmEcdsaP256Sha256);
1180 key.SetLength(sizeof(Dns::KeyRecord) - sizeof(Dns::ResourceRecord) + sizeof(Crypto::Ecdsa::P256::PublicKey));
1181 SuccessOrExit(error = aMessage.Append(key));
1182 SuccessOrExit(error = aInfo.mKeyPair.GetPublicKey(publicKey));
1183 SuccessOrExit(error = aMessage.Append(publicKey));
1184 aInfo.mRecordCount++;
1185
1186 exit:
1187 return error;
1188 }
1189
AppendDeleteAllRrsets(Message & aMessage) const1190 Error Client::AppendDeleteAllRrsets(Message &aMessage) const
1191 {
1192 // "Delete all RRsets from a name" (RFC 2136 - 2.5.3)
1193 // Name should be already appended in the message.
1194
1195 Dns::ResourceRecord rr;
1196
1197 rr.Init(Dns::ResourceRecord::kTypeAny, Dns::ResourceRecord::kClassAny);
1198 rr.SetTtl(0);
1199 rr.SetLength(0);
1200
1201 return aMessage.Append(rr);
1202 }
1203
AppendHostName(Message & aMessage,Info & aInfo,bool aDoNotCompress) const1204 Error Client::AppendHostName(Message &aMessage, Info &aInfo, bool aDoNotCompress) const
1205 {
1206 Error error;
1207
1208 if (aDoNotCompress)
1209 {
1210 // Uncompressed (canonical form) of host name is used for SIG(0)
1211 // calculation.
1212 SuccessOrExit(error = Dns::Name::AppendMultipleLabels(mHostInfo.GetName(), aMessage));
1213 error = Dns::Name::AppendName(mDomainName, aMessage);
1214 ExitNow();
1215 }
1216
1217 // If host name was previously added in the message, add it
1218 // compressed as pointer to the previous one. Otherwise,
1219 // append it and remember the offset.
1220
1221 if (aInfo.mHostNameOffset != Info::kUnknownOffset)
1222 {
1223 ExitNow(error = Dns::Name::AppendPointerLabel(aInfo.mHostNameOffset, aMessage));
1224 }
1225
1226 aInfo.mHostNameOffset = aMessage.GetLength();
1227 SuccessOrExit(error = Dns::Name::AppendMultipleLabels(mHostInfo.GetName(), aMessage));
1228 error = Dns::Name::AppendPointerLabel(aInfo.mDomainNameOffset, aMessage);
1229
1230 exit:
1231 return error;
1232 }
1233
AppendUpdateLeaseOptRecord(Message & aMessage) const1234 Error Client::AppendUpdateLeaseOptRecord(Message &aMessage) const
1235 {
1236 Error error;
1237 Dns::OptRecord optRecord;
1238 Dns::LeaseOption leaseOption;
1239
1240 // Append empty (root domain) as OPT RR name.
1241 SuccessOrExit(error = Dns::Name::AppendTerminator(aMessage));
1242
1243 // `Init()` sets the type and clears (set to zero) the extended
1244 // Response Code, version and all flags.
1245 optRecord.Init();
1246 optRecord.SetUdpPayloadSize(kUdpPayloadSize);
1247 optRecord.SetDnsSecurityFlag();
1248 optRecord.SetLength(sizeof(Dns::LeaseOption));
1249
1250 SuccessOrExit(error = aMessage.Append(optRecord));
1251
1252 leaseOption.Init();
1253
1254 if ((mHostInfo.GetState() == kToRemove) || (mHostInfo.GetState() == kRemoving))
1255 {
1256 leaseOption.SetLeaseInterval(0);
1257 leaseOption.SetKeyLeaseInterval(mShouldRemoveKeyLease ? 0 : mKeyLeaseInterval);
1258 }
1259 else
1260 {
1261 leaseOption.SetLeaseInterval(mLeaseInterval);
1262 leaseOption.SetKeyLeaseInterval(mKeyLeaseInterval);
1263 }
1264
1265 error = aMessage.Append(leaseOption);
1266
1267 exit:
1268 return error;
1269 }
1270
AppendSignature(Message & aMessage,Info & aInfo)1271 Error Client::AppendSignature(Message &aMessage, Info &aInfo)
1272 {
1273 Error error;
1274 Dns::SigRecord sig;
1275 Crypto::Sha256 sha256;
1276 Crypto::Sha256::Hash hash;
1277 Crypto::Ecdsa::P256::Signature signature;
1278 uint16_t offset;
1279 uint16_t len;
1280
1281 // Prepare SIG RR: TTL, type covered, labels count should be set
1282 // to zero. Since we have no clock, inception and expiration time
1283 // are also set to zero. The RDATA length will be set later (not
1284 // yet known due to variably (and possible compression) of signer's
1285 // name.
1286
1287 sig.Clear();
1288 sig.Init(Dns::ResourceRecord::kClassAny);
1289 sig.SetAlgorithm(Dns::KeyRecord::kAlgorithmEcdsaP256Sha256);
1290
1291 // Append the SIG RR with full uncompressed form of the host name
1292 // as the signer's name. This is used for SIG(0) calculation only.
1293 // It will be overwritten with host name compressed.
1294
1295 offset = aMessage.GetLength();
1296 SuccessOrExit(error = aMessage.Append(sig));
1297 SuccessOrExit(error = AppendHostName(aMessage, aInfo, /* aDoNotCompress */ true));
1298
1299 // Calculate signature (RFC 2931): Calculated over "data" which is
1300 // concatenation of (1) the SIG RR RDATA wire format (including
1301 // the canonical form of the signer's name), entirely omitting the
1302 // signature subfield, (2) DNS query message, including DNS header
1303 // but not UDP/IP header before the header RR counts have been
1304 // adjusted for the inclusion of SIG(0).
1305
1306 sha256.Start();
1307
1308 // (1) SIG RR RDATA wire format
1309 len = aMessage.GetLength() - offset - sizeof(Dns::ResourceRecord);
1310 sha256.Update(aMessage, offset + sizeof(Dns::ResourceRecord), len);
1311
1312 // (2) Message from DNS header before SIG
1313 sha256.Update(aMessage, 0, offset);
1314
1315 sha256.Finish(hash);
1316 SuccessOrExit(error = aInfo.mKeyPair.Sign(hash, signature));
1317
1318 // Move back in message and append SIG RR now with compressed host
1319 // name (as signer's name) along with the calculated signature.
1320
1321 IgnoreError(aMessage.SetLength(offset));
1322
1323 // SIG(0) uses owner name of root (single zero byte).
1324 SuccessOrExit(error = Dns::Name::AppendTerminator(aMessage));
1325
1326 offset = aMessage.GetLength();
1327 SuccessOrExit(error = aMessage.Append(sig));
1328 SuccessOrExit(error = AppendHostName(aMessage, aInfo));
1329 SuccessOrExit(error = aMessage.Append(signature));
1330 UpdateRecordLengthInMessage(sig, offset, aMessage);
1331
1332 exit:
1333 return error;
1334 }
1335
UpdateRecordLengthInMessage(Dns::ResourceRecord & aRecord,uint16_t aOffset,Message & aMessage) const1336 void Client::UpdateRecordLengthInMessage(Dns::ResourceRecord &aRecord, uint16_t aOffset, Message &aMessage) const
1337 {
1338 // This method is used to calculate an RR DATA length and update
1339 // (rewrite) it in a message. This should be called immediately
1340 // after all the fields in the record are written in the message.
1341 // `aOffset` gives the offset in the message to the start of the
1342 // record.
1343
1344 aRecord.SetLength(aMessage.GetLength() - aOffset - sizeof(Dns::ResourceRecord));
1345 aMessage.Write(aOffset, aRecord);
1346 }
1347
HandleUdpReceive(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo)1348 void Client::HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
1349 {
1350 OT_UNUSED_VARIABLE(aMessageInfo);
1351
1352 static_cast<Client *>(aContext)->ProcessResponse(AsCoreType(aMessage));
1353 }
1354
ProcessResponse(Message & aMessage)1355 void Client::ProcessResponse(Message &aMessage)
1356 {
1357 static const ItemState kNewStateOnUpdateDone[]{
1358 /* (0) kToAdd -> */ kToAdd,
1359 /* (1) kAdding -> */ kRegistered,
1360 /* (2) kToRefresh -> */ kToRefresh,
1361 /* (3) kRefreshing -> */ kRegistered,
1362 /* (4) kToRemove -> */ kToRemove,
1363 /* (5) kRemoving -> */ kRemoved,
1364 /* (6) kRegistered -> */ kRegistered,
1365 /* (7) kRemoved -> */ kRemoved,
1366 };
1367
1368 Error error = kErrorNone;
1369 Dns::UpdateHeader header;
1370 uint16_t offset = aMessage.GetOffset();
1371 uint16_t recordCount;
1372 LinkedList<Service> removedServices;
1373
1374 VerifyOrExit(GetState() == kStateUpdating);
1375
1376 SuccessOrExit(error = aMessage.Read(offset, header));
1377
1378 VerifyOrExit(header.GetType() == Dns::Header::kTypeResponse, error = kErrorParse);
1379 VerifyOrExit(header.GetQueryType() == Dns::Header::kQueryTypeUpdate, error = kErrorParse);
1380 VerifyOrExit(header.GetMessageId() == mUpdateMessageId, error = kErrorDrop);
1381
1382 if (!Get<Mle::Mle>().IsRxOnWhenIdle())
1383 {
1384 Get<DataPollSender>().StopFastPolls();
1385 }
1386
1387 // Response is for the earlier request message.
1388
1389 LogInfo("Received response");
1390
1391 #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE && OPENTHREAD_CONFIG_SRP_CLIENT_SWITCH_SERVER_ON_FAILURE
1392 mAutoStart.ResetTimoutFailureCount();
1393 #endif
1394
1395 error = Dns::Header::ResponseCodeToError(header.GetResponseCode());
1396
1397 if (error != kErrorNone)
1398 {
1399 LogInfo("Server rejected %s code:%d", ErrorToString(error), header.GetResponseCode());
1400
1401 if (mHostInfo.GetState() == kAdding)
1402 {
1403 // Since server rejected the update message, we go back to
1404 // `kToAdd` state to allow user to give a new name using
1405 // `SetHostName()`.
1406 mHostInfo.SetState(kToAdd);
1407 }
1408
1409 // Wait for the timer to expire to retry. Note that timer is
1410 // already scheduled for the current wait interval when state
1411 // was changed to `kStateUpdating`.
1412
1413 LogRetryWaitInterval();
1414 GrowRetryWaitInterval();
1415 SetState(kStateToRetry);
1416 InvokeCallback(error);
1417
1418 #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE && OPENTHREAD_CONFIG_SRP_CLIENT_SWITCH_SERVER_ON_FAILURE
1419 if ((error == kErrorDuplicated) || (error == kErrorSecurity))
1420 {
1421 // If the server rejects the update with specific errors
1422 // (indicating duplicate name and/or security error), we
1423 // try to switch the server (we check if another can be
1424 // found in the Network Data).
1425 //
1426 // Note that this is done after invoking the callback and
1427 // notifying the user of the error from server. This works
1428 // correctly even if user makes changes from callback
1429 // (e.g., calls SRP client APIs like `Stop` or disables
1430 // auto-start), since we have a guard check at the top of
1431 // `SelectNextServer()` to verify that client is still
1432 // running and auto-start is enabled and selected the
1433 // server.
1434
1435 SelectNextServer(/* aDisallowSwitchOnRegisteredHost */ true);
1436 }
1437 #endif
1438 ExitNow(error = kErrorNone);
1439 }
1440
1441 offset += sizeof(header);
1442
1443 // Skip over all sections till Additional Data section
1444 // SPEC ENHANCEMENT: Server can echo the request back or not
1445 // include any of RRs. Would be good to explicitly require SRP server
1446 // to not echo back RRs.
1447
1448 if (header.GetZoneRecordCount() != 0)
1449 {
1450 VerifyOrExit(header.GetZoneRecordCount() == 1, error = kErrorParse);
1451 SuccessOrExit(error = Dns::Name::ParseName(aMessage, offset));
1452 VerifyOrExit(offset + sizeof(Dns::Zone) <= aMessage.GetLength(), error = kErrorParse);
1453 offset += sizeof(Dns::Zone);
1454 }
1455
1456 // Check for Update Lease OPT RR. This determines the lease
1457 // interval accepted by server. If not present, then use the
1458 // transmitted lease interval from the update request message.
1459
1460 mAcceptedLeaseInterval = mLeaseInterval;
1461 recordCount =
1462 header.GetPrerequisiteRecordCount() + header.GetUpdateRecordCount() + header.GetAdditionalRecordCount();
1463
1464 while (recordCount > 0)
1465 {
1466 uint16_t startOffset = offset;
1467 Dns::ResourceRecord rr;
1468
1469 SuccessOrExit(error = ReadResourceRecord(aMessage, offset, rr));
1470 recordCount--;
1471
1472 if (rr.GetType() == Dns::ResourceRecord::kTypeOpt)
1473 {
1474 SuccessOrExit(error = ProcessOptRecord(aMessage, startOffset, static_cast<Dns::OptRecord &>(rr)));
1475 }
1476 }
1477
1478 // Calculate the lease renew time based on update message tx time
1479 // and the lease time. `kLeaseRenewGuardInterval` is used to
1480 // ensure that we renew the lease before server expires it. In the
1481 // unlikely (but maybe useful for testing) case where the accepted
1482 // lease interval is too short (shorter than the guard time) we
1483 // just use half of the accepted lease interval.
1484
1485 if (mAcceptedLeaseInterval > kLeaseRenewGuardInterval)
1486 {
1487 mLeaseRenewTime += Time::SecToMsec(mAcceptedLeaseInterval - kLeaseRenewGuardInterval);
1488 }
1489 else
1490 {
1491 mLeaseRenewTime += Time::SecToMsec(mAcceptedLeaseInterval) / 2;
1492 }
1493
1494 for (Service &service : mServices)
1495 {
1496 if ((service.GetState() == kAdding) || (service.GetState() == kRefreshing))
1497 {
1498 service.SetLeaseRenewTime(mLeaseRenewTime);
1499 }
1500 }
1501
1502 // State changes:
1503 // kAdding -> kRegistered
1504 // kRefreshing -> kRegistered
1505 // kRemoving -> kRemoved
1506
1507 ChangeHostAndServiceStates(kNewStateOnUpdateDone);
1508 mSingleServiceMode.Disable();
1509
1510 HandleUpdateDone();
1511 UpdateState();
1512
1513 exit:
1514 if (error != kErrorNone)
1515 {
1516 LogInfo("Failed to process response %s", ErrorToString(error));
1517 }
1518 }
1519
HandleUpdateDone(void)1520 void Client::HandleUpdateDone(void)
1521 {
1522 HostInfo hostInfoCopy = mHostInfo;
1523 LinkedList<Service> removedServices;
1524
1525 if (mHostInfo.GetState() == kRemoved)
1526 {
1527 mHostInfo.Clear();
1528 }
1529
1530 ResetRetryWaitInterval();
1531 SetState(kStateUpdated);
1532
1533 GetRemovedServices(removedServices);
1534 InvokeCallback(kErrorNone, hostInfoCopy, removedServices.GetHead());
1535 }
1536
GetRemovedServices(LinkedList<Service> & aRemovedServices)1537 void Client::GetRemovedServices(LinkedList<Service> &aRemovedServices)
1538 {
1539 mServices.RemoveAllMatching(kRemoved, aRemovedServices);
1540 }
1541
ReadResourceRecord(const Message & aMessage,uint16_t & aOffset,Dns::ResourceRecord & aRecord)1542 Error Client::ReadResourceRecord(const Message &aMessage, uint16_t &aOffset, Dns::ResourceRecord &aRecord)
1543 {
1544 // Reads and skips over a Resource Record (RR) from message at
1545 // given offset. On success, `aOffset` is updated to point to end
1546 // of RR.
1547
1548 Error error;
1549
1550 SuccessOrExit(error = Dns::Name::ParseName(aMessage, aOffset));
1551 SuccessOrExit(error = aMessage.Read(aOffset, aRecord));
1552 VerifyOrExit(aOffset + aRecord.GetSize() <= aMessage.GetLength(), error = kErrorParse);
1553 aOffset += static_cast<uint16_t>(aRecord.GetSize());
1554
1555 exit:
1556 return error;
1557 }
1558
ProcessOptRecord(const Message & aMessage,uint16_t aOffset,const Dns::OptRecord & aOptRecord)1559 Error Client::ProcessOptRecord(const Message &aMessage, uint16_t aOffset, const Dns::OptRecord &aOptRecord)
1560 {
1561 // Read and process all options (in an OPT RR) from a message.
1562 // The `aOffset` points to beginning of record in `aMessage`.
1563
1564 Error error = kErrorNone;
1565 uint16_t len;
1566
1567 IgnoreError(Dns::Name::ParseName(aMessage, aOffset));
1568 aOffset += sizeof(Dns::OptRecord);
1569
1570 len = aOptRecord.GetLength();
1571
1572 while (len > 0)
1573 {
1574 Dns::LeaseOption leaseOption;
1575 Dns::Option & option = leaseOption;
1576 uint16_t size;
1577
1578 SuccessOrExit(error = aMessage.Read(aOffset, option));
1579
1580 VerifyOrExit(aOffset + option.GetSize() <= aMessage.GetLength(), error = kErrorParse);
1581
1582 if ((option.GetOptionCode() == Dns::Option::kUpdateLease) &&
1583 (option.GetOptionLength() >= Dns::LeaseOption::kOptionLength))
1584 {
1585 SuccessOrExit(error = aMessage.Read(aOffset, leaseOption));
1586
1587 mAcceptedLeaseInterval = leaseOption.GetLeaseInterval();
1588
1589 if (mAcceptedLeaseInterval > kMaxLease)
1590 {
1591 mAcceptedLeaseInterval = kMaxLease;
1592 }
1593 }
1594
1595 size = static_cast<uint16_t>(option.GetSize());
1596 aOffset += size;
1597 len -= size;
1598 }
1599
1600 exit:
1601 return error;
1602 }
1603
UpdateState(void)1604 void Client::UpdateState(void)
1605 {
1606 TimeMilli now = TimerMilli::GetNow();
1607 TimeMilli earliestRenewTime = now.GetDistantFuture();
1608 bool shouldUpdate = false;
1609
1610 VerifyOrExit((GetState() != kStateStopped) && (GetState() != kStatePaused));
1611 VerifyOrExit(mHostInfo.GetName() != nullptr);
1612
1613 // Go through the host info and all the services to check if there
1614 // are any new changes (i.e., anything new to add or remove). This
1615 // is used to determine whether to send an SRP update message or
1616 // not. Also keep track of the earliest renew time among the
1617 // previously registered services. This is used to schedule the
1618 // timer for next refresh.
1619
1620 switch (mHostInfo.GetState())
1621 {
1622 case kAdding:
1623 case kRefreshing:
1624 case kRemoving:
1625 break;
1626
1627 case kRegistered:
1628 if (now < mLeaseRenewTime)
1629 {
1630 break;
1631 }
1632
1633 mHostInfo.SetState(kToRefresh);
1634
1635 // Fall through
1636
1637 case kToAdd:
1638 case kToRefresh:
1639 // Make sure we have at least one service and at least one
1640 // host address, otherwise no need to send SRP update message.
1641 // The exception is when removing host info where we allow
1642 // for empty service list.
1643 VerifyOrExit(!mServices.IsEmpty() && (mHostInfo.IsAutoAddressEnabled() || (mHostInfo.GetNumAddresses() > 0)));
1644
1645 // Fall through
1646
1647 case kToRemove:
1648 shouldUpdate = true;
1649 break;
1650
1651 case kRemoved:
1652 ExitNow();
1653 }
1654
1655 // If host info is being removed, we skip over checking service list
1656 // for new adds (or removes). This handles the situation where while
1657 // remove is ongoing and before we get a response from the server,
1658 // user adds a new service to be registered. We wait for remove to
1659 // finish (receive response from server) before starting with a new
1660 // service adds.
1661
1662 if (mHostInfo.GetState() != kRemoving)
1663 {
1664 for (Service &service : mServices)
1665 {
1666 switch (service.GetState())
1667 {
1668 case kToAdd:
1669 case kToRefresh:
1670 case kToRemove:
1671 shouldUpdate = true;
1672 break;
1673
1674 case kRegistered:
1675 if (service.GetLeaseRenewTime() <= now)
1676 {
1677 service.SetState(kToRefresh);
1678 shouldUpdate = true;
1679 }
1680 else if (service.GetLeaseRenewTime() < earliestRenewTime)
1681 {
1682 earliestRenewTime = service.GetLeaseRenewTime();
1683 }
1684
1685 break;
1686
1687 case kAdding:
1688 case kRefreshing:
1689 case kRemoving:
1690 case kRemoved:
1691 break;
1692 }
1693 }
1694 }
1695
1696 if (shouldUpdate)
1697 {
1698 SetState(kStateToUpdate);
1699 ExitNow();
1700 }
1701
1702 if ((GetState() == kStateUpdated) && (earliestRenewTime != now.GetDistantFuture()))
1703 {
1704 mTimer.FireAt(earliestRenewTime);
1705 }
1706
1707 exit:
1708 return;
1709 }
1710
GrowRetryWaitInterval(void)1711 void Client::GrowRetryWaitInterval(void)
1712 {
1713 mRetryWaitInterval =
1714 mRetryWaitInterval / kRetryIntervalGrowthFactorDenominator * kRetryIntervalGrowthFactorNumerator;
1715
1716 if (mRetryWaitInterval > kMaxRetryWaitInterval)
1717 {
1718 mRetryWaitInterval = kMaxRetryWaitInterval;
1719 }
1720 }
1721
GetBoundedLeaseInterval(uint32_t aInterval,uint32_t aDefaultInterval) const1722 uint32_t Client::GetBoundedLeaseInterval(uint32_t aInterval, uint32_t aDefaultInterval) const
1723 {
1724 uint32_t boundedInterval = aDefaultInterval;
1725
1726 if (aInterval != 0)
1727 {
1728 boundedInterval = OT_MIN(aInterval, static_cast<uint32_t>(kMaxLease));
1729 }
1730
1731 return boundedInterval;
1732 }
1733
ShouldRenewEarly(const Service & aService) const1734 bool Client::ShouldRenewEarly(const Service &aService) const
1735 {
1736 // Check if we reached the service renew time or close to it. The
1737 // "early renew interval" is used to allow early refresh. It is
1738 // calculated as a factor of the `mAcceptedLeaseInterval`. The
1739 // "early lease renew factor" is given as a fraction (numerator and
1740 // denominator). If the denominator is set to zero (i.e., factor is
1741 // set to infinity), then service is always included in all SRP
1742 // update messages.
1743
1744 bool shouldRenew;
1745
1746 #if OPENTHREAD_CONFIG_SRP_CLIENT_EARLY_LEASE_RENEW_FACTOR_DENOMINATOR != 0
1747 uint32_t earlyRenewInterval =
1748 Time::SecToMsec(mAcceptedLeaseInterval) / kEarlyLeaseRenewFactorDenominator * kEarlyLeaseRenewFactorNumerator;
1749
1750 shouldRenew = (aService.GetLeaseRenewTime() <= TimerMilli::GetNow() + earlyRenewInterval);
1751 #else
1752 OT_UNUSED_VARIABLE(aService);
1753 shouldRenew = true;
1754 #endif
1755
1756 return shouldRenew;
1757 }
1758
HandleTimer(Timer & aTimer)1759 void Client::HandleTimer(Timer &aTimer)
1760 {
1761 aTimer.Get<Client>().HandleTimer();
1762 }
1763
HandleTimer(void)1764 void Client::HandleTimer(void)
1765 {
1766 switch (GetState())
1767 {
1768 case kStateStopped:
1769 case kStatePaused:
1770 break;
1771
1772 case kStateToUpdate:
1773 case kStateToRetry:
1774 SendUpdate();
1775 break;
1776
1777 case kStateUpdating:
1778 mSingleServiceMode.Disable();
1779 LogRetryWaitInterval();
1780 LogInfo("Timed out, no response");
1781 GrowRetryWaitInterval();
1782 SetState(kStateToUpdate);
1783 InvokeCallback(kErrorResponseTimeout);
1784
1785 #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE && OPENTHREAD_CONFIG_SRP_CLIENT_SWITCH_SERVER_ON_FAILURE
1786
1787 // After certain number of back-to-back timeout failures, we try
1788 // to switch the server. This is again done after invoking the
1789 // callback. It works correctly due to the guard check at the
1790 // top of `SelectNextServer()`.
1791
1792 mAutoStart.IncrementTimoutFailureCount();
1793
1794 if (mAutoStart.GetTimoutFailureCount() >= kMaxTimeoutFailuresToSwitchServer)
1795 {
1796 SelectNextServer(kDisallowSwitchOnRegisteredHost);
1797 }
1798 #endif
1799 break;
1800
1801 case kStateUpdated:
1802 UpdateState();
1803 break;
1804 }
1805 }
1806
1807 #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
1808
EnableAutoStartMode(AutoStartCallback aCallback,void * aContext)1809 void Client::EnableAutoStartMode(AutoStartCallback aCallback, void *aContext)
1810 {
1811 mAutoStart.SetCallback(aCallback, aContext);
1812
1813 VerifyOrExit(mAutoStart.GetState() == AutoStart::kDisabled);
1814
1815 mAutoStart.SetState(AutoStart::kSelectedNone);
1816 ProcessAutoStart();
1817
1818 exit:
1819 return;
1820 }
1821
ProcessAutoStart(void)1822 void Client::ProcessAutoStart(void)
1823 {
1824 Ip6::SockAddr serverSockAddr;
1825 DnsSrpAnycast::Info anycastInfo;
1826 DnsSrpUnicast::Info unicastInfo;
1827 bool shouldRestart = false;
1828
1829 // If auto start mode is enabled, we check the Network Data entries
1830 // to discover and select the preferred SRP server to register with.
1831 // If we currently have a selected server, we ensure that it is
1832 // still present in the Network Data and is still the preferred one.
1833
1834 VerifyOrExit(mAutoStart.GetState() != AutoStart::kDisabled);
1835
1836 // If SRP client is running, we check to make sure that auto-start
1837 // did select the current server, and server was not specified by
1838 // user directly.
1839
1840 if (IsRunning())
1841 {
1842 VerifyOrExit(mAutoStart.GetState() != AutoStart::kSelectedNone);
1843 }
1844
1845 // There are three types of entries in Network Data:
1846 //
1847 // 1) Preferred unicast entries with address included in service data.
1848 // 2) Anycast entries (each having a seq number).
1849 // 3) Unicast entries with address info included in server data.
1850
1851 serverSockAddr.Clear();
1852
1853 if (SelectUnicastEntry(DnsSrpUnicast::kFromServiceData, unicastInfo) == kErrorNone)
1854 {
1855 mAutoStart.SetState(AutoStart::kSelectedUnicastPreferred);
1856 serverSockAddr = unicastInfo.mSockAddr;
1857 }
1858 else if (Get<NetworkData::Service::Manager>().FindPreferredDnsSrpAnycastInfo(anycastInfo) == kErrorNone)
1859 {
1860 serverSockAddr.SetAddress(anycastInfo.mAnycastAddress);
1861 serverSockAddr.SetPort(kAnycastServerPort);
1862
1863 // We check if we are selecting an anycast entry for first
1864 // time, or if the seq number has changed. Even if the
1865 // anycast address remains the same as before, on a seq
1866 // number change, the client still needs to restart to
1867 // re-register its info.
1868
1869 if ((mAutoStart.GetState() != AutoStart::kSelectedAnycast) ||
1870 (mAutoStart.GetAnycastSeqNum() != anycastInfo.mSequenceNumber))
1871 {
1872 shouldRestart = true;
1873 mAutoStart.SetAnycastSeqNum(anycastInfo.mSequenceNumber);
1874 }
1875
1876 mAutoStart.SetState(AutoStart::kSelectedAnycast);
1877 }
1878 else if (SelectUnicastEntry(DnsSrpUnicast::kFromServerData, unicastInfo) == kErrorNone)
1879 {
1880 mAutoStart.SetState(AutoStart::kSelectedUnicast);
1881 serverSockAddr = unicastInfo.mSockAddr;
1882 }
1883
1884 if (IsRunning())
1885 {
1886 VerifyOrExit((GetServerAddress() != serverSockAddr) || shouldRestart);
1887 Stop(kRequesterAuto, kResetRetryInterval);
1888 }
1889
1890 if (!serverSockAddr.GetAddress().IsUnspecified())
1891 {
1892 IgnoreError(Start(serverSockAddr, kRequesterAuto));
1893 }
1894 else
1895 {
1896 mAutoStart.SetState(AutoStart::kSelectedNone);
1897 }
1898
1899 exit:
1900 return;
1901 }
1902
SelectUnicastEntry(DnsSrpUnicast::Origin aOrigin,DnsSrpUnicast::Info & aInfo) const1903 Error Client::SelectUnicastEntry(DnsSrpUnicast::Origin aOrigin, DnsSrpUnicast::Info &aInfo) const
1904 {
1905 Error error = kErrorNotFound;
1906 DnsSrpUnicast::Info unicastInfo;
1907 NetworkData::Service::Manager::Iterator iterator;
1908 #if OPENTHREAD_CONFIG_SRP_CLIENT_SAVE_SELECTED_SERVER_ENABLE
1909 Settings::SrpClientInfo savedInfo;
1910 bool hasSavedServerInfo = false;
1911
1912 if (!IsRunning())
1913 {
1914 hasSavedServerInfo = (Get<Settings>().Read(savedInfo) == kErrorNone);
1915 }
1916 #endif
1917
1918 while (Get<NetworkData::Service::Manager>().GetNextDnsSrpUnicastInfo(iterator, unicastInfo) == kErrorNone)
1919 {
1920 if (unicastInfo.mOrigin != aOrigin)
1921 {
1922 continue;
1923 }
1924
1925 if (mAutoStart.HasSelectedServer() && (GetServerAddress() == unicastInfo.mSockAddr))
1926 {
1927 aInfo = unicastInfo;
1928 error = kErrorNone;
1929 ExitNow();
1930 }
1931
1932 #if OPENTHREAD_CONFIG_SRP_CLIENT_SAVE_SELECTED_SERVER_ENABLE
1933 if (hasSavedServerInfo && (unicastInfo.mSockAddr.GetAddress() == savedInfo.GetServerAddress()) &&
1934 (unicastInfo.mSockAddr.GetPort() == savedInfo.GetServerPort()))
1935 {
1936 // Stop the search if we see a match for the previously
1937 // saved server info in the network data entries.
1938
1939 aInfo = unicastInfo;
1940 error = kErrorNone;
1941 ExitNow();
1942 }
1943 #endif
1944
1945 // Prefer the numerically lowest server address
1946
1947 if ((error == kErrorNotFound) || (unicastInfo.mSockAddr.GetAddress() < aInfo.mSockAddr.GetAddress()))
1948 {
1949 aInfo = unicastInfo;
1950 error = kErrorNone;
1951 }
1952 }
1953
1954 exit:
1955 return error;
1956 }
1957
1958 #if OPENTHREAD_CONFIG_SRP_CLIENT_SWITCH_SERVER_ON_FAILURE
SelectNextServer(bool aDisallowSwitchOnRegisteredHost)1959 void Client::SelectNextServer(bool aDisallowSwitchOnRegisteredHost)
1960 {
1961 // This method tries to find the next unicast server info entry in the
1962 // Network Data after the current one selected. If found, it
1963 // restarts the client with the new server (keeping the retry wait
1964 // interval as before).
1965
1966 Ip6::SockAddr serverSockAddr;
1967 bool selectNext = false;
1968 DnsSrpUnicast::Origin origin = DnsSrpUnicast::kFromServiceData;
1969
1970 serverSockAddr.Clear();
1971
1972 // Ensure that client is running, auto-start is enabled and
1973 // auto-start selected the server and it is a unicast entry.
1974
1975 VerifyOrExit(IsRunning());
1976
1977 switch (mAutoStart.GetState())
1978 {
1979 case AutoStart::kSelectedUnicastPreferred:
1980 origin = DnsSrpUnicast::kFromServiceData;
1981 break;
1982
1983 case AutoStart::kSelectedUnicast:
1984 origin = DnsSrpUnicast::kFromServerData;
1985 break;
1986
1987 case AutoStart::kSelectedAnycast:
1988 case AutoStart::kDisabled:
1989 case AutoStart::kSelectedNone:
1990 ExitNow();
1991 }
1992
1993 if (aDisallowSwitchOnRegisteredHost)
1994 {
1995 // Ensure that host info is not yet registered (indicating that no
1996 // service has yet been registered either).
1997 VerifyOrExit((mHostInfo.GetState() == kAdding) || (mHostInfo.GetState() == kToAdd));
1998 }
1999
2000 // We go through all entries to find the one matching the currently
2001 // selected one, then set `selectNext` to `true` so to select the
2002 // next one.
2003
2004 do
2005 {
2006 DnsSrpUnicast::Info unicastInfo;
2007 NetworkData::Service::Manager::Iterator iterator;
2008
2009 while (Get<NetworkData::Service::Manager>().GetNextDnsSrpUnicastInfo(iterator, unicastInfo) == kErrorNone)
2010 {
2011 if (unicastInfo.mOrigin != origin)
2012 {
2013 continue;
2014 }
2015
2016 if (selectNext)
2017 {
2018 serverSockAddr = unicastInfo.mSockAddr;
2019 ExitNow();
2020 }
2021
2022 if (GetServerAddress() == unicastInfo.mSockAddr)
2023 {
2024 selectNext = true;
2025 }
2026 }
2027
2028 // We loop back to handle the case where the current entry
2029 // is the last one.
2030
2031 } while (selectNext);
2032
2033 // If we reach here it indicates we could not find the entry
2034 // associated with currently selected server in the list. This
2035 // situation is rather unlikely but can still happen if Network
2036 // Data happens to be changed and the entry removed but
2037 // the "changed" event from `Notifier` may have not yet been
2038 // processed (note that events are emitted from their own
2039 // tasklet). In such a case we keep `serverSockAddr` as empty.
2040
2041 exit:
2042 if (!serverSockAddr.GetAddress().IsUnspecified() && (GetServerAddress() != serverSockAddr))
2043 {
2044 // We specifically update `mHostInfo` to `kToAdd` state. This
2045 // ensures that `Stop()` will keep it as kToAdd` and we detect
2046 // that the host info has not been registered yet and allow the
2047 // `SelectNextServer()` to happen again if the timeouts/failures
2048 // continue to happen with the new server.
2049
2050 mHostInfo.SetState(kToAdd);
2051 Stop(kRequesterAuto, kKeepRetryInterval);
2052 IgnoreError(Start(serverSockAddr, kRequesterAuto));
2053 }
2054 }
2055 #endif // OPENTHREAD_CONFIG_SRP_CLIENT_SWITCH_SERVER_ON_FAILURE
2056
2057 #endif // OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
2058
ItemStateToString(ItemState aState)2059 const char *Client::ItemStateToString(ItemState aState)
2060 {
2061 static const char *const kItemStateStrings[] = {
2062 "ToAdd", // kToAdd (0)
2063 "Adding", // kAdding (1)
2064 "ToRefresh", // kToRefresh (2)
2065 "Refreshing", // kRefreshing (3)
2066 "ToRemove", // kToRemove (4)
2067 "Removing", // kRemoving (5)
2068 "Registered", // kRegistered (6)
2069 "Removed", // kRemoved (7)
2070 };
2071
2072 return kItemStateStrings[aState];
2073 }
2074
2075 #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
2076
StateToString(State aState)2077 const char *Client::StateToString(State aState)
2078 {
2079 static const char *const kStateStrings[] = {
2080 "Stopped", // kStateStopped (0)
2081 "Paused", // kStatePaused (1)
2082 "ToUpdate", // kStateToUpdate (2)
2083 "Updating", // kStateUpdating (3)
2084 "Updated", // kStateUpdated (4)
2085 "ToRetry", // kStateToRetry (5)
2086 };
2087
2088 static_assert(kStateStopped == 0, "kStateStopped value is not correct");
2089 static_assert(kStatePaused == 1, "kStatePaused value is not correct");
2090 static_assert(kStateToUpdate == 2, "kStateToUpdate value is not correct");
2091 static_assert(kStateUpdating == 3, "kStateUpdating value is not correct");
2092 static_assert(kStateUpdated == 4, "kStateUpdated value is not correct");
2093 static_assert(kStateToRetry == 5, "kStateToRetry value is not correct");
2094
2095 return kStateStrings[aState];
2096 }
2097
LogRetryWaitInterval(void) const2098 void Client::LogRetryWaitInterval(void) const
2099 {
2100 constexpr uint16_t kLogInMsecLimit = 5000; // Max interval (in msec) to log the value in msec unit
2101
2102 uint32_t interval = GetRetryWaitInterval();
2103
2104 LogInfo("Retry interval %u %s", (interval < kLogInMsecLimit) ? interval : Time::MsecToSec(interval),
2105 (interval < kLogInMsecLimit) ? "ms" : "sec");
2106 }
2107
2108 #endif // #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
2109
2110 } // namespace Srp
2111 } // namespace ot
2112
2113 #endif // OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE
2114