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