1 /*
2 * Copyright (c) 2024, 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 #define OTBR_LOG_TAG "MDNS"
30
31 #include "android/mdns_publisher.hpp"
32
33 namespace otbr {
34
Create(Mdns::Publisher::StateCallback aCallback)35 Mdns::Publisher *Mdns::Publisher::Create(Mdns::Publisher::StateCallback aCallback)
36 {
37 return new Android::MdnsPublisher(std::move(aCallback));
38 }
39
40 namespace Android {
DnsErrorToOtbrErrorImpl(int32_t aError)41 otbrError DnsErrorToOtbrErrorImpl(int32_t aError)
42 {
43 return aError == 0 ? OTBR_ERROR_NONE : OTBR_ERROR_MDNS;
44 }
45
DnsErrorToOtbrError(int32_t aError)46 otbrError MdnsPublisher::DnsErrorToOtbrError(int32_t aError)
47 {
48 return DnsErrorToOtbrErrorImpl(aError);
49 }
50
onSuccess()51 Status MdnsPublisher::NsdStatusReceiver::onSuccess()
52 {
53 if (!mCallback.IsNull())
54 {
55 std::move(mCallback)(OTBR_ERROR_NONE);
56 }
57
58 return Status::ok();
59 }
60
onServiceDiscovered(const std::string & aName,const std::string & aType,bool aIsFound)61 Status MdnsPublisher::NsdDiscoverServiceCallback::onServiceDiscovered(const std::string &aName,
62 const std::string &aType,
63 bool aIsFound)
64 {
65 std::shared_ptr<ServiceSubscription> subscription = mSubscription.lock();
66
67 VerifyOrExit(subscription != nullptr);
68 VerifyOrExit(aIsFound, subscription->mPublisher.OnServiceRemoved(0, aType, aName));
69
70 subscription->Resolve(aName, aType);
71
72 exit:
73 return Status::ok();
74 }
75
onServiceResolved(const std::string & aHostname,int aNetifIndex,const std::string & aName,const std::string & aType,int aPort,const std::vector<std::string> & aAddresses,const std::vector<DnsTxtAttribute> & aTxt,int aTtlSeconds)76 Status MdnsPublisher::NsdResolveServiceCallback::onServiceResolved(const std::string &aHostname,
77 int aNetifIndex,
78 const std::string &aName,
79 const std::string &aType,
80 int aPort,
81 const std::vector<std::string> &aAddresses,
82 const std::vector<DnsTxtAttribute> &aTxt,
83 int aTtlSeconds)
84 {
85 DiscoveredInstanceInfo info;
86 TxtList txtList;
87 std::shared_ptr<ServiceSubscription> subscription = mSubscription.lock();
88
89 VerifyOrExit(subscription != nullptr);
90
91 info.mHostName = aHostname + ".local.";
92 info.mName = aName;
93 info.mPort = aPort;
94 info.mTtl = std::clamp(aTtlSeconds, kMinResolvedTtl, kMaxResolvedTtl);
95 info.mNetifIndex = aNetifIndex;
96 for (const auto &addressStr : aAddresses)
97 {
98 Ip6Address address;
99 // addressStr may be in the format of "fe80::1234%eth0"
100 std::string addrStr(addressStr.begin(), std::find(addressStr.begin(), addressStr.end(), '%'));
101 int error = Ip6Address::FromString(addrStr.c_str(), address);
102
103 if (error != OTBR_ERROR_NONE)
104 {
105 otbrLogInfo("Failed to parse resolved IPv6 address: %s", addressStr.c_str());
106 continue;
107 }
108 info.mAddresses.push_back(address);
109 }
110 for (const auto &entry : aTxt)
111 {
112 txtList.emplace_back(entry.name.c_str(), entry.value.data(), entry.value.size());
113 }
114 EncodeTxtData(txtList, info.mTxtData);
115
116 subscription->mPublisher.OnServiceResolved(aType, info);
117
118 exit:
119 return Status::ok();
120 }
121
onHostResolved(const std::string & aName,const std::vector<std::string> & aAddresses)122 Status MdnsPublisher::NsdResolveHostCallback::onHostResolved(const std::string &aName,
123 const std::vector<std::string> &aAddresses)
124 {
125 DiscoveredHostInfo info;
126 std::shared_ptr<HostSubscription> subscription = mSubscription.lock();
127
128 VerifyOrExit(subscription != nullptr);
129
130 info.mTtl = kDefaultResolvedTtl;
131 for (const auto &addressStr : aAddresses)
132 {
133 Ip6Address address;
134 int error = Ip6Address::FromString(addressStr.c_str(), address);
135
136 if (error != OTBR_ERROR_NONE)
137 {
138 otbrLogInfo("Failed to parse resolved IPv6 address: %s", addressStr.c_str());
139 continue;
140 }
141 info.mAddresses.push_back(address);
142 }
143
144 subscription->mPublisher.OnHostResolved(aName, info);
145
146 exit:
147 return Status::ok();
148 }
149
SetINsdPublisher(std::shared_ptr<INsdPublisher> aINsdPublisher)150 void MdnsPublisher::SetINsdPublisher(std::shared_ptr<INsdPublisher> aINsdPublisher)
151 {
152 otbrLogInfo("Set INsdPublisher %p", aINsdPublisher.get());
153
154 mNsdPublisher = std::move(aINsdPublisher);
155
156 if (mNsdPublisher != nullptr)
157 {
158 mStateCallback(Mdns::Publisher::State::kReady);
159 }
160 else
161 {
162 mStateCallback(Mdns::Publisher::State::kIdle);
163 }
164 }
165
onError(int aError)166 Status MdnsPublisher::NsdStatusReceiver::onError(int aError)
167 {
168 if (!mCallback.IsNull())
169 {
170 std::move(mCallback)(DnsErrorToOtbrErrorImpl(aError));
171 }
172
173 return Status::ok();
174 }
175
CreateReceiver(Mdns::Publisher::ResultCallback aCallback)176 std::shared_ptr<MdnsPublisher::NsdStatusReceiver> CreateReceiver(Mdns::Publisher::ResultCallback aCallback)
177 {
178 return ndk::SharedRefBase::make<MdnsPublisher::NsdStatusReceiver>(std::move(aCallback));
179 }
180
CreateNsdDiscoverServiceCallback(const std::shared_ptr<MdnsPublisher::ServiceSubscription> & aServiceSubscription)181 std::shared_ptr<MdnsPublisher::NsdDiscoverServiceCallback> CreateNsdDiscoverServiceCallback(
182 const std::shared_ptr<MdnsPublisher::ServiceSubscription> &aServiceSubscription)
183 {
184 return ndk::SharedRefBase::make<MdnsPublisher::NsdDiscoverServiceCallback>(aServiceSubscription);
185 }
186
CreateNsdResolveServiceCallback(const std::shared_ptr<MdnsPublisher::ServiceSubscription> & aServiceSubscription)187 std::shared_ptr<MdnsPublisher::NsdResolveServiceCallback> CreateNsdResolveServiceCallback(
188 const std::shared_ptr<MdnsPublisher::ServiceSubscription> &aServiceSubscription)
189 {
190 return ndk::SharedRefBase::make<MdnsPublisher::NsdResolveServiceCallback>(aServiceSubscription);
191 }
192
CreateNsdResolveHostCallback(const std::shared_ptr<MdnsPublisher::HostSubscription> & aHostSubscription)193 std::shared_ptr<MdnsPublisher::NsdResolveHostCallback> CreateNsdResolveHostCallback(
194 const std::shared_ptr<MdnsPublisher::HostSubscription> &aHostSubscription)
195 {
196 return ndk::SharedRefBase::make<MdnsPublisher::NsdResolveHostCallback>(aHostSubscription);
197 }
198
DieForNotImplemented(const char * aFuncName)199 void DieForNotImplemented(const char *aFuncName)
200 {
201 VerifyOrDie(false, (std::string(aFuncName) + " is not implemented").c_str());
202 }
203
PublishServiceImpl(const std::string & aHostName,const std::string & aName,const std::string & aType,const SubTypeList & aSubTypeList,uint16_t aPort,const TxtData & aTxtData,ResultCallback && aCallback)204 otbrError MdnsPublisher::PublishServiceImpl(const std::string &aHostName,
205 const std::string &aName,
206 const std::string &aType,
207 const SubTypeList &aSubTypeList,
208 uint16_t aPort,
209 const TxtData &aTxtData,
210 ResultCallback &&aCallback)
211 {
212 int32_t listenerId = AllocateListenerId();
213 TxtList txtList;
214 otbrError error = OTBR_ERROR_NONE;
215
216 std::vector<DnsTxtAttribute> txtAttributes;
217
218 VerifyOrExit(IsStarted(), error = OTBR_ERROR_MDNS);
219 if (mNsdPublisher == nullptr)
220 {
221 otbrLogWarning("No platform mDNS implementation registered!");
222 ExitNow(error = OTBR_ERROR_MDNS);
223 }
224
225 aCallback = HandleDuplicateServiceRegistration(aHostName, aName, aType, aSubTypeList, aPort, aTxtData,
226 std::move(aCallback));
227 VerifyOrExit(!aCallback.IsNull(), error = OTBR_ERROR_INVALID_STATE);
228
229 SuccessOrExit(error = DecodeTxtData(txtList, aTxtData.data(), aTxtData.size()));
230
231 for (const auto &txtEntry : txtList)
232 {
233 DnsTxtAttribute txtAttribute;
234
235 txtAttribute.name = txtEntry.mKey;
236 txtAttribute.value = txtEntry.mValue;
237 txtAttributes.push_back(std::move(txtAttribute));
238 }
239 AddServiceRegistration(MakeUnique<NsdServiceRegistration>(aHostName, aName, aType, aSubTypeList, aPort, aTxtData,
240 /* aCallback= */ nullptr, this, listenerId,
241 mNsdPublisher));
242
243 otbrLogInfo("Publishing service %s.%s listener ID = %d", aName.c_str(), aType.c_str(), listenerId);
244
245 mNsdPublisher->registerService(aHostName, aName, aType, aSubTypeList, aPort, txtAttributes,
246 CreateReceiver(std::move(aCallback)), listenerId);
247
248 exit:
249 return error;
250 }
251
UnpublishService(const std::string & aName,const std::string & aType,ResultCallback && aCallback)252 void MdnsPublisher::UnpublishService(const std::string &aName, const std::string &aType, ResultCallback &&aCallback)
253 {
254 NsdServiceRegistration *serviceRegistration =
255 static_cast<NsdServiceRegistration *>(FindServiceRegistration(aName, aType));
256
257 VerifyOrExit(IsStarted(), std::move(aCallback)(OTBR_ERROR_MDNS));
258 if (mNsdPublisher == nullptr)
259 {
260 otbrLogWarning("No platform mDNS implementation registered!");
261 ExitNow(std::move(aCallback)(OTBR_ERROR_MDNS));
262 }
263 VerifyOrExit(serviceRegistration != nullptr, std::move(aCallback)(OTBR_ERROR_NONE));
264
265 serviceRegistration->mUnregisterReceiver = CreateReceiver(std::move(aCallback));
266 RemoveServiceRegistration(aName, aType, OTBR_ERROR_NONE);
267
268 exit:
269 return;
270 }
271
PublishHostImpl(const std::string & aName,const AddressList & aAddresses,ResultCallback && aCallback)272 otbrError MdnsPublisher::PublishHostImpl(const std::string &aName,
273 const AddressList &aAddresses,
274 ResultCallback &&aCallback)
275 {
276 int32_t listenerId = AllocateListenerId();
277 TxtList txtList;
278 otbrError error = OTBR_ERROR_NONE;
279
280 std::vector<std::string> addressStrings;
281
282 VerifyOrExit(IsStarted(), error = OTBR_ERROR_MDNS);
283 if (mNsdPublisher == nullptr)
284 {
285 otbrLogWarning("No platform mDNS implementation registered!");
286 ExitNow(error = OTBR_ERROR_MDNS);
287 }
288
289 aCallback = HandleDuplicateHostRegistration(aName, aAddresses, std::move(aCallback));
290 VerifyOrExit(!aCallback.IsNull(), error = OTBR_ERROR_INVALID_STATE);
291
292 AddHostRegistration(
293 MakeUnique<NsdHostRegistration>(aName, aAddresses, /* aCallback= */ nullptr, this, listenerId, mNsdPublisher));
294
295 otbrLogInfo("Publishing host %s listener ID = %d", aName.c_str(), listenerId);
296
297 addressStrings.reserve(aAddresses.size());
298 for (const Ip6Address &address : aAddresses)
299 {
300 addressStrings.push_back(address.ToString());
301 }
302
303 if (aAddresses.size())
304 {
305 mNsdPublisher->registerHost(aName, addressStrings, CreateReceiver(std::move(aCallback)), listenerId);
306 }
307 else
308 {
309 // No addresses to register.
310 std::move(aCallback)(OTBR_ERROR_NONE);
311 }
312
313 exit:
314 return error;
315 }
316
PublishKeyImpl(const std::string & aName,const KeyData & aKeyData,ResultCallback && aCallback)317 otbrError MdnsPublisher::PublishKeyImpl(const std::string &aName, const KeyData &aKeyData, ResultCallback &&aCallback)
318 {
319 OTBR_UNUSED_VARIABLE(aName);
320 OTBR_UNUSED_VARIABLE(aKeyData);
321 OTBR_UNUSED_VARIABLE(aCallback);
322
323 DieForNotImplemented(__func__);
324
325 return OTBR_ERROR_MDNS;
326 }
327
UnpublishHost(const std::string & aName,ResultCallback && aCallback)328 void MdnsPublisher::UnpublishHost(const std::string &aName, ResultCallback &&aCallback)
329 {
330 NsdHostRegistration *hostRegistration = static_cast<NsdHostRegistration *>(FindHostRegistration(aName));
331
332 VerifyOrExit(IsStarted(), std::move(aCallback)(OTBR_ERROR_MDNS));
333 if (mNsdPublisher == nullptr)
334 {
335 otbrLogWarning("No platform mDNS implementation registered!");
336 ExitNow(std::move(aCallback)(OTBR_ERROR_MDNS));
337 }
338 VerifyOrExit(hostRegistration != nullptr, std::move(aCallback)(OTBR_ERROR_NONE));
339
340 hostRegistration->mUnregisterReceiver = CreateReceiver(std::move(aCallback));
341 RemoveHostRegistration(aName, OTBR_ERROR_NONE);
342
343 exit:
344 return;
345 }
346
UnpublishKey(const std::string & aName,ResultCallback && aCallback)347 void MdnsPublisher::UnpublishKey(const std::string &aName, ResultCallback &&aCallback)
348 {
349 OTBR_UNUSED_VARIABLE(aName);
350 OTBR_UNUSED_VARIABLE(aCallback);
351
352 DieForNotImplemented(__func__);
353 }
354
SubscribeService(const std::string & aType,const std::string & aInstanceName)355 void MdnsPublisher::SubscribeService(const std::string &aType, const std::string &aInstanceName)
356 {
357 auto service = std::make_shared<ServiceSubscription>(aType, aInstanceName, *this, mNsdPublisher);
358
359 VerifyOrExit(IsStarted(), otbrLogWarning("No platform mDNS implementation registered!"));
360
361 mServiceSubscriptions.push_back(std::move(service));
362
363 otbrLogInfo("Subscribe service %s.%s (total %zu)", aInstanceName.c_str(), aType.c_str(),
364 mServiceSubscriptions.size());
365
366 if (aInstanceName.empty())
367 {
368 mServiceSubscriptions.back()->Browse();
369 }
370 else
371 {
372 mServiceSubscriptions.back()->Resolve(aInstanceName, aType);
373 }
374 exit:
375 return;
376 }
377
UnsubscribeService(const std::string & aType,const std::string & aInstanceName)378 void MdnsPublisher::UnsubscribeService(const std::string &aType, const std::string &aInstanceName)
379 {
380 ServiceSubscriptionList::iterator it;
381
382 VerifyOrExit(IsStarted());
383
384 it = std::find_if(mServiceSubscriptions.begin(), mServiceSubscriptions.end(),
385 [&aType, &aInstanceName](const std::shared_ptr<ServiceSubscription> &aService) {
386 return aService->mType == aType && aService->mName == aInstanceName;
387 });
388
389 VerifyOrExit(it != mServiceSubscriptions.end(),
390 otbrLogWarning("The service %s.%s is already unsubscribed.", aInstanceName.c_str(), aType.c_str()));
391
392 {
393 std::shared_ptr<ServiceSubscription> service = std::move(*it);
394
395 mServiceSubscriptions.erase(it);
396 }
397
398 otbrLogInfo("Unsubscribe service %s.%s (left %zu)", aInstanceName.c_str(), aType.c_str(),
399 mServiceSubscriptions.size());
400
401 exit:
402 return;
403 }
404
SubscribeHost(const std::string & aHostName)405 void MdnsPublisher::SubscribeHost(const std::string &aHostName)
406 {
407 auto host = std::make_shared<HostSubscription>(aHostName, *this, mNsdPublisher, AllocateListenerId());
408
409 VerifyOrExit(IsStarted(), otbrLogWarning("No platform mDNS implementation registered!"));
410
411 mNsdPublisher->resolveHost(aHostName, CreateNsdResolveHostCallback(host), host->mListenerId);
412 mHostSubscriptions.push_back(std::move(host));
413
414 otbrLogInfo("Subscribe host %s (total %zu)", aHostName.c_str(), mHostSubscriptions.size());
415
416 exit:
417 return;
418 }
419
UnsubscribeHost(const std::string & aHostName)420 void MdnsPublisher::UnsubscribeHost(const std::string &aHostName)
421 {
422 HostSubscriptionList::iterator it;
423
424 VerifyOrExit(IsStarted());
425
426 it = std::find_if(
427 mHostSubscriptions.begin(), mHostSubscriptions.end(),
428 [&aHostName](const std::shared_ptr<HostSubscription> &aHost) { return aHost->mName == aHostName; });
429
430 VerifyOrExit(it != mHostSubscriptions.end(),
431 otbrLogWarning("The host %s is already unsubscribed.", aHostName.c_str()));
432
433 {
434 std::shared_ptr<HostSubscription> host = std::move(*it);
435
436 mHostSubscriptions.erase(it);
437 }
438
439 otbrLogInfo("Unsubscribe host %s (left %zu)", aHostName.c_str(), mHostSubscriptions.size());
440
441 exit:
442 return;
443 }
444
OnServiceResolveFailedImpl(const std::string & aType,const std::string & aInstanceName,int32_t aErrorCode)445 void MdnsPublisher::OnServiceResolveFailedImpl(const std::string &aType,
446 const std::string &aInstanceName,
447 int32_t aErrorCode)
448 {
449 OTBR_UNUSED_VARIABLE(aType);
450 OTBR_UNUSED_VARIABLE(aInstanceName);
451 OTBR_UNUSED_VARIABLE(aErrorCode);
452
453 DieForNotImplemented(__func__);
454 }
455
OnHostResolveFailedImpl(const std::string & aHostName,int32_t aErrorCode)456 void MdnsPublisher::OnHostResolveFailedImpl(const std::string &aHostName, int32_t aErrorCode)
457 {
458 OTBR_UNUSED_VARIABLE(aHostName);
459 OTBR_UNUSED_VARIABLE(aErrorCode);
460
461 DieForNotImplemented(__func__);
462 }
463
AllocateListenerId(void)464 int32_t MdnsPublisher::AllocateListenerId(void)
465 {
466 if (mNextListenerId == std::numeric_limits<int32_t>::max())
467 {
468 mNextListenerId = 0;
469 }
470 return mNextListenerId++;
471 }
472
~NsdServiceRegistration(void)473 MdnsPublisher::NsdServiceRegistration::~NsdServiceRegistration(void)
474 {
475 auto nsdPublisher = mNsdPublisher.lock();
476
477 VerifyOrExit(mPublisher->IsStarted() && nsdPublisher != nullptr);
478
479 otbrLogInfo("Unpublishing service %s.%s listener ID = %d", mName.c_str(), mType.c_str(), mListenerId);
480
481 if (!mUnregisterReceiver)
482 {
483 mUnregisterReceiver = CreateReceiver([](int) {});
484 }
485
486 nsdPublisher->unregister(mUnregisterReceiver, mListenerId);
487
488 exit:
489 return;
490 }
491
~NsdHostRegistration(void)492 MdnsPublisher::NsdHostRegistration::~NsdHostRegistration(void)
493 {
494 auto nsdPublisher = mNsdPublisher.lock();
495
496 VerifyOrExit(mPublisher->IsStarted() && nsdPublisher != nullptr);
497
498 otbrLogInfo("Unpublishing host %s listener ID = %d", mName.c_str(), mListenerId);
499
500 if (!mUnregisterReceiver)
501 {
502 mUnregisterReceiver = CreateReceiver([](int) {});
503 }
504
505 nsdPublisher->unregister(mUnregisterReceiver, mListenerId);
506
507 exit:
508 return;
509 }
510
Release(void)511 void MdnsPublisher::ServiceSubscription::Release(void)
512 {
513 otbrLogInfo("Browsing service type %s", mType.c_str());
514
515 std::vector<std::string> instanceNames;
516
517 for (const auto &nameAndResolvers : mResolvers)
518 {
519 instanceNames.push_back(nameAndResolvers.first);
520 }
521 for (const auto &name : instanceNames)
522 {
523 RemoveServiceResolver(name);
524 }
525
526 mNsdPublisher->stopServiceDiscovery(mBrowseListenerId);
527 }
528
Browse(void)529 void MdnsPublisher::ServiceSubscription::Browse(void)
530 {
531 VerifyOrExit(mPublisher.IsStarted());
532
533 otbrLogInfo("Browsing service type %s", mType.c_str());
534
535 mNsdPublisher->discoverService(mType, CreateNsdDiscoverServiceCallback(shared_from_this()), mBrowseListenerId);
536
537 exit:
538 return;
539 }
540
Resolve(const std::string & aName,const std::string & aType)541 void MdnsPublisher::ServiceSubscription::Resolve(const std::string &aName, const std::string &aType)
542 {
543 ServiceResolver *resolver = new ServiceResolver(mPublisher.AllocateListenerId(), mNsdPublisher);
544
545 VerifyOrExit(mPublisher.IsStarted());
546
547 otbrLogInfo("Resolving service %s.%s", aName.c_str(), aType.c_str());
548
549 AddServiceResolver(aName, resolver);
550 mNsdPublisher->resolveService(aName, aType, CreateNsdResolveServiceCallback(shared_from_this()),
551 resolver->mListenerId);
552
553 exit:
554 return;
555 }
556
AddServiceResolver(const std::string & aName,ServiceResolver * aResolver)557 void MdnsPublisher::ServiceSubscription::AddServiceResolver(const std::string &aName, ServiceResolver *aResolver)
558 {
559 mResolvers[aName].insert(aResolver);
560 }
RemoveServiceResolver(const std::string & aName)561 void MdnsPublisher::ServiceSubscription::RemoveServiceResolver(const std::string &aName)
562 {
563 int numResolvers = 0;
564
565 VerifyOrExit(mResolvers.find(aName) != mResolvers.end());
566
567 numResolvers = mResolvers[aName].size();
568
569 for (auto resolver : mResolvers[aName])
570 {
571 delete resolver;
572 }
573
574 mResolvers.erase(aName);
575
576 exit:
577 otbrLogDebug("Removed %d service resolver for instance %s", numResolvers, aName.c_str());
578 return;
579 }
580
581 } // namespace Android
582 } // namespace otbr
583