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