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