• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include <assert.h>
39 
40 #include <algorithm>
41 #include <functional>
42 
43 #include "common/code_utils.hpp"
44 #include "utils/dns_utils.hpp"
45 
46 namespace otbr {
47 
48 namespace Mdns {
49 
PublishService(const std::string & aHostName,const std::string & aName,const std::string & aType,const SubTypeList & aSubTypeList,uint16_t aPort,const TxtList & aTxtList,ResultCallback && aCallback)50 void Publisher::PublishService(const std::string &aHostName,
51                                const std::string &aName,
52                                const std::string &aType,
53                                const SubTypeList &aSubTypeList,
54                                uint16_t           aPort,
55                                const TxtList &    aTxtList,
56                                ResultCallback &&  aCallback)
57 {
58     mServiceRegistrationBeginTime[std::make_pair(aName, aType)] = Clock::now();
59 
60     PublishServiceImpl(aHostName, aName, aType, aSubTypeList, aPort, aTxtList, std::move(aCallback));
61 }
62 
PublishHost(const std::string & aName,const std::vector<uint8_t> & aAddress,ResultCallback && aCallback)63 void Publisher::PublishHost(const std::string &aName, const std::vector<uint8_t> &aAddress, ResultCallback &&aCallback)
64 {
65     mHostRegistrationBeginTime[aName] = Clock::now();
66 
67     PublishHostImpl(aName, aAddress, std::move(aCallback));
68 }
69 
OnServiceResolveFailed(const std::string & aType,const std::string & aInstanceName,int32_t aErrorCode)70 void Publisher::OnServiceResolveFailed(const std::string &aType, const std::string &aInstanceName, int32_t aErrorCode)
71 {
72     UpdateMdnsResponseCounters(mTelemetryInfo.mServiceResolutions, DnsErrorToOtbrError(aErrorCode));
73     UpdateServiceInstanceResolutionEmaLatency(aInstanceName, aType, DnsErrorToOtbrError(aErrorCode));
74     OnServiceResolveFailedImpl(aType, aInstanceName, aErrorCode);
75 }
76 
OnHostResolveFailed(const std::string & aHostName,int32_t aErrorCode)77 void Publisher::OnHostResolveFailed(const std::string &aHostName, int32_t aErrorCode)
78 {
79     UpdateMdnsResponseCounters(mTelemetryInfo.mHostResolutions, DnsErrorToOtbrError(aErrorCode));
80     UpdateHostResolutionEmaLatency(aHostName, DnsErrorToOtbrError(aErrorCode));
81     OnHostResolveFailedImpl(aHostName, aErrorCode);
82 }
83 
EncodeTxtData(const TxtList & aTxtList,std::vector<uint8_t> & aTxtData)84 otbrError Publisher::EncodeTxtData(const TxtList &aTxtList, std::vector<uint8_t> &aTxtData)
85 {
86     otbrError error = OTBR_ERROR_NONE;
87 
88     for (const auto &txtEntry : aTxtList)
89     {
90         const auto & name        = txtEntry.mName;
91         const auto & value       = txtEntry.mValue;
92         const size_t entryLength = name.length() + 1 + value.size();
93 
94         VerifyOrExit(entryLength <= kMaxTextEntrySize, error = OTBR_ERROR_INVALID_ARGS);
95 
96         aTxtData.push_back(static_cast<uint8_t>(entryLength));
97         aTxtData.insert(aTxtData.end(), name.begin(), name.end());
98         aTxtData.push_back('=');
99         aTxtData.insert(aTxtData.end(), value.begin(), value.end());
100     }
101 
102 exit:
103     return error;
104 }
105 
DecodeTxtData(Publisher::TxtList & aTxtList,const uint8_t * aTxtData,uint16_t aTxtLength)106 otbrError Publisher::DecodeTxtData(Publisher::TxtList &aTxtList, const uint8_t *aTxtData, uint16_t aTxtLength)
107 {
108     otbrError error = OTBR_ERROR_NONE;
109 
110     for (uint16_t r = 0; r < aTxtLength;)
111     {
112         uint16_t entrySize = aTxtData[r];
113         uint16_t keyStart  = r + 1;
114         uint16_t entryEnd  = keyStart + entrySize;
115         uint16_t keyEnd    = keyStart;
116         uint16_t valStart;
117 
118         while (keyEnd < entryEnd && aTxtData[keyEnd] != '=')
119         {
120             keyEnd++;
121         }
122 
123         valStart = keyEnd;
124         if (valStart < entryEnd && aTxtData[valStart] == '=')
125         {
126             valStart++;
127         }
128 
129         aTxtList.emplace_back(reinterpret_cast<const char *>(&aTxtData[keyStart]), keyEnd - keyStart,
130                               &aTxtData[valStart], entryEnd - valStart);
131 
132         r += entrySize + 1;
133         VerifyOrExit(r <= aTxtLength, error = OTBR_ERROR_PARSE);
134     }
135 
136 exit:
137     return error;
138 }
139 
RemoveSubscriptionCallbacks(uint64_t aSubscriberId)140 void Publisher::RemoveSubscriptionCallbacks(uint64_t aSubscriberId)
141 {
142     size_t erased;
143 
144     OTBR_UNUSED_VARIABLE(erased);
145 
146     assert(aSubscriberId > 0);
147 
148     erased = mDiscoveredCallbacks.erase(aSubscriberId);
149 
150     assert(erased == 1);
151 }
152 
AddSubscriptionCallbacks(Publisher::DiscoveredServiceInstanceCallback aInstanceCallback,Publisher::DiscoveredHostCallback aHostCallback)153 uint64_t Publisher::AddSubscriptionCallbacks(Publisher::DiscoveredServiceInstanceCallback aInstanceCallback,
154                                              Publisher::DiscoveredHostCallback            aHostCallback)
155 {
156     uint64_t subscriberId = mNextSubscriberId++;
157 
158     assert(subscriberId > 0);
159 
160     mDiscoveredCallbacks.emplace(subscriberId, std::make_pair(std::move(aInstanceCallback), std::move(aHostCallback)));
161     return subscriberId;
162 }
163 
OnServiceResolved(const std::string & aType,const DiscoveredInstanceInfo & aInstanceInfo)164 void Publisher::OnServiceResolved(const std::string &aType, const DiscoveredInstanceInfo &aInstanceInfo)
165 {
166     otbrLogInfo("Service %s is resolved successfully: %s %s host %s addresses %zu", aType.c_str(),
167                 aInstanceInfo.mRemoved ? "remove" : "add", aInstanceInfo.mName.c_str(), aInstanceInfo.mHostName.c_str(),
168                 aInstanceInfo.mAddresses.size());
169 
170     DnsUtils::CheckServiceNameSanity(aType);
171 
172     assert(aInstanceInfo.mNetifIndex > 0);
173 
174     if (!aInstanceInfo.mRemoved)
175     {
176         DnsUtils::CheckHostnameSanity(aInstanceInfo.mHostName);
177     }
178 
179     UpdateMdnsResponseCounters(mTelemetryInfo.mServiceResolutions, OTBR_ERROR_NONE);
180     UpdateServiceInstanceResolutionEmaLatency(aInstanceInfo.mName, aType, OTBR_ERROR_NONE);
181 
182     for (const auto &subCallback : mDiscoveredCallbacks)
183     {
184         if (subCallback.second.first != nullptr)
185         {
186             subCallback.second.first(aType, aInstanceInfo);
187         }
188     }
189 }
190 
OnServiceRemoved(uint32_t aNetifIndex,const std::string & aType,const std::string & aInstanceName)191 void Publisher::OnServiceRemoved(uint32_t aNetifIndex, const std::string &aType, const std::string &aInstanceName)
192 {
193     DiscoveredInstanceInfo instanceInfo;
194 
195     otbrLogInfo("Service %s.%s is removed from netif %u.", aInstanceName.c_str(), aType.c_str(), aNetifIndex);
196 
197     instanceInfo.mRemoved    = true;
198     instanceInfo.mNetifIndex = aNetifIndex;
199     instanceInfo.mName       = aInstanceName;
200 
201     OnServiceResolved(aType, instanceInfo);
202 }
203 
OnHostResolved(const std::string & aHostName,const Publisher::DiscoveredHostInfo & aHostInfo)204 void Publisher::OnHostResolved(const std::string &aHostName, const Publisher::DiscoveredHostInfo &aHostInfo)
205 {
206     otbrLogInfo("Host %s is resolved successfully: host %s addresses %zu ttl %u", aHostName.c_str(),
207                 aHostInfo.mHostName.c_str(), aHostInfo.mAddresses.size(), aHostInfo.mTtl);
208 
209     if (!aHostInfo.mHostName.empty())
210     {
211         DnsUtils::CheckHostnameSanity(aHostInfo.mHostName);
212     }
213 
214     UpdateMdnsResponseCounters(mTelemetryInfo.mHostResolutions, OTBR_ERROR_NONE);
215     UpdateHostResolutionEmaLatency(aHostName, OTBR_ERROR_NONE);
216 
217     for (const auto &subCallback : mDiscoveredCallbacks)
218     {
219         if (subCallback.second.second != nullptr)
220         {
221             subCallback.second.second(aHostName, aHostInfo);
222         }
223     }
224 }
225 
SortSubTypeList(SubTypeList aSubTypeList)226 Publisher::SubTypeList Publisher::SortSubTypeList(SubTypeList aSubTypeList)
227 {
228     std::sort(aSubTypeList.begin(), aSubTypeList.end());
229     return aSubTypeList;
230 }
231 
SortTxtList(TxtList aTxtList)232 Publisher::TxtList Publisher::SortTxtList(TxtList aTxtList)
233 {
234     std::sort(aTxtList.begin(), aTxtList.end(),
235               [](const TxtEntry &aLhs, const TxtEntry &aRhs) { return aLhs.mName < aRhs.mName; });
236     return aTxtList;
237 }
238 
MakeFullServiceName(const std::string & aName,const std::string & aType)239 std::string Publisher::MakeFullServiceName(const std::string &aName, const std::string &aType)
240 {
241     return aName + "." + aType + ".local";
242 }
243 
MakeFullHostName(const std::string & aName)244 std::string Publisher::MakeFullHostName(const std::string &aName)
245 {
246     return aName + ".local";
247 }
248 
AddServiceRegistration(ServiceRegistrationPtr && aServiceReg)249 void Publisher::AddServiceRegistration(ServiceRegistrationPtr &&aServiceReg)
250 {
251     mServiceRegistrations.emplace(MakeFullServiceName(aServiceReg->mName, aServiceReg->mType), std::move(aServiceReg));
252 }
253 
RemoveServiceRegistration(const std::string & aName,const std::string & aType,otbrError aError)254 void Publisher::RemoveServiceRegistration(const std::string &aName, const std::string &aType, otbrError aError)
255 {
256     auto                   it = mServiceRegistrations.find(MakeFullServiceName(aName, aType));
257     ServiceRegistrationPtr serviceReg;
258 
259     otbrLogInfo("Removing service %s.%s", aName.c_str(), aType.c_str());
260     VerifyOrExit(it != mServiceRegistrations.end());
261 
262     // Keep the ServiceRegistration around before calling `Complete`
263     // to invoke the callback. This is for avoiding invalid access
264     // to the ServiceRegistration when it's freed from the callback.
265     serviceReg = std::move(it->second);
266     mServiceRegistrations.erase(it);
267     serviceReg->Complete(aError);
268 
269 exit:
270     return;
271 }
272 
FindServiceRegistration(const std::string & aName,const std::string & aType)273 Publisher::ServiceRegistration *Publisher::FindServiceRegistration(const std::string &aName, const std::string &aType)
274 {
275     auto it = mServiceRegistrations.find(MakeFullServiceName(aName, aType));
276 
277     return it != mServiceRegistrations.end() ? it->second.get() : nullptr;
278 }
279 
HandleDuplicateServiceRegistration(const std::string & aHostName,const std::string & aName,const std::string & aType,const SubTypeList & aSubTypeList,uint16_t aPort,const TxtList & aTxtList,ResultCallback && aCallback)280 Publisher::ResultCallback Publisher::HandleDuplicateServiceRegistration(const std::string &aHostName,
281                                                                         const std::string &aName,
282                                                                         const std::string &aType,
283                                                                         const SubTypeList &aSubTypeList,
284                                                                         uint16_t           aPort,
285                                                                         const TxtList &    aTxtList,
286                                                                         ResultCallback &&  aCallback)
287 {
288     ServiceRegistration *serviceReg = FindServiceRegistration(aName, aType);
289 
290     VerifyOrExit(serviceReg != nullptr);
291 
292     if (serviceReg->IsOutdated(aHostName, aName, aType, aSubTypeList, aPort, aTxtList))
293     {
294         otbrLogInfo("Removing existing service %s.%s: outdated", aName.c_str(), aType.c_str());
295         RemoveServiceRegistration(aName, aType, OTBR_ERROR_ABORTED);
296     }
297     else if (serviceReg->IsCompleted())
298     {
299         // Returns success if the same service has already been
300         // registered with exactly the same parameters.
301         std::move(aCallback)(OTBR_ERROR_NONE);
302     }
303     else
304     {
305         // If the same service is being registered with the same parameters,
306         // let's join the waiting queue for the result.
307         serviceReg->mCallback = std::bind(
308             [](std::shared_ptr<ResultCallback> aExistingCallback, std::shared_ptr<ResultCallback> aNewCallback,
309                otbrError aError) {
310                 std::move (*aExistingCallback)(aError);
311                 std::move (*aNewCallback)(aError);
312             },
313             std::make_shared<ResultCallback>(std::move(serviceReg->mCallback)),
314             std::make_shared<ResultCallback>(std::move(aCallback)), std::placeholders::_1);
315     }
316 
317 exit:
318     return std::move(aCallback);
319 }
320 
HandleDuplicateHostRegistration(const std::string & aName,const std::vector<uint8_t> & aAddress,ResultCallback && aCallback)321 Publisher::ResultCallback Publisher::HandleDuplicateHostRegistration(const std::string &         aName,
322                                                                      const std::vector<uint8_t> &aAddress,
323                                                                      ResultCallback &&           aCallback)
324 {
325     HostRegistration *hostReg = FindHostRegistration(aName);
326 
327     VerifyOrExit(hostReg != nullptr);
328 
329     if (hostReg->IsOutdated(aName, aAddress))
330     {
331         otbrLogInfo("Removing existing host %s: outdated", aName.c_str());
332         RemoveHostRegistration(hostReg->mName, OTBR_ERROR_ABORTED);
333     }
334     else if (hostReg->IsCompleted())
335     {
336         // Returns success if the same service has already been
337         // registered with exactly the same parameters.
338         std::move(aCallback)(OTBR_ERROR_NONE);
339     }
340     else
341     {
342         // If the same service is being registered with the same parameters,
343         // let's join the waiting queue for the result.
344         hostReg->mCallback = std::bind(
345             [](std::shared_ptr<ResultCallback> aExistingCallback, std::shared_ptr<ResultCallback> aNewCallback,
346                otbrError aError) {
347                 std::move (*aExistingCallback)(aError);
348                 std::move (*aNewCallback)(aError);
349             },
350             std::make_shared<ResultCallback>(std::move(hostReg->mCallback)),
351             std::make_shared<ResultCallback>(std::move(aCallback)), std::placeholders::_1);
352     }
353 
354 exit:
355     return std::move(aCallback);
356 }
357 
AddHostRegistration(HostRegistrationPtr && aHostReg)358 void Publisher::AddHostRegistration(HostRegistrationPtr &&aHostReg)
359 {
360     mHostRegistrations.emplace(MakeFullHostName(aHostReg->mName), std::move(aHostReg));
361 }
362 
RemoveHostRegistration(const std::string & aName,otbrError aError)363 void Publisher::RemoveHostRegistration(const std::string &aName, otbrError aError)
364 {
365     auto                it = mHostRegistrations.find(MakeFullHostName(aName));
366     HostRegistrationPtr hostReg;
367 
368     otbrLogInfo("Removing host %s", aName.c_str());
369     VerifyOrExit(it != mHostRegistrations.end());
370 
371     // Keep the HostRegistration around before calling `Complete`
372     // to invoke the callback. This is for avoiding invalid access
373     // to the HostRegistration when it's freed from the callback.
374     hostReg = std::move(it->second);
375     mHostRegistrations.erase(it);
376     hostReg->Complete(aError);
377 
378 exit:
379     return;
380 }
381 
FindHostRegistration(const std::string & aName)382 Publisher::HostRegistration *Publisher::FindHostRegistration(const std::string &aName)
383 {
384     auto it = mHostRegistrations.find(MakeFullHostName(aName));
385 
386     return it != mHostRegistrations.end() ? it->second.get() : nullptr;
387 }
388 
~Registration(void)389 Publisher::Registration::~Registration(void)
390 {
391     TriggerCompleteCallback(OTBR_ERROR_ABORTED);
392 }
393 
IsOutdated(const std::string & aHostName,const std::string & aName,const std::string & aType,const SubTypeList & aSubTypeList,uint16_t aPort,const TxtList & aTxtList) const394 bool Publisher::ServiceRegistration::IsOutdated(const std::string &aHostName,
395                                                 const std::string &aName,
396                                                 const std::string &aType,
397                                                 const SubTypeList &aSubTypeList,
398                                                 uint16_t           aPort,
399                                                 const TxtList &    aTxtList) const
400 {
401     return !(mHostName == aHostName && mName == aName && mType == aType && mSubTypeList == aSubTypeList &&
402              mPort == aPort && mTxtList == aTxtList);
403 }
404 
Complete(otbrError aError)405 void Publisher::ServiceRegistration::Complete(otbrError aError)
406 {
407     OnComplete(aError);
408     Registration::TriggerCompleteCallback(aError);
409 }
410 
OnComplete(otbrError aError)411 void Publisher::ServiceRegistration::OnComplete(otbrError aError)
412 {
413     if (!IsCompleted())
414     {
415         mPublisher->UpdateMdnsResponseCounters(mPublisher->mTelemetryInfo.mServiceRegistrations, aError);
416         mPublisher->UpdateServiceRegistrationEmaLatency(mName, mType, aError);
417     }
418 }
419 
IsOutdated(const std::string & aName,const std::vector<uint8_t> & aAddress) const420 bool Publisher::HostRegistration::IsOutdated(const std::string &aName, const std::vector<uint8_t> &aAddress) const
421 {
422     return !(mName == aName && mAddress == aAddress);
423 }
424 
Complete(otbrError aError)425 void Publisher::HostRegistration::Complete(otbrError aError)
426 {
427     OnComplete(aError);
428     Registration::TriggerCompleteCallback(aError);
429 }
430 
OnComplete(otbrError aError)431 void Publisher::HostRegistration::OnComplete(otbrError aError)
432 {
433     if (!IsCompleted())
434     {
435         mPublisher->UpdateMdnsResponseCounters(mPublisher->mTelemetryInfo.mHostRegistrations, aError);
436         mPublisher->UpdateHostRegistrationEmaLatency(mName, aError);
437     }
438 }
439 
UpdateMdnsResponseCounters(otbr::MdnsResponseCounters & aCounters,otbrError aError)440 void Publisher::UpdateMdnsResponseCounters(otbr::MdnsResponseCounters &aCounters, otbrError aError)
441 {
442     switch (aError)
443     {
444     case OTBR_ERROR_NONE:
445         ++aCounters.mSuccess;
446         break;
447     case OTBR_ERROR_NOT_FOUND:
448         ++aCounters.mNotFound;
449         break;
450     case OTBR_ERROR_INVALID_ARGS:
451         ++aCounters.mInvalidArgs;
452         break;
453     case OTBR_ERROR_DUPLICATED:
454         ++aCounters.mDuplicated;
455         break;
456     case OTBR_ERROR_NOT_IMPLEMENTED:
457         ++aCounters.mNotImplemented;
458         break;
459     case OTBR_ERROR_MDNS:
460     default:
461         ++aCounters.mUnknownError;
462         break;
463     }
464 }
465 
UpdateEmaLatency(uint32_t & aEmaLatency,uint32_t aLatency,otbrError aError)466 void Publisher::UpdateEmaLatency(uint32_t &aEmaLatency, uint32_t aLatency, otbrError aError)
467 {
468     VerifyOrExit(aError != OTBR_ERROR_ABORTED);
469 
470     if (!aEmaLatency)
471     {
472         aEmaLatency = aLatency;
473     }
474     else
475     {
476         aEmaLatency =
477             (aLatency * MdnsTelemetryInfo::kEmaFactorNumerator +
478              aEmaLatency * (MdnsTelemetryInfo::kEmaFactorDenominator - MdnsTelemetryInfo::kEmaFactorNumerator)) /
479             MdnsTelemetryInfo::kEmaFactorDenominator;
480     }
481 
482 exit:
483     return;
484 }
485 
UpdateServiceRegistrationEmaLatency(const std::string & aInstanceName,const std::string & aType,otbrError aError)486 void Publisher::UpdateServiceRegistrationEmaLatency(const std::string &aInstanceName,
487                                                     const std::string &aType,
488                                                     otbrError          aError)
489 {
490     auto it = mServiceRegistrationBeginTime.find(std::make_pair(aInstanceName, aType));
491 
492     if (it != mServiceRegistrationBeginTime.end())
493     {
494         uint32_t latency = std::chrono::duration_cast<Milliseconds>(Clock::now() - it->second).count();
495         UpdateEmaLatency(mTelemetryInfo.mServiceRegistrationEmaLatency, latency, aError);
496         mServiceRegistrationBeginTime.erase(it);
497     }
498 }
499 
UpdateHostRegistrationEmaLatency(const std::string & aHostName,otbrError aError)500 void Publisher::UpdateHostRegistrationEmaLatency(const std::string &aHostName, otbrError aError)
501 {
502     auto it = mHostRegistrationBeginTime.find(aHostName);
503 
504     if (it != mHostRegistrationBeginTime.end())
505     {
506         uint32_t latency = std::chrono::duration_cast<Milliseconds>(Clock::now() - it->second).count();
507         UpdateEmaLatency(mTelemetryInfo.mHostRegistrationEmaLatency, latency, aError);
508         mHostRegistrationBeginTime.erase(it);
509     }
510 }
511 
UpdateServiceInstanceResolutionEmaLatency(const std::string & aInstanceName,const std::string & aType,otbrError aError)512 void Publisher::UpdateServiceInstanceResolutionEmaLatency(const std::string &aInstanceName,
513                                                           const std::string &aType,
514                                                           otbrError          aError)
515 {
516     auto it = mServiceInstanceResolutionBeginTime.find(std::make_pair(aInstanceName, aType));
517 
518     if (it != mServiceInstanceResolutionBeginTime.end())
519     {
520         uint32_t latency = std::chrono::duration_cast<Milliseconds>(Clock::now() - it->second).count();
521         UpdateEmaLatency(mTelemetryInfo.mServiceResolutionEmaLatency, latency, aError);
522         mServiceInstanceResolutionBeginTime.erase(it);
523     }
524 }
525 
UpdateHostResolutionEmaLatency(const std::string & aHostName,otbrError aError)526 void Publisher::UpdateHostResolutionEmaLatency(const std::string &aHostName, otbrError aError)
527 {
528     auto it = mHostResolutionBeginTime.find(aHostName);
529 
530     if (it != mHostResolutionBeginTime.end())
531     {
532         uint32_t latency = std::chrono::duration_cast<Milliseconds>(Clock::now() - it->second).count();
533         UpdateEmaLatency(mTelemetryInfo.mHostResolutionEmaLatency, latency, aError);
534         mHostResolutionBeginTime.erase(it);
535     }
536 }
537 
538 } // namespace Mdns
539 
540 } // namespace otbr
541