1 /*
2 * Copyright (c) 2021, 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 /**
30 * @file
31 * This file includes implementation of mDNS publisher.
32 */
33
34 #define OTBR_LOG_TAG "MDNS"
35
36 #include "mdns/mdns.hpp"
37
38 #if OTBR_ENABLE_MDNS
39
40 #include <assert.h>
41
42 #include <algorithm>
43 #include <functional>
44
45 #include "common/code_utils.hpp"
46 #include "utils/dns_utils.hpp"
47
48 namespace otbr {
49
50 namespace Mdns {
51
PublishService(const std::string & aHostName,const std::string & aName,const std::string & aType,const SubTypeList & aSubTypeList,uint16_t aPort,const TxtData & aTxtData,ResultCallback && aCallback)52 void Publisher::PublishService(const std::string &aHostName,
53 const std::string &aName,
54 const std::string &aType,
55 const SubTypeList &aSubTypeList,
56 uint16_t aPort,
57 const TxtData &aTxtData,
58 ResultCallback &&aCallback)
59 {
60 otbrError error;
61
62 mServiceRegistrationBeginTime[std::make_pair(aName, aType)] = Clock::now();
63
64 error = PublishServiceImpl(aHostName, aName, aType, aSubTypeList, aPort, aTxtData, std::move(aCallback));
65 if (error != OTBR_ERROR_NONE)
66 {
67 UpdateMdnsResponseCounters(mTelemetryInfo.mServiceRegistrations, error);
68 }
69 }
70
PublishHost(const std::string & aName,const AddressList & aAddresses,ResultCallback && aCallback)71 void Publisher::PublishHost(const std::string &aName, const AddressList &aAddresses, ResultCallback &&aCallback)
72 {
73 otbrError error;
74
75 mHostRegistrationBeginTime[aName] = Clock::now();
76
77 error = PublishHostImpl(aName, aAddresses, std::move(aCallback));
78 if (error != OTBR_ERROR_NONE)
79 {
80 UpdateMdnsResponseCounters(mTelemetryInfo.mHostRegistrations, error);
81 }
82 }
83
PublishKey(const std::string & aName,const KeyData & aKeyData,ResultCallback && aCallback)84 void Publisher::PublishKey(const std::string &aName, const KeyData &aKeyData, ResultCallback &&aCallback)
85 {
86 otbrError error;
87
88 mKeyRegistrationBeginTime[aName] = Clock::now();
89
90 error = PublishKeyImpl(aName, aKeyData, std::move(aCallback));
91 if (error != OTBR_ERROR_NONE)
92 {
93 UpdateMdnsResponseCounters(mTelemetryInfo.mKeyRegistrations, error);
94 }
95 }
96
OnServiceResolveFailed(std::string aType,std::string aInstanceName,int32_t aErrorCode)97 void Publisher::OnServiceResolveFailed(std::string aType, std::string aInstanceName, int32_t aErrorCode)
98 {
99 UpdateMdnsResponseCounters(mTelemetryInfo.mServiceResolutions, DnsErrorToOtbrError(aErrorCode));
100 UpdateServiceInstanceResolutionEmaLatency(aInstanceName, aType, DnsErrorToOtbrError(aErrorCode));
101 OnServiceResolveFailedImpl(aType, aInstanceName, aErrorCode);
102 }
103
OnHostResolveFailed(std::string aHostName,int32_t aErrorCode)104 void Publisher::OnHostResolveFailed(std::string aHostName, int32_t aErrorCode)
105 {
106 UpdateMdnsResponseCounters(mTelemetryInfo.mHostResolutions, DnsErrorToOtbrError(aErrorCode));
107 UpdateHostResolutionEmaLatency(aHostName, DnsErrorToOtbrError(aErrorCode));
108 OnHostResolveFailedImpl(aHostName, aErrorCode);
109 }
110
EncodeTxtData(const TxtList & aTxtList,std::vector<uint8_t> & aTxtData)111 otbrError Publisher::EncodeTxtData(const TxtList &aTxtList, std::vector<uint8_t> &aTxtData)
112 {
113 otbrError error = OTBR_ERROR_NONE;
114
115 aTxtData.clear();
116
117 for (const TxtEntry &txtEntry : aTxtList)
118 {
119 size_t entryLength = txtEntry.mKey.length();
120
121 if (!txtEntry.mIsBooleanAttribute)
122 {
123 entryLength += txtEntry.mValue.size() + sizeof(uint8_t); // for `=` char.
124 }
125
126 VerifyOrExit(entryLength <= kMaxTextEntrySize, error = OTBR_ERROR_INVALID_ARGS);
127
128 aTxtData.push_back(static_cast<uint8_t>(entryLength));
129 aTxtData.insert(aTxtData.end(), txtEntry.mKey.begin(), txtEntry.mKey.end());
130
131 if (!txtEntry.mIsBooleanAttribute)
132 {
133 aTxtData.push_back('=');
134 aTxtData.insert(aTxtData.end(), txtEntry.mValue.begin(), txtEntry.mValue.end());
135 }
136 }
137
138 if (aTxtData.empty())
139 {
140 aTxtData.push_back(0);
141 }
142
143 exit:
144 return error;
145 }
146
DecodeTxtData(Publisher::TxtList & aTxtList,const uint8_t * aTxtData,uint16_t aTxtLength)147 otbrError Publisher::DecodeTxtData(Publisher::TxtList &aTxtList, const uint8_t *aTxtData, uint16_t aTxtLength)
148 {
149 otbrError error = OTBR_ERROR_NONE;
150
151 aTxtList.clear();
152
153 for (uint16_t r = 0; r < aTxtLength;)
154 {
155 uint16_t entrySize = aTxtData[r];
156 uint16_t keyStart = r + 1;
157 uint16_t entryEnd = keyStart + entrySize;
158 uint16_t keyEnd = keyStart;
159
160 VerifyOrExit(entryEnd <= aTxtLength, error = OTBR_ERROR_PARSE);
161
162 while (keyEnd < entryEnd && aTxtData[keyEnd] != '=')
163 {
164 keyEnd++;
165 }
166
167 if (keyEnd == entryEnd)
168 {
169 if (keyEnd > keyStart)
170 {
171 // No `=`, treat as a boolean attribute.
172 aTxtList.emplace_back(reinterpret_cast<const char *>(&aTxtData[keyStart]), keyEnd - keyStart);
173 }
174 }
175 else
176 {
177 uint16_t valStart = keyEnd + 1; // To skip over `=`
178
179 aTxtList.emplace_back(reinterpret_cast<const char *>(&aTxtData[keyStart]), keyEnd - keyStart,
180 &aTxtData[valStart], entryEnd - valStart);
181 }
182
183 r += entrySize + 1;
184 }
185
186 exit:
187 return error;
188 }
189
RemoveSubscriptionCallbacks(uint64_t aSubscriberId)190 void Publisher::RemoveSubscriptionCallbacks(uint64_t aSubscriberId)
191 {
192 mDiscoverCallbacks.remove_if(
193 [aSubscriberId](const DiscoverCallback &aCallback) { return (aCallback.mId == aSubscriberId); });
194 }
195
AddSubscriptionCallbacks(Publisher::DiscoveredServiceInstanceCallback aInstanceCallback,Publisher::DiscoveredHostCallback aHostCallback)196 uint64_t Publisher::AddSubscriptionCallbacks(Publisher::DiscoveredServiceInstanceCallback aInstanceCallback,
197 Publisher::DiscoveredHostCallback aHostCallback)
198 {
199 uint64_t id = mNextSubscriberId++;
200
201 assert(id > 0);
202 mDiscoverCallbacks.emplace_back(id, aInstanceCallback, aHostCallback);
203
204 return id;
205 }
206
OnServiceResolved(std::string aType,DiscoveredInstanceInfo aInstanceInfo)207 void Publisher::OnServiceResolved(std::string aType, DiscoveredInstanceInfo aInstanceInfo)
208 {
209 bool checkToInvoke = false;
210
211 otbrLogInfo("Service %s is resolved successfully: %s %s host %s addresses %zu", aType.c_str(),
212 aInstanceInfo.mRemoved ? "remove" : "add", aInstanceInfo.mName.c_str(), aInstanceInfo.mHostName.c_str(),
213 aInstanceInfo.mAddresses.size());
214
215 if (!aInstanceInfo.mRemoved)
216 {
217 std::string addressesString;
218
219 for (const auto &address : aInstanceInfo.mAddresses)
220 {
221 addressesString += address.ToString() + ",";
222 }
223 if (addressesString.size())
224 {
225 addressesString.pop_back();
226 }
227 otbrLogInfo("addresses: [ %s ]", addressesString.c_str());
228 }
229
230 DnsUtils::CheckServiceNameSanity(aType);
231
232 assert(aInstanceInfo.mNetifIndex > 0);
233
234 if (!aInstanceInfo.mRemoved)
235 {
236 DnsUtils::CheckHostnameSanity(aInstanceInfo.mHostName);
237 }
238
239 UpdateMdnsResponseCounters(mTelemetryInfo.mServiceResolutions, OTBR_ERROR_NONE);
240 UpdateServiceInstanceResolutionEmaLatency(aInstanceInfo.mName, aType, OTBR_ERROR_NONE);
241
242 // The `mDiscoverCallbacks` list can get updated as the callbacks
243 // are invoked. We first mark `mShouldInvoke` on all non-null
244 // service callbacks. We clear it before invoking the callback
245 // and restart the iteration over the `mDiscoverCallbacks` list
246 // to find the next one to signal, since the list may have changed.
247
248 for (DiscoverCallback &callback : mDiscoverCallbacks)
249 {
250 if (callback.mServiceCallback != nullptr)
251 {
252 callback.mShouldInvoke = true;
253 checkToInvoke = true;
254 }
255 }
256
257 while (checkToInvoke)
258 {
259 checkToInvoke = false;
260
261 for (DiscoverCallback &callback : mDiscoverCallbacks)
262 {
263 if (callback.mShouldInvoke)
264 {
265 callback.mShouldInvoke = false;
266 checkToInvoke = true;
267 callback.mServiceCallback(aType, aInstanceInfo);
268 break;
269 }
270 }
271 }
272 }
273
OnServiceRemoved(uint32_t aNetifIndex,std::string aType,std::string aInstanceName)274 void Publisher::OnServiceRemoved(uint32_t aNetifIndex, std::string aType, std::string aInstanceName)
275 {
276 DiscoveredInstanceInfo instanceInfo;
277
278 otbrLogInfo("Service %s.%s is removed from netif %u.", aInstanceName.c_str(), aType.c_str(), aNetifIndex);
279
280 instanceInfo.mRemoved = true;
281 instanceInfo.mNetifIndex = aNetifIndex;
282 instanceInfo.mName = aInstanceName;
283
284 OnServiceResolved(aType, instanceInfo);
285 }
286
OnHostResolved(std::string aHostName,Publisher::DiscoveredHostInfo aHostInfo)287 void Publisher::OnHostResolved(std::string aHostName, Publisher::DiscoveredHostInfo aHostInfo)
288 {
289 bool checkToInvoke = false;
290
291 otbrLogInfo("Host %s is resolved successfully: host %s addresses %zu ttl %u", aHostName.c_str(),
292 aHostInfo.mHostName.c_str(), aHostInfo.mAddresses.size(), aHostInfo.mTtl);
293
294 if (!aHostInfo.mHostName.empty())
295 {
296 DnsUtils::CheckHostnameSanity(aHostInfo.mHostName);
297 }
298
299 UpdateMdnsResponseCounters(mTelemetryInfo.mHostResolutions, OTBR_ERROR_NONE);
300 UpdateHostResolutionEmaLatency(aHostName, OTBR_ERROR_NONE);
301
302 // The `mDiscoverCallbacks` list can get updated as the callbacks
303 // are invoked. We first mark `mShouldInvoke` on all non-null
304 // host callbacks. We clear it before invoking the callback
305 // and restart the iteration over the `mDiscoverCallbacks` list
306 // to find the next one to signal, since the list may have changed.
307
308 for (DiscoverCallback &callback : mDiscoverCallbacks)
309 {
310 if (callback.mHostCallback != nullptr)
311 {
312 callback.mShouldInvoke = true;
313 checkToInvoke = true;
314 }
315 }
316
317 while (checkToInvoke)
318 {
319 checkToInvoke = false;
320
321 for (DiscoverCallback &callback : mDiscoverCallbacks)
322 {
323 if (callback.mShouldInvoke)
324 {
325 callback.mShouldInvoke = false;
326 checkToInvoke = true;
327 callback.mHostCallback(aHostName, aHostInfo);
328 break;
329 }
330 }
331 }
332 }
333
SortSubTypeList(SubTypeList aSubTypeList)334 Publisher::SubTypeList Publisher::SortSubTypeList(SubTypeList aSubTypeList)
335 {
336 std::sort(aSubTypeList.begin(), aSubTypeList.end());
337 return aSubTypeList;
338 }
339
SortAddressList(AddressList aAddressList)340 Publisher::AddressList Publisher::SortAddressList(AddressList aAddressList)
341 {
342 std::sort(aAddressList.begin(), aAddressList.end());
343 return aAddressList;
344 }
345
MakeFullServiceName(const std::string & aName,const std::string & aType)346 std::string Publisher::MakeFullServiceName(const std::string &aName, const std::string &aType)
347 {
348 return aName + "." + aType + ".local";
349 }
350
MakeFullName(const std::string & aName)351 std::string Publisher::MakeFullName(const std::string &aName)
352 {
353 return aName + ".local";
354 }
355
AddServiceRegistration(ServiceRegistrationPtr && aServiceReg)356 void Publisher::AddServiceRegistration(ServiceRegistrationPtr &&aServiceReg)
357 {
358 mServiceRegistrations.emplace(MakeFullServiceName(aServiceReg->mName, aServiceReg->mType), std::move(aServiceReg));
359 }
360
RemoveServiceRegistration(const std::string & aName,const std::string & aType,otbrError aError)361 void Publisher::RemoveServiceRegistration(const std::string &aName, const std::string &aType, otbrError aError)
362 {
363 auto it = mServiceRegistrations.find(MakeFullServiceName(aName, aType));
364 ServiceRegistrationPtr serviceReg;
365
366 otbrLogInfo("Removing service %s.%s", aName.c_str(), aType.c_str());
367 VerifyOrExit(it != mServiceRegistrations.end());
368
369 // Keep the ServiceRegistration around before calling `Complete`
370 // to invoke the callback. This is for avoiding invalid access
371 // to the ServiceRegistration when it's freed from the callback.
372 serviceReg = std::move(it->second);
373 mServiceRegistrations.erase(it);
374 serviceReg->Complete(aError);
375
376 exit:
377 return;
378 }
379
FindServiceRegistration(const std::string & aName,const std::string & aType)380 Publisher::ServiceRegistration *Publisher::FindServiceRegistration(const std::string &aName, const std::string &aType)
381 {
382 auto it = mServiceRegistrations.find(MakeFullServiceName(aName, aType));
383
384 return it != mServiceRegistrations.end() ? it->second.get() : nullptr;
385 }
386
FindServiceRegistration(const std::string & aNameAndType)387 Publisher::ServiceRegistration *Publisher::FindServiceRegistration(const std::string &aNameAndType)
388 {
389 auto it = mServiceRegistrations.find(MakeFullName(aNameAndType));
390
391 return it != mServiceRegistrations.end() ? it->second.get() : nullptr;
392 }
393
HandleDuplicateServiceRegistration(const std::string & aHostName,const std::string & aName,const std::string & aType,const SubTypeList & aSubTypeList,uint16_t aPort,const TxtData & aTxtData,ResultCallback && aCallback)394 Publisher::ResultCallback Publisher::HandleDuplicateServiceRegistration(const std::string &aHostName,
395 const std::string &aName,
396 const std::string &aType,
397 const SubTypeList &aSubTypeList,
398 uint16_t aPort,
399 const TxtData &aTxtData,
400 ResultCallback &&aCallback)
401 {
402 ServiceRegistration *serviceReg = FindServiceRegistration(aName, aType);
403
404 VerifyOrExit(serviceReg != nullptr);
405
406 if (serviceReg->IsOutdated(aHostName, aName, aType, aSubTypeList, aPort, aTxtData))
407 {
408 otbrLogInfo("Removing existing service %s.%s: outdated", aName.c_str(), aType.c_str());
409 RemoveServiceRegistration(aName, aType, OTBR_ERROR_ABORTED);
410 }
411 else if (serviceReg->IsCompleted())
412 {
413 // Returns success if the same service has already been
414 // registered with exactly the same parameters.
415 std::move(aCallback)(OTBR_ERROR_NONE);
416 }
417 else
418 {
419 // If the same service is being registered with the same parameters,
420 // let's join the waiting queue for the result.
421 serviceReg->mCallback = std::bind(
422 [](std::shared_ptr<ResultCallback> aExistingCallback, std::shared_ptr<ResultCallback> aNewCallback,
423 otbrError aError) {
424 std::move (*aExistingCallback)(aError);
425 std::move (*aNewCallback)(aError);
426 },
427 std::make_shared<ResultCallback>(std::move(serviceReg->mCallback)),
428 std::make_shared<ResultCallback>(std::move(aCallback)), std::placeholders::_1);
429 }
430
431 exit:
432 return std::move(aCallback);
433 }
434
HandleDuplicateHostRegistration(const std::string & aName,const AddressList & aAddresses,ResultCallback && aCallback)435 Publisher::ResultCallback Publisher::HandleDuplicateHostRegistration(const std::string &aName,
436 const AddressList &aAddresses,
437 ResultCallback &&aCallback)
438 {
439 HostRegistration *hostReg = FindHostRegistration(aName);
440
441 VerifyOrExit(hostReg != nullptr);
442
443 if (hostReg->IsOutdated(aName, aAddresses))
444 {
445 otbrLogInfo("Removing existing host %s: outdated", aName.c_str());
446 RemoveHostRegistration(hostReg->mName, OTBR_ERROR_ABORTED);
447 }
448 else if (hostReg->IsCompleted())
449 {
450 // Returns success if the same service has already been
451 // registered with exactly the same parameters.
452 std::move(aCallback)(OTBR_ERROR_NONE);
453 }
454 else
455 {
456 // If the same service is being registered with the same parameters,
457 // let's join the waiting queue for the result.
458 hostReg->mCallback = std::bind(
459 [](std::shared_ptr<ResultCallback> aExistingCallback, std::shared_ptr<ResultCallback> aNewCallback,
460 otbrError aError) {
461 std::move (*aExistingCallback)(aError);
462 std::move (*aNewCallback)(aError);
463 },
464 std::make_shared<ResultCallback>(std::move(hostReg->mCallback)),
465 std::make_shared<ResultCallback>(std::move(aCallback)), std::placeholders::_1);
466 }
467
468 exit:
469 return std::move(aCallback);
470 }
471
AddHostRegistration(HostRegistrationPtr && aHostReg)472 void Publisher::AddHostRegistration(HostRegistrationPtr &&aHostReg)
473 {
474 mHostRegistrations.emplace(MakeFullHostName(aHostReg->mName), std::move(aHostReg));
475 }
476
RemoveHostRegistration(const std::string & aName,otbrError aError)477 void Publisher::RemoveHostRegistration(const std::string &aName, otbrError aError)
478 {
479 auto it = mHostRegistrations.find(MakeFullHostName(aName));
480 HostRegistrationPtr hostReg;
481
482 otbrLogInfo("Removing host %s", aName.c_str());
483 VerifyOrExit(it != mHostRegistrations.end());
484
485 // Keep the HostRegistration around before calling `Complete`
486 // to invoke the callback. This is for avoiding invalid access
487 // to the HostRegistration when it's freed from the callback.
488 hostReg = std::move(it->second);
489 mHostRegistrations.erase(it);
490 hostReg->Complete(aError);
491 otbrLogInfo("Removed host %s", aName.c_str());
492
493 exit:
494 return;
495 }
496
FindHostRegistration(const std::string & aName)497 Publisher::HostRegistration *Publisher::FindHostRegistration(const std::string &aName)
498 {
499 auto it = mHostRegistrations.find(MakeFullHostName(aName));
500
501 return it != mHostRegistrations.end() ? it->second.get() : nullptr;
502 }
503
HandleDuplicateKeyRegistration(const std::string & aName,const KeyData & aKeyData,ResultCallback && aCallback)504 Publisher::ResultCallback Publisher::HandleDuplicateKeyRegistration(const std::string &aName,
505 const KeyData &aKeyData,
506 ResultCallback &&aCallback)
507 {
508 KeyRegistration *keyReg = FindKeyRegistration(aName);
509
510 VerifyOrExit(keyReg != nullptr);
511
512 if (keyReg->IsOutdated(aName, aKeyData))
513 {
514 otbrLogInfo("Removing existing key %s: outdated", aName.c_str());
515 RemoveKeyRegistration(keyReg->mName, OTBR_ERROR_ABORTED);
516 }
517 else if (keyReg->IsCompleted())
518 {
519 // Returns success if the same key has already been
520 // registered with exactly the same parameters.
521 std::move(aCallback)(OTBR_ERROR_NONE);
522 }
523 else
524 {
525 // If the same key is being registered with the same parameters,
526 // let's join the waiting queue for the result.
527 keyReg->mCallback = std::bind(
528 [](std::shared_ptr<ResultCallback> aExistingCallback, std::shared_ptr<ResultCallback> aNewCallback,
529 otbrError aError) {
530 std::move (*aExistingCallback)(aError);
531 std::move (*aNewCallback)(aError);
532 },
533 std::make_shared<ResultCallback>(std::move(keyReg->mCallback)),
534 std::make_shared<ResultCallback>(std::move(aCallback)), std::placeholders::_1);
535 }
536
537 exit:
538 return std::move(aCallback);
539 }
540
AddKeyRegistration(KeyRegistrationPtr && aKeyReg)541 void Publisher::AddKeyRegistration(KeyRegistrationPtr &&aKeyReg)
542 {
543 mKeyRegistrations.emplace(MakeFullKeyName(aKeyReg->mName), std::move(aKeyReg));
544 }
545
RemoveKeyRegistration(const std::string & aName,otbrError aError)546 void Publisher::RemoveKeyRegistration(const std::string &aName, otbrError aError)
547 {
548 auto it = mKeyRegistrations.find(MakeFullKeyName(aName));
549 KeyRegistrationPtr keyReg;
550
551 otbrLogInfo("Removing key %s", aName.c_str());
552 VerifyOrExit(it != mKeyRegistrations.end());
553
554 // Keep the KeyRegistration around before calling `Complete`
555 // to invoke the callback. This is for avoiding invalid access
556 // to the KeyRegistration when it's freed from the callback.
557 keyReg = std::move(it->second);
558 mKeyRegistrations.erase(it);
559 keyReg->Complete(aError);
560 otbrLogInfo("Removed key %s", aName.c_str());
561
562 exit:
563 return;
564 }
565
FindKeyRegistration(const std::string & aName)566 Publisher::KeyRegistration *Publisher::FindKeyRegistration(const std::string &aName)
567 {
568 auto it = mKeyRegistrations.find(MakeFullKeyName(aName));
569
570 return it != mKeyRegistrations.end() ? it->second.get() : nullptr;
571 }
572
FindKeyRegistration(const std::string & aName,const std::string & aType)573 Publisher::KeyRegistration *Publisher::FindKeyRegistration(const std::string &aName, const std::string &aType)
574 {
575 auto it = mKeyRegistrations.find(MakeFullServiceName(aName, aType));
576
577 return it != mKeyRegistrations.end() ? it->second.get() : nullptr;
578 }
579
~Registration(void)580 Publisher::Registration::~Registration(void)
581 {
582 TriggerCompleteCallback(OTBR_ERROR_ABORTED);
583 }
584
IsOutdated(const std::string & aHostName,const std::string & aName,const std::string & aType,const SubTypeList & aSubTypeList,uint16_t aPort,const TxtData & aTxtData) const585 bool Publisher::ServiceRegistration::IsOutdated(const std::string &aHostName,
586 const std::string &aName,
587 const std::string &aType,
588 const SubTypeList &aSubTypeList,
589 uint16_t aPort,
590 const TxtData &aTxtData) const
591 {
592 return !(mHostName == aHostName && mName == aName && mType == aType && mSubTypeList == aSubTypeList &&
593 mPort == aPort && mTxtData == aTxtData);
594 }
595
Complete(otbrError aError)596 void Publisher::ServiceRegistration::Complete(otbrError aError)
597 {
598 OnComplete(aError);
599 Registration::TriggerCompleteCallback(aError);
600 }
601
OnComplete(otbrError aError)602 void Publisher::ServiceRegistration::OnComplete(otbrError aError)
603 {
604 if (!IsCompleted())
605 {
606 mPublisher->UpdateMdnsResponseCounters(mPublisher->mTelemetryInfo.mServiceRegistrations, aError);
607 mPublisher->UpdateServiceRegistrationEmaLatency(mName, mType, aError);
608 }
609 }
610
IsOutdated(const std::string & aName,const AddressList & aAddresses) const611 bool Publisher::HostRegistration::IsOutdated(const std::string &aName, const AddressList &aAddresses) const
612 {
613 return !(mName == aName && mAddresses == aAddresses);
614 }
615
Complete(otbrError aError)616 void Publisher::HostRegistration::Complete(otbrError aError)
617 {
618 OnComplete(aError);
619 Registration::TriggerCompleteCallback(aError);
620 }
621
OnComplete(otbrError aError)622 void Publisher::HostRegistration::OnComplete(otbrError aError)
623 {
624 if (!IsCompleted())
625 {
626 mPublisher->UpdateMdnsResponseCounters(mPublisher->mTelemetryInfo.mHostRegistrations, aError);
627 mPublisher->UpdateHostRegistrationEmaLatency(mName, aError);
628 }
629 }
630
IsOutdated(const std::string & aName,const KeyData & aKeyData) const631 bool Publisher::KeyRegistration::IsOutdated(const std::string &aName, const KeyData &aKeyData) const
632 {
633 return !(mName == aName && mKeyData == aKeyData);
634 }
635
Complete(otbrError aError)636 void Publisher::KeyRegistration::Complete(otbrError aError)
637 {
638 OnComplete(aError);
639 Registration::TriggerCompleteCallback(aError);
640 }
641
OnComplete(otbrError aError)642 void Publisher::KeyRegistration::OnComplete(otbrError aError)
643 {
644 if (!IsCompleted())
645 {
646 mPublisher->UpdateMdnsResponseCounters(mPublisher->mTelemetryInfo.mKeyRegistrations, aError);
647 mPublisher->UpdateKeyRegistrationEmaLatency(mName, aError);
648 }
649 }
650
UpdateMdnsResponseCounters(otbr::MdnsResponseCounters & aCounters,otbrError aError)651 void Publisher::UpdateMdnsResponseCounters(otbr::MdnsResponseCounters &aCounters, otbrError aError)
652 {
653 switch (aError)
654 {
655 case OTBR_ERROR_NONE:
656 ++aCounters.mSuccess;
657 break;
658 case OTBR_ERROR_NOT_FOUND:
659 ++aCounters.mNotFound;
660 break;
661 case OTBR_ERROR_INVALID_ARGS:
662 ++aCounters.mInvalidArgs;
663 break;
664 case OTBR_ERROR_DUPLICATED:
665 ++aCounters.mDuplicated;
666 break;
667 case OTBR_ERROR_NOT_IMPLEMENTED:
668 ++aCounters.mNotImplemented;
669 break;
670 case OTBR_ERROR_ABORTED:
671 ++aCounters.mAborted;
672 break;
673 case OTBR_ERROR_INVALID_STATE:
674 ++aCounters.mInvalidState;
675 break;
676 case OTBR_ERROR_MDNS:
677 default:
678 ++aCounters.mUnknownError;
679 break;
680 }
681 }
682
UpdateEmaLatency(uint32_t & aEmaLatency,uint32_t aLatency,otbrError aError)683 void Publisher::UpdateEmaLatency(uint32_t &aEmaLatency, uint32_t aLatency, otbrError aError)
684 {
685 VerifyOrExit(aError != OTBR_ERROR_ABORTED);
686
687 if (!aEmaLatency)
688 {
689 aEmaLatency = aLatency;
690 }
691 else
692 {
693 aEmaLatency =
694 (aLatency * MdnsTelemetryInfo::kEmaFactorNumerator +
695 aEmaLatency * (MdnsTelemetryInfo::kEmaFactorDenominator - MdnsTelemetryInfo::kEmaFactorNumerator)) /
696 MdnsTelemetryInfo::kEmaFactorDenominator;
697 }
698
699 exit:
700 return;
701 }
702
UpdateServiceRegistrationEmaLatency(const std::string & aInstanceName,const std::string & aType,otbrError aError)703 void Publisher::UpdateServiceRegistrationEmaLatency(const std::string &aInstanceName,
704 const std::string &aType,
705 otbrError aError)
706 {
707 auto it = mServiceRegistrationBeginTime.find(std::make_pair(aInstanceName, aType));
708
709 if (it != mServiceRegistrationBeginTime.end())
710 {
711 uint32_t latency = std::chrono::duration_cast<Milliseconds>(Clock::now() - it->second).count();
712 UpdateEmaLatency(mTelemetryInfo.mServiceRegistrationEmaLatency, latency, aError);
713 mServiceRegistrationBeginTime.erase(it);
714 }
715 }
716
UpdateHostRegistrationEmaLatency(const std::string & aHostName,otbrError aError)717 void Publisher::UpdateHostRegistrationEmaLatency(const std::string &aHostName, otbrError aError)
718 {
719 auto it = mHostRegistrationBeginTime.find(aHostName);
720
721 if (it != mHostRegistrationBeginTime.end())
722 {
723 uint32_t latency = std::chrono::duration_cast<Milliseconds>(Clock::now() - it->second).count();
724 UpdateEmaLatency(mTelemetryInfo.mHostRegistrationEmaLatency, latency, aError);
725 mHostRegistrationBeginTime.erase(it);
726 }
727 }
728
UpdateKeyRegistrationEmaLatency(const std::string & aKeyName,otbrError aError)729 void Publisher::UpdateKeyRegistrationEmaLatency(const std::string &aKeyName, otbrError aError)
730 {
731 auto it = mKeyRegistrationBeginTime.find(aKeyName);
732
733 if (it != mKeyRegistrationBeginTime.end())
734 {
735 uint32_t latency = std::chrono::duration_cast<Milliseconds>(Clock::now() - it->second).count();
736 UpdateEmaLatency(mTelemetryInfo.mKeyRegistrationEmaLatency, latency, aError);
737 mKeyRegistrationBeginTime.erase(it);
738 }
739 }
740
UpdateServiceInstanceResolutionEmaLatency(const std::string & aInstanceName,const std::string & aType,otbrError aError)741 void Publisher::UpdateServiceInstanceResolutionEmaLatency(const std::string &aInstanceName,
742 const std::string &aType,
743 otbrError aError)
744 {
745 auto it = mServiceInstanceResolutionBeginTime.find(std::make_pair(aInstanceName, aType));
746
747 if (it != mServiceInstanceResolutionBeginTime.end())
748 {
749 uint32_t latency = std::chrono::duration_cast<Milliseconds>(Clock::now() - it->second).count();
750 UpdateEmaLatency(mTelemetryInfo.mServiceResolutionEmaLatency, latency, aError);
751 mServiceInstanceResolutionBeginTime.erase(it);
752 }
753 }
754
UpdateHostResolutionEmaLatency(const std::string & aHostName,otbrError aError)755 void Publisher::UpdateHostResolutionEmaLatency(const std::string &aHostName, otbrError aError)
756 {
757 auto it = mHostResolutionBeginTime.find(aHostName);
758
759 if (it != mHostResolutionBeginTime.end())
760 {
761 uint32_t latency = std::chrono::duration_cast<Milliseconds>(Clock::now() - it->second).count();
762 UpdateEmaLatency(mTelemetryInfo.mHostResolutionEmaLatency, latency, aError);
763 mHostResolutionBeginTime.erase(it);
764 }
765 }
766
AddAddress(AddressList & aAddressList,const Ip6Address & aAddress)767 void Publisher::AddAddress(AddressList &aAddressList, const Ip6Address &aAddress)
768 {
769 aAddressList.push_back(aAddress);
770 }
771
RemoveAddress(AddressList & aAddressList,const Ip6Address & aAddress)772 void Publisher::RemoveAddress(AddressList &aAddressList, const Ip6Address &aAddress)
773 {
774 auto it = std::find(aAddressList.begin(), aAddressList.end(), aAddress);
775
776 if (it != aAddressList.end())
777 {
778 aAddressList.erase(it);
779 }
780 }
781
782 } // namespace Mdns
783 } // namespace otbr
784
785 #endif // OTBR_ENABLE_MDNS
786