xref: /aosp_15_r20/external/ot-br-posix/src/mdns/mdns.cpp (revision 4a64e381480ef79f0532b2421e44e6ee336b8e0d)
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