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