1 /*
2 * Copyright (c) 2018, 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 implements mDNS publisher based on mDNSResponder.
32 */
33
34 #define OTBR_LOG_TAG "MDNS"
35
36 #include "mdns/mdns_mdnssd.hpp"
37
38 #include <algorithm>
39
40 #include <arpa/inet.h>
41 #include <assert.h>
42 #include <errno.h>
43 #include <inttypes.h>
44 #include <netinet/in.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48
49 #include "common/code_utils.hpp"
50 #include "common/dns_utils.hpp"
51 #include "common/logging.hpp"
52 #include "common/time.hpp"
53
54 namespace otbr {
55
56 namespace Mdns {
57
58 static const char kDomain[] = "local.";
59
DNSErrorToOtbrError(DNSServiceErrorType aError)60 static otbrError DNSErrorToOtbrError(DNSServiceErrorType aError)
61 {
62 otbrError error;
63
64 switch (aError)
65 {
66 case kDNSServiceErr_NoError:
67 error = OTBR_ERROR_NONE;
68 break;
69
70 case kDNSServiceErr_NoSuchKey:
71 case kDNSServiceErr_NoSuchName:
72 case kDNSServiceErr_NoSuchRecord:
73 error = OTBR_ERROR_NOT_FOUND;
74 break;
75
76 case kDNSServiceErr_Invalid:
77 case kDNSServiceErr_BadParam:
78 case kDNSServiceErr_BadFlags:
79 case kDNSServiceErr_BadInterfaceIndex:
80 error = OTBR_ERROR_INVALID_ARGS;
81 break;
82
83 case kDNSServiceErr_NameConflict:
84 error = OTBR_ERROR_DUPLICATED;
85 break;
86
87 case kDNSServiceErr_Unsupported:
88 error = OTBR_ERROR_NOT_IMPLEMENTED;
89 break;
90
91 default:
92 error = OTBR_ERROR_MDNS;
93 break;
94 }
95
96 return error;
97 }
98
DNSErrorToString(DNSServiceErrorType aError)99 static const char *DNSErrorToString(DNSServiceErrorType aError)
100 {
101 switch (aError)
102 {
103 case kDNSServiceErr_NoError:
104 return "OK";
105
106 case kDNSServiceErr_Unknown:
107 // 0xFFFE FFFF
108 return "Unknown";
109
110 case kDNSServiceErr_NoSuchName:
111 return "No Such Name";
112
113 case kDNSServiceErr_NoMemory:
114 return "No Memory";
115
116 case kDNSServiceErr_BadParam:
117 return "Bad Param";
118
119 case kDNSServiceErr_BadReference:
120 return "Bad Reference";
121
122 case kDNSServiceErr_BadState:
123 return "Bad State";
124
125 case kDNSServiceErr_BadFlags:
126 return "Bad Flags";
127
128 case kDNSServiceErr_Unsupported:
129 return "Unsupported";
130
131 case kDNSServiceErr_NotInitialized:
132 return "Not Initialized";
133
134 case kDNSServiceErr_AlreadyRegistered:
135 return "Already Registered";
136
137 case kDNSServiceErr_NameConflict:
138 return "Name Conflict";
139
140 case kDNSServiceErr_Invalid:
141 return "Invalid";
142
143 case kDNSServiceErr_Firewall:
144 return "Firewall";
145
146 case kDNSServiceErr_Incompatible:
147 // client library incompatible with daemon
148 return "Incompatible";
149
150 case kDNSServiceErr_BadInterfaceIndex:
151 return "Bad Interface Index";
152
153 case kDNSServiceErr_Refused:
154 return "Refused";
155
156 case kDNSServiceErr_NoSuchRecord:
157 return "No Such Record";
158
159 case kDNSServiceErr_NoAuth:
160 return "No Auth";
161
162 case kDNSServiceErr_NoSuchKey:
163 return "No Such Key";
164
165 case kDNSServiceErr_NATTraversal:
166 return "NAT Traversal";
167
168 case kDNSServiceErr_DoubleNAT:
169 return "Double NAT";
170
171 case kDNSServiceErr_BadTime:
172 // Codes up to here existed in Tiger
173 return "Bad Time";
174
175 case kDNSServiceErr_BadSig:
176 return "Bad Sig";
177
178 case kDNSServiceErr_BadKey:
179 return "Bad Key";
180
181 case kDNSServiceErr_Transient:
182 return "Transient";
183
184 case kDNSServiceErr_ServiceNotRunning:
185 // Background daemon not running
186 return "Service Not Running";
187
188 case kDNSServiceErr_NATPortMappingUnsupported:
189 // NAT doesn't support NAT-PMP or UPnP
190 return "NAT Port Mapping Unsupported";
191
192 case kDNSServiceErr_NATPortMappingDisabled:
193 // NAT supports NAT-PMP or UPnP but it's disabled by the administrator
194 return "NAT Port Mapping Disabled";
195
196 case kDNSServiceErr_NoRouter:
197 // No router currently configured (probably no network connectivity)
198 return "No Router";
199
200 case kDNSServiceErr_PollingMode:
201 return "Polling Mode";
202
203 case kDNSServiceErr_Timeout:
204 return "Timeout";
205
206 default:
207 assert(false);
208 return nullptr;
209 }
210 }
211
PublisherMDnsSd(StateCallback aCallback)212 PublisherMDnsSd::PublisherMDnsSd(StateCallback aCallback)
213 : mHostsRef(nullptr)
214 , mState(State::kIdle)
215 , mStateCallback(std::move(aCallback))
216 {
217 }
218
~PublisherMDnsSd(void)219 PublisherMDnsSd::~PublisherMDnsSd(void)
220 {
221 Stop();
222 }
223
Start(void)224 otbrError PublisherMDnsSd::Start(void)
225 {
226 mState = State::kReady;
227 mStateCallback(State::kReady);
228 return OTBR_ERROR_NONE;
229 }
230
IsStarted(void) const231 bool PublisherMDnsSd::IsStarted(void) const
232 {
233 return mState == State::kReady;
234 }
235
Stop(void)236 void PublisherMDnsSd::Stop(void)
237 {
238 ServiceRegistrationMap serviceRegistrations;
239 HostRegistrationMap hostRegistrations;
240
241 VerifyOrExit(mState == State::kReady);
242
243 std::swap(mServiceRegistrations, serviceRegistrations);
244 std::swap(mHostRegistrations, hostRegistrations);
245
246 if (mHostsRef != nullptr)
247 {
248 DNSServiceRefDeallocate(mHostsRef);
249 otbrLogDebug("Deallocated DNSServiceRef for hosts: %p", mHostsRef);
250 mHostsRef = nullptr;
251 }
252
253 mSubscribedServices.clear();
254
255 mSubscribedHosts.clear();
256
257 mState = State::kIdle;
258
259 exit:
260 return;
261 }
262
Update(MainloopContext & aMainloop)263 void PublisherMDnsSd::Update(MainloopContext &aMainloop)
264 {
265 for (auto &kv : mServiceRegistrations)
266 {
267 auto &serviceReg = static_cast<DnssdServiceRegistration &>(*kv.second);
268
269 assert(serviceReg.GetServiceRef() != nullptr);
270
271 int fd = DNSServiceRefSockFD(serviceReg.GetServiceRef());
272
273 if (fd != -1)
274 {
275 FD_SET(fd, &aMainloop.mReadFdSet);
276 aMainloop.mMaxFd = std::max(aMainloop.mMaxFd, fd);
277 }
278 }
279
280 if (mHostsRef != nullptr)
281 {
282 int fd = DNSServiceRefSockFD(mHostsRef);
283
284 assert(fd != -1);
285
286 FD_SET(fd, &aMainloop.mReadFdSet);
287
288 aMainloop.mMaxFd = std::max(aMainloop.mMaxFd, fd);
289 }
290
291 for (const auto &service : mSubscribedServices)
292 {
293 service->UpdateAll(aMainloop);
294 }
295
296 for (const auto &host : mSubscribedHosts)
297 {
298 host->Update(aMainloop);
299 }
300 }
301
Process(const MainloopContext & aMainloop)302 void PublisherMDnsSd::Process(const MainloopContext &aMainloop)
303 {
304 std::vector<DNSServiceRef> readyServices;
305
306 for (auto &kv : mServiceRegistrations)
307 {
308 auto &serviceReg = static_cast<DnssdServiceRegistration &>(*kv.second);
309 int fd = DNSServiceRefSockFD(serviceReg.GetServiceRef());
310
311 if (FD_ISSET(fd, &aMainloop.mReadFdSet))
312 {
313 readyServices.push_back(serviceReg.GetServiceRef());
314 }
315 }
316
317 if (mHostsRef != nullptr)
318 {
319 int fd = DNSServiceRefSockFD(mHostsRef);
320
321 if (FD_ISSET(fd, &aMainloop.mReadFdSet))
322 {
323 readyServices.push_back(mHostsRef);
324 }
325 }
326
327 for (const auto &service : mSubscribedServices)
328 {
329 service->ProcessAll(aMainloop, readyServices);
330 }
331
332 for (const auto &host : mSubscribedHosts)
333 {
334 host->Process(aMainloop, readyServices);
335 }
336
337 for (DNSServiceRef serviceRef : readyServices)
338 {
339 DNSServiceErrorType error = DNSServiceProcessResult(serviceRef);
340
341 if (error != kDNSServiceErr_NoError)
342 {
343 otbrLogLevel logLevel = (error == kDNSServiceErr_BadReference) ? OTBR_LOG_INFO : OTBR_LOG_WARNING;
344 otbrLog(logLevel, OTBR_LOG_TAG, "DNSServiceProcessResult failed: %s (serviceRef = %p)",
345 DNSErrorToString(error), serviceRef);
346 }
347 if (error == kDNSServiceErr_ServiceNotRunning)
348 {
349 otbrLogWarning("Need to reconnect to mdnsd");
350 Stop();
351 Start();
352 ExitNow();
353 }
354 }
355 exit:
356 return;
357 }
358
~DnssdServiceRegistration(void)359 PublisherMDnsSd::DnssdServiceRegistration::~DnssdServiceRegistration(void)
360 {
361 if (mServiceRef != nullptr)
362 {
363 DNSServiceRefDeallocate(mServiceRef);
364 }
365 }
366
~DnssdHostRegistration(void)367 PublisherMDnsSd::DnssdHostRegistration::~DnssdHostRegistration(void)
368 {
369 VerifyOrExit(mServiceRef != nullptr && mRecordRef != nullptr);
370
371 if (IsCompleted())
372 {
373 // The Bonjour mDNSResponder somehow doesn't send goodbye message for the AAAA record when it is
374 // removed by `DNSServiceRemoveRecord`. Per RFC 6762, a goodbye message of a record sets its TTL
375 // to zero but the receiver should record the TTL of 1 and flushes the cache 1 second later. Here
376 // we remove the AAAA record after updating its TTL to 1 second. This has the same effect as
377 // sending a goodbye message.
378 // TODO: resolve the goodbye issue with Bonjour mDNSResponder.
379 int dnsError = DNSServiceUpdateRecord(mServiceRef, mRecordRef, kDNSServiceFlagsUnique, mAddress.size(),
380 mAddress.data(), /* ttl */ 1);
381 otbrLogWarning("Failed to send goodbye message for host %s: %s", MakeFullHostName(mName).c_str(),
382 DNSErrorToString(dnsError));
383 }
384
385 DNSServiceRemoveRecord(mServiceRef, mRecordRef, /* flags */ 0);
386 // TODO: ?
387 // DNSRecordRefDeallocate(mRecordRef);
388
389 exit:
390 return;
391 }
392
FindServiceRegistration(const DNSServiceRef & aServiceRef)393 Publisher::ServiceRegistration *PublisherMDnsSd::FindServiceRegistration(const DNSServiceRef &aServiceRef)
394 {
395 ServiceRegistration *result = nullptr;
396
397 for (auto &kv : mServiceRegistrations)
398 {
399 // We are sure that the service registrations must be instances of `DnssdServiceRegistration`.
400 auto &serviceReg = static_cast<DnssdServiceRegistration &>(*kv.second);
401
402 if (serviceReg.GetServiceRef() == aServiceRef)
403 {
404 result = kv.second.get();
405 break;
406 }
407 }
408
409 return result;
410 }
411
FindHostRegistration(const DNSServiceRef & aServiceRef,const DNSRecordRef & aRecordRef)412 Publisher::HostRegistration *PublisherMDnsSd::FindHostRegistration(const DNSServiceRef &aServiceRef,
413 const DNSRecordRef & aRecordRef)
414 {
415 HostRegistration *result = nullptr;
416
417 for (auto &kv : mHostRegistrations)
418 {
419 // We are sure that the host registrations must be instances of `DnssdHostRegistration`.
420 auto &hostReg = static_cast<DnssdHostRegistration &>(*kv.second);
421
422 if (hostReg.GetServiceRef() == aServiceRef && hostReg.GetRecordRef() == aRecordRef)
423 {
424 result = kv.second.get();
425 break;
426 }
427 }
428
429 return result;
430 }
431
HandleServiceRegisterResult(DNSServiceRef aService,const DNSServiceFlags aFlags,DNSServiceErrorType aError,const char * aName,const char * aType,const char * aDomain,void * aContext)432 void PublisherMDnsSd::HandleServiceRegisterResult(DNSServiceRef aService,
433 const DNSServiceFlags aFlags,
434 DNSServiceErrorType aError,
435 const char * aName,
436 const char * aType,
437 const char * aDomain,
438 void * aContext)
439 {
440 static_cast<PublisherMDnsSd *>(aContext)->HandleServiceRegisterResult(aService, aFlags, aError, aName, aType,
441 aDomain);
442 }
443
HandleServiceRegisterResult(DNSServiceRef aServiceRef,const DNSServiceFlags aFlags,DNSServiceErrorType aError,const char * aName,const char * aType,const char * aDomain)444 void PublisherMDnsSd::HandleServiceRegisterResult(DNSServiceRef aServiceRef,
445 const DNSServiceFlags aFlags,
446 DNSServiceErrorType aError,
447 const char * aName,
448 const char * aType,
449 const char * aDomain)
450 {
451 OTBR_UNUSED_VARIABLE(aDomain);
452
453 otbrError error = DNSErrorToOtbrError(aError);
454 std::string originalInstanceName;
455 ServiceRegistration *serviceReg = FindServiceRegistration(aServiceRef);
456
457 otbrLogInfo("Received reply for service %s.%s, serviceRef = %p", aName, aType, aServiceRef);
458
459 VerifyOrExit(serviceReg != nullptr);
460
461 if (aError == kDNSServiceErr_NoError && (aFlags & kDNSServiceFlagsAdd))
462 {
463 otbrLogInfo("Successfully registered service %s.%s", aName, aType);
464 serviceReg->Complete(OTBR_ERROR_NONE);
465 }
466 else
467 {
468 otbrLogErr("Failed to register service %s.%s: %s", aName, aType, DNSErrorToString(aError));
469 RemoveServiceRegistration(serviceReg->mName, serviceReg->mType, error);
470 }
471
472 exit:
473 return;
474 }
475
PublishServiceImpl(const std::string & aHostName,const std::string & aName,const std::string & aType,const SubTypeList & aSubTypeList,uint16_t aPort,const TxtList & aTxtList,ResultCallback && aCallback)476 void PublisherMDnsSd::PublishServiceImpl(const std::string &aHostName,
477 const std::string &aName,
478 const std::string &aType,
479 const SubTypeList &aSubTypeList,
480 uint16_t aPort,
481 const TxtList & aTxtList,
482 ResultCallback && aCallback)
483 {
484 otbrError ret = OTBR_ERROR_NONE;
485 int error = 0;
486 std::vector<uint8_t> txt;
487 SubTypeList sortedSubTypeList = SortSubTypeList(aSubTypeList);
488 TxtList sortedTxtList = SortTxtList(aTxtList);
489 std::string regType = MakeRegType(aType, sortedSubTypeList);
490 DNSServiceRef serviceRef = nullptr;
491 std::string fullHostName;
492
493 VerifyOrExit(mState == State::kReady, ret = OTBR_ERROR_INVALID_STATE);
494
495 if (!aHostName.empty())
496 {
497 fullHostName = MakeFullHostName(aHostName);
498 }
499
500 aCallback = HandleDuplicateServiceRegistration(aHostName, aName, aType, sortedSubTypeList, aPort, sortedTxtList,
501 std::move(aCallback));
502 VerifyOrExit(!aCallback.IsNull());
503
504 SuccessOrExit(ret = EncodeTxtData(aTxtList, txt));
505 otbrLogInfo("Registering new service %s.%s.local, serviceRef = %p", aName.c_str(), regType.c_str(), serviceRef);
506 SuccessOrExit(error = DNSServiceRegister(&serviceRef, kDNSServiceFlagsNoAutoRename, kDNSServiceInterfaceIndexAny,
507 aName.c_str(), regType.c_str(), /* domain */ nullptr,
508 !aHostName.empty() ? fullHostName.c_str() : nullptr, htons(aPort),
509 txt.size(), txt.data(), HandleServiceRegisterResult, this));
510 AddServiceRegistration(std::unique_ptr<DnssdServiceRegistration>(new DnssdServiceRegistration(
511 aHostName, aName, aType, sortedSubTypeList, aPort, sortedTxtList, std::move(aCallback), serviceRef, this)));
512
513 exit:
514 if (error != kDNSServiceErr_NoError || ret != OTBR_ERROR_NONE)
515 {
516 if (error != kDNSServiceErr_NoError)
517 {
518 ret = DNSErrorToOtbrError(error);
519 otbrLogErr("Failed to publish service %s.%s for mdnssd error: %s!", aName.c_str(), aType.c_str(),
520 DNSErrorToString(error));
521 }
522
523 if (serviceRef != nullptr)
524 {
525 DNSServiceRefDeallocate(serviceRef);
526 }
527 std::move(aCallback)(ret);
528 }
529 }
530
UnpublishService(const std::string & aName,const std::string & aType,ResultCallback && aCallback)531 void PublisherMDnsSd::UnpublishService(const std::string &aName, const std::string &aType, ResultCallback &&aCallback)
532 {
533 otbrError error = OTBR_ERROR_NONE;
534
535 VerifyOrExit(mState == Publisher::State::kReady, error = OTBR_ERROR_INVALID_STATE);
536 RemoveServiceRegistration(aName, aType, OTBR_ERROR_ABORTED);
537
538 exit:
539 std::move(aCallback)(error);
540 }
541
PublishHostImpl(const std::string & aName,const std::vector<uint8_t> & aAddress,ResultCallback && aCallback)542 void PublisherMDnsSd::PublishHostImpl(const std::string & aName,
543 const std::vector<uint8_t> &aAddress,
544 ResultCallback && aCallback)
545 {
546 otbrError ret = OTBR_ERROR_NONE;
547 int error = 0;
548 std::string fullName;
549 DNSRecordRef recordRef = nullptr;
550
551 VerifyOrExit(mState == Publisher::State::kReady, ret = OTBR_ERROR_INVALID_STATE);
552
553 // Supports only IPv6 for now, may support IPv4 in the future.
554 VerifyOrExit(aAddress.size() == OTBR_IP6_ADDRESS_SIZE, error = OTBR_ERROR_INVALID_ARGS);
555
556 fullName = MakeFullHostName(aName);
557
558 if (mHostsRef == nullptr)
559 {
560 SuccessOrExit(error = DNSServiceCreateConnection(&mHostsRef));
561 otbrLogDebug("Created new DNSServiceRef for hosts: %p", mHostsRef);
562 }
563
564 aCallback = HandleDuplicateHostRegistration(aName, aAddress, std::move(aCallback));
565 VerifyOrExit(!aCallback.IsNull());
566
567 otbrLogInfo("Registering new host %s", aName.c_str());
568 SuccessOrExit(error = DNSServiceRegisterRecord(mHostsRef, &recordRef, kDNSServiceFlagsUnique,
569 kDNSServiceInterfaceIndexAny, fullName.c_str(), kDNSServiceType_AAAA,
570 kDNSServiceClass_IN, aAddress.size(), aAddress.data(), /* ttl */ 0,
571 HandleRegisterHostResult, this));
572 AddHostRegistration(std::unique_ptr<DnssdHostRegistration>(
573 new DnssdHostRegistration(aName, aAddress, std::move(aCallback), mHostsRef, recordRef, this)));
574
575 exit:
576 if (error != kDNSServiceErr_NoError || ret != OTBR_ERROR_NONE)
577 {
578 if (error != kDNSServiceErr_NoError)
579 {
580 ret = DNSErrorToOtbrError(error);
581 otbrLogErr("Failed to publish/update host %s for mdnssd error: %s!", aName.c_str(),
582 DNSErrorToString(error));
583 }
584
585 std::move(aCallback)(ret);
586 }
587 }
588
UnpublishHost(const std::string & aName,ResultCallback && aCallback)589 void PublisherMDnsSd::UnpublishHost(const std::string &aName, ResultCallback &&aCallback)
590 {
591 otbrError error = OTBR_ERROR_NONE;
592
593 VerifyOrExit(mState == Publisher::State::kReady, error = OTBR_ERROR_INVALID_STATE);
594 RemoveHostRegistration(aName, OTBR_ERROR_ABORTED);
595
596 exit:
597 // We may failed to unregister the host from underlying mDNS publishers, but
598 // it usually means that the mDNS publisher is already not functioning. So it's
599 // okay to return success directly since the service is not advertised anyway.
600 std::move(aCallback)(error);
601 }
602
HandleRegisterHostResult(DNSServiceRef aServiceRef,DNSRecordRef aRecordRef,DNSServiceFlags aFlags,DNSServiceErrorType aError,void * aContext)603 void PublisherMDnsSd::HandleRegisterHostResult(DNSServiceRef aServiceRef,
604 DNSRecordRef aRecordRef,
605 DNSServiceFlags aFlags,
606 DNSServiceErrorType aError,
607 void * aContext)
608 {
609 static_cast<PublisherMDnsSd *>(aContext)->HandleRegisterHostResult(aServiceRef, aRecordRef, aFlags, aError);
610 }
611
HandleRegisterHostResult(DNSServiceRef aServiceRef,DNSRecordRef aRecordRef,DNSServiceFlags aFlags,DNSServiceErrorType aError)612 void PublisherMDnsSd::HandleRegisterHostResult(DNSServiceRef aServiceRef,
613 DNSRecordRef aRecordRef,
614 DNSServiceFlags aFlags,
615 DNSServiceErrorType aError)
616 {
617 OTBR_UNUSED_VARIABLE(aFlags);
618
619 otbrError error = DNSErrorToOtbrError(aError);
620 HostRegistration *hostReg = FindHostRegistration(aServiceRef, aRecordRef);
621
622 std::string hostName;
623
624 VerifyOrExit(hostReg != nullptr);
625
626 hostName = MakeFullHostName(hostReg->mName);
627
628 otbrLogInfo("Received reply for host %s", hostName.c_str());
629
630 if (error == OTBR_ERROR_NONE)
631 {
632 otbrLogInfo("Successfully registered host %s", hostName.c_str());
633 hostReg->Complete(OTBR_ERROR_NONE);
634 }
635 else
636 {
637 otbrLogWarning("Failed to register host %s for mdnssd error: %s", hostName.c_str(), DNSErrorToString(aError));
638 RemoveHostRegistration(hostReg->mName, error);
639 }
640
641 exit:
642 return;
643 }
644
645 // See `regtype` parameter of the DNSServiceRegister() function for more information.
MakeRegType(const std::string & aType,SubTypeList aSubTypeList)646 std::string PublisherMDnsSd::MakeRegType(const std::string &aType, SubTypeList aSubTypeList)
647 {
648 std::string regType = aType;
649
650 std::sort(aSubTypeList.begin(), aSubTypeList.end());
651
652 for (const auto &subType : aSubTypeList)
653 {
654 regType += "," + subType;
655 }
656
657 return regType;
658 }
659
SubscribeService(const std::string & aType,const std::string & aInstanceName)660 void PublisherMDnsSd::SubscribeService(const std::string &aType, const std::string &aInstanceName)
661 {
662 VerifyOrExit(mState == Publisher::State::kReady);
663 mSubscribedServices.push_back(MakeUnique<ServiceSubscription>(*this, aType, aInstanceName));
664
665 otbrLogInfo("Subscribe service %s.%s (total %zu)", aInstanceName.c_str(), aType.c_str(),
666 mSubscribedServices.size());
667
668 if (aInstanceName.empty())
669 {
670 mSubscribedServices.back()->Browse();
671 }
672 else
673 {
674 mSubscribedServices.back()->Resolve(kDNSServiceInterfaceIndexAny, aInstanceName, aType, kDomain);
675 }
676
677 exit:
678 return;
679 }
680
UnsubscribeService(const std::string & aType,const std::string & aInstanceName)681 void PublisherMDnsSd::UnsubscribeService(const std::string &aType, const std::string &aInstanceName)
682 {
683 ServiceSubscriptionList::iterator it;
684
685 VerifyOrExit(mState == Publisher::State::kReady);
686 it = std::find_if(mSubscribedServices.begin(), mSubscribedServices.end(),
687 [&aType, &aInstanceName](const std::unique_ptr<ServiceSubscription> &aService) {
688 return aService->mType == aType && aService->mInstanceName == aInstanceName;
689 });
690 assert(it != mSubscribedServices.end());
691
692 mSubscribedServices.erase(it);
693
694 otbrLogInfo("Unsubscribe service %s.%s (left %zu)", aInstanceName.c_str(), aType.c_str(),
695 mSubscribedServices.size());
696
697 exit:
698 return;
699 }
700
OnServiceResolveFailedImpl(const std::string & aType,const std::string & aInstanceName,int32_t aErrorCode)701 void PublisherMDnsSd::OnServiceResolveFailedImpl(const std::string &aType,
702 const std::string &aInstanceName,
703 int32_t aErrorCode)
704 {
705 otbrLogWarning("Resolve service %s.%s failed: code=%" PRId32, aInstanceName.c_str(), aType.c_str(), aErrorCode);
706 }
707
OnHostResolveFailedImpl(const std::string & aHostName,int32_t aErrorCode)708 void PublisherMDnsSd::OnHostResolveFailedImpl(const std::string &aHostName, int32_t aErrorCode)
709 {
710 otbrLogWarning("Resolve host %s failed: code=%" PRId32, aHostName.c_str(), aErrorCode);
711 }
712
DnsErrorToOtbrError(int32_t aErrorCode)713 otbrError PublisherMDnsSd::DnsErrorToOtbrError(int32_t aErrorCode)
714 {
715 return otbr::Mdns::DNSErrorToOtbrError(aErrorCode);
716 }
717
SubscribeHost(const std::string & aHostName)718 void PublisherMDnsSd::SubscribeHost(const std::string &aHostName)
719 {
720 VerifyOrExit(mState == State::kReady);
721 mSubscribedHosts.push_back(MakeUnique<HostSubscription>(*this, aHostName));
722
723 otbrLogInfo("Subscribe host %s (total %zu)", aHostName.c_str(), mSubscribedHosts.size());
724
725 mSubscribedHosts.back()->Resolve();
726
727 exit:
728 return;
729 }
730
UnsubscribeHost(const std::string & aHostName)731 void PublisherMDnsSd::UnsubscribeHost(const std::string &aHostName)
732 {
733 HostSubscriptionList ::iterator it;
734
735 VerifyOrExit(mState == Publisher::State::kReady);
736 it = std::find_if(
737 mSubscribedHosts.begin(), mSubscribedHosts.end(),
738 [&aHostName](const std::unique_ptr<HostSubscription> &aHost) { return aHost->mHostName == aHostName; });
739
740 assert(it != mSubscribedHosts.end());
741
742 mSubscribedHosts.erase(it);
743
744 otbrLogInfo("Unsubscribe host %s (remaining %d)", aHostName.c_str(), mSubscribedHosts.size());
745
746 exit:
747 return;
748 }
749
Create(StateCallback aCallback)750 Publisher *Publisher::Create(StateCallback aCallback)
751 {
752 return new PublisherMDnsSd(aCallback);
753 }
754
Destroy(Publisher * aPublisher)755 void Publisher::Destroy(Publisher *aPublisher)
756 {
757 delete static_cast<PublisherMDnsSd *>(aPublisher);
758 }
759
Release(void)760 void PublisherMDnsSd::ServiceRef::Release(void)
761 {
762 DeallocateServiceRef();
763 }
764
DeallocateServiceRef(void)765 void PublisherMDnsSd::ServiceRef::DeallocateServiceRef(void)
766 {
767 if (mServiceRef != nullptr)
768 {
769 DNSServiceRefDeallocate(mServiceRef);
770 mServiceRef = nullptr;
771 }
772 }
773
Update(MainloopContext & aMainloop) const774 void PublisherMDnsSd::ServiceRef::Update(MainloopContext &aMainloop) const
775 {
776 int fd;
777
778 VerifyOrExit(mServiceRef != nullptr);
779
780 fd = DNSServiceRefSockFD(mServiceRef);
781 assert(fd != -1);
782 FD_SET(fd, &aMainloop.mReadFdSet);
783 aMainloop.mMaxFd = std::max(aMainloop.mMaxFd, fd);
784 exit:
785 return;
786 }
787
Process(const MainloopContext & aMainloop,std::vector<DNSServiceRef> & aReadyServices) const788 void PublisherMDnsSd::ServiceRef::Process(const MainloopContext & aMainloop,
789 std::vector<DNSServiceRef> &aReadyServices) const
790 {
791 int fd;
792
793 VerifyOrExit(mServiceRef != nullptr);
794
795 fd = DNSServiceRefSockFD(mServiceRef);
796 assert(fd != -1);
797 if (FD_ISSET(fd, &aMainloop.mReadFdSet))
798 {
799 aReadyServices.push_back(mServiceRef);
800 }
801 exit:
802 return;
803 }
804
Browse(void)805 void PublisherMDnsSd::ServiceSubscription::Browse(void)
806 {
807 assert(mServiceRef == nullptr);
808
809 otbrLogInfo("DNSServiceBrowse %s", mType.c_str());
810 DNSServiceBrowse(&mServiceRef, /* flags */ 0, kDNSServiceInterfaceIndexAny, mType.c_str(),
811 /* domain */ nullptr, HandleBrowseResult, this);
812 }
813
HandleBrowseResult(DNSServiceRef aServiceRef,DNSServiceFlags aFlags,uint32_t aInterfaceIndex,DNSServiceErrorType aErrorCode,const char * aInstanceName,const char * aType,const char * aDomain,void * aContext)814 void PublisherMDnsSd::ServiceSubscription::HandleBrowseResult(DNSServiceRef aServiceRef,
815 DNSServiceFlags aFlags,
816 uint32_t aInterfaceIndex,
817 DNSServiceErrorType aErrorCode,
818 const char * aInstanceName,
819 const char * aType,
820 const char * aDomain,
821 void * aContext)
822 {
823 static_cast<ServiceSubscription *>(aContext)->HandleBrowseResult(aServiceRef, aFlags, aInterfaceIndex, aErrorCode,
824 aInstanceName, aType, aDomain);
825 }
826
HandleBrowseResult(DNSServiceRef aServiceRef,DNSServiceFlags aFlags,uint32_t aInterfaceIndex,DNSServiceErrorType aErrorCode,const char * aInstanceName,const char * aType,const char * aDomain)827 void PublisherMDnsSd::ServiceSubscription::HandleBrowseResult(DNSServiceRef aServiceRef,
828 DNSServiceFlags aFlags,
829 uint32_t aInterfaceIndex,
830 DNSServiceErrorType aErrorCode,
831 const char * aInstanceName,
832 const char * aType,
833 const char * aDomain)
834 {
835 OTBR_UNUSED_VARIABLE(aServiceRef);
836 OTBR_UNUSED_VARIABLE(aDomain);
837
838 otbrLogInfo("DNSServiceBrowse reply: %s %s.%s inf %" PRIu32 ", flags=%" PRIu32 ", error=%" PRId32,
839 aFlags & kDNSServiceFlagsAdd ? "add" : "remove", aInstanceName, aType, aInterfaceIndex, aFlags,
840 aErrorCode);
841
842 VerifyOrExit(aErrorCode == kDNSServiceErr_NoError);
843
844 if (aFlags & kDNSServiceFlagsAdd)
845 {
846 Resolve(aInterfaceIndex, aInstanceName, aType, aDomain);
847 }
848 else
849 {
850 mMDnsSd->OnServiceRemoved(aInterfaceIndex, mType, aInstanceName);
851 }
852
853 exit:
854 if (aErrorCode != kDNSServiceErr_NoError)
855 {
856 mMDnsSd->OnServiceResolveFailed(mType, mInstanceName, aErrorCode);
857 Release();
858 }
859 }
860
Resolve(uint32_t aInterfaceIndex,const std::string & aInstanceName,const std::string & aType,const std::string & aDomain)861 void PublisherMDnsSd::ServiceSubscription::Resolve(uint32_t aInterfaceIndex,
862 const std::string &aInstanceName,
863 const std::string &aType,
864 const std::string &aDomain)
865 {
866 mResolvingInstances.push_back(
867 MakeUnique<ServiceInstanceResolution>(*this, aInstanceName, aType, aDomain, aInterfaceIndex));
868 mResolvingInstances.back()->Resolve();
869 }
870
RemoveInstanceResolution(PublisherMDnsSd::ServiceInstanceResolution & aInstanceResolution)871 void PublisherMDnsSd::ServiceSubscription::RemoveInstanceResolution(
872 PublisherMDnsSd::ServiceInstanceResolution &aInstanceResolution)
873 {
874 auto it = std::find_if(mResolvingInstances.begin(), mResolvingInstances.end(),
875 [&aInstanceResolution](const std::unique_ptr<ServiceInstanceResolution> &aElem) {
876 return &aInstanceResolution == aElem.get();
877 });
878
879 assert(it != mResolvingInstances.end());
880
881 mResolvingInstances.erase(it);
882 }
883
UpdateAll(MainloopContext & aMainloop) const884 void PublisherMDnsSd::ServiceSubscription::UpdateAll(MainloopContext &aMainloop) const
885 {
886 Update(aMainloop);
887
888 for (const auto &instance : mResolvingInstances)
889 {
890 instance->Update(aMainloop);
891 }
892 }
893
ProcessAll(const MainloopContext & aMainloop,std::vector<DNSServiceRef> & aReadyServices) const894 void PublisherMDnsSd::ServiceSubscription::ProcessAll(const MainloopContext & aMainloop,
895 std::vector<DNSServiceRef> &aReadyServices) const
896 {
897 Process(aMainloop, aReadyServices);
898
899 for (const auto &instance : mResolvingInstances)
900 {
901 instance->Process(aMainloop, aReadyServices);
902 }
903 }
904
Resolve(void)905 void PublisherMDnsSd::ServiceInstanceResolution::Resolve(void)
906 {
907 assert(mServiceRef == nullptr);
908
909 mSubscription->mMDnsSd->mServiceInstanceResolutionBeginTime[std::make_pair(mInstanceName, mTypeEndWithDot)] =
910 Clock::now();
911
912 otbrLogInfo("DNSServiceResolve %s %s inf %u", mInstanceName.c_str(), mTypeEndWithDot.c_str(), mNetifIndex);
913 DNSServiceResolve(&mServiceRef, /* flags */ kDNSServiceFlagsTimeout, mNetifIndex, mInstanceName.c_str(),
914 mTypeEndWithDot.c_str(), mDomain.c_str(), HandleResolveResult, this);
915 }
916
HandleResolveResult(DNSServiceRef aServiceRef,DNSServiceFlags aFlags,uint32_t aInterfaceIndex,DNSServiceErrorType aErrorCode,const char * aFullName,const char * aHostTarget,uint16_t aPort,uint16_t aTxtLen,const unsigned char * aTxtRecord,void * aContext)917 void PublisherMDnsSd::ServiceInstanceResolution::HandleResolveResult(DNSServiceRef aServiceRef,
918 DNSServiceFlags aFlags,
919 uint32_t aInterfaceIndex,
920 DNSServiceErrorType aErrorCode,
921 const char * aFullName,
922 const char * aHostTarget,
923 uint16_t aPort,
924 uint16_t aTxtLen,
925 const unsigned char *aTxtRecord,
926 void * aContext)
927 {
928 static_cast<ServiceInstanceResolution *>(aContext)->HandleResolveResult(
929 aServiceRef, aFlags, aInterfaceIndex, aErrorCode, aFullName, aHostTarget, aPort, aTxtLen, aTxtRecord);
930 }
931
HandleResolveResult(DNSServiceRef aServiceRef,DNSServiceFlags aFlags,uint32_t aInterfaceIndex,DNSServiceErrorType aErrorCode,const char * aFullName,const char * aHostTarget,uint16_t aPort,uint16_t aTxtLen,const unsigned char * aTxtRecord)932 void PublisherMDnsSd::ServiceInstanceResolution::HandleResolveResult(DNSServiceRef aServiceRef,
933 DNSServiceFlags aFlags,
934 uint32_t aInterfaceIndex,
935 DNSServiceErrorType aErrorCode,
936 const char * aFullName,
937 const char * aHostTarget,
938 uint16_t aPort,
939 uint16_t aTxtLen,
940 const unsigned char *aTxtRecord)
941 {
942 OTBR_UNUSED_VARIABLE(aServiceRef);
943
944 std::string instanceName, type, domain;
945 otbrError error = OTBR_ERROR_NONE;
946
947 otbrLogInfo("DNSServiceResolve reply: %s host %s:%d, TXT=%dB inf %u, flags=%u", aFullName, aHostTarget, aPort,
948 aTxtLen, aInterfaceIndex, aFlags);
949
950 VerifyOrExit(aErrorCode == kDNSServiceErr_NoError);
951
952 SuccessOrExit(error = SplitFullServiceInstanceName(aFullName, instanceName, type, domain));
953
954 mInstanceInfo.mNetifIndex = aInterfaceIndex;
955 mInstanceInfo.mName = instanceName;
956 mInstanceInfo.mHostName = aHostTarget;
957 mInstanceInfo.mPort = ntohs(aPort);
958 mInstanceInfo.mTxtData.assign(aTxtRecord, aTxtRecord + aTxtLen);
959 // priority and weight are not given in the reply
960 mInstanceInfo.mPriority = 0;
961 mInstanceInfo.mWeight = 0;
962
963 DeallocateServiceRef();
964 error = GetAddrInfo(aInterfaceIndex);
965
966 exit:
967 if (error != OTBR_ERROR_NONE)
968 {
969 otbrLogWarning("Failed to resolve service instance %s", aFullName);
970 }
971
972 if (aErrorCode != kDNSServiceErr_NoError || error != OTBR_ERROR_NONE)
973 {
974 mSubscription->mMDnsSd->OnServiceResolveFailed(mSubscription->mType, mInstanceName, aErrorCode);
975 FinishResolution();
976 }
977 }
978
GetAddrInfo(uint32_t aInterfaceIndex)979 otbrError PublisherMDnsSd::ServiceInstanceResolution::GetAddrInfo(uint32_t aInterfaceIndex)
980 {
981 DNSServiceErrorType dnsError;
982
983 assert(mServiceRef == nullptr);
984
985 otbrLogInfo("DNSServiceGetAddrInfo %s inf %d", mInstanceInfo.mHostName.c_str(), aInterfaceIndex);
986
987 dnsError = DNSServiceGetAddrInfo(&mServiceRef, kDNSServiceFlagsTimeout, aInterfaceIndex,
988 kDNSServiceProtocol_IPv6 | kDNSServiceProtocol_IPv4,
989 mInstanceInfo.mHostName.c_str(), HandleGetAddrInfoResult, this);
990
991 if (dnsError != kDNSServiceErr_NoError)
992 {
993 otbrLogWarning("DNSServiceGetAddrInfo failed: %s", DNSErrorToString(dnsError));
994 }
995
996 return dnsError == kDNSServiceErr_NoError ? OTBR_ERROR_NONE : OTBR_ERROR_MDNS;
997 }
998
HandleGetAddrInfoResult(DNSServiceRef aServiceRef,DNSServiceFlags aFlags,uint32_t aInterfaceIndex,DNSServiceErrorType aErrorCode,const char * aHostName,const struct sockaddr * aAddress,uint32_t aTtl,void * aContext)999 void PublisherMDnsSd::ServiceInstanceResolution::HandleGetAddrInfoResult(DNSServiceRef aServiceRef,
1000 DNSServiceFlags aFlags,
1001 uint32_t aInterfaceIndex,
1002 DNSServiceErrorType aErrorCode,
1003 const char * aHostName,
1004 const struct sockaddr *aAddress,
1005 uint32_t aTtl,
1006 void * aContext)
1007 {
1008 static_cast<ServiceInstanceResolution *>(aContext)->HandleGetAddrInfoResult(aServiceRef, aFlags, aInterfaceIndex,
1009 aErrorCode, aHostName, aAddress, aTtl);
1010 }
1011
HandleGetAddrInfoResult(DNSServiceRef aServiceRef,DNSServiceFlags aFlags,uint32_t aInterfaceIndex,DNSServiceErrorType aErrorCode,const char * aHostName,const struct sockaddr * aAddress,uint32_t aTtl)1012 void PublisherMDnsSd::ServiceInstanceResolution::HandleGetAddrInfoResult(DNSServiceRef aServiceRef,
1013 DNSServiceFlags aFlags,
1014 uint32_t aInterfaceIndex,
1015 DNSServiceErrorType aErrorCode,
1016 const char * aHostName,
1017 const struct sockaddr *aAddress,
1018 uint32_t aTtl)
1019 {
1020 OTBR_UNUSED_VARIABLE(aServiceRef);
1021 OTBR_UNUSED_VARIABLE(aInterfaceIndex);
1022
1023 Ip6Address address;
1024
1025 otbrLog(aErrorCode == kDNSServiceErr_NoError ? OTBR_LOG_INFO : OTBR_LOG_WARNING, OTBR_LOG_TAG,
1026 "DNSServiceGetAddrInfo reply: flags=%" PRIu32 ", host=%s, sa_family=%u, error=%" PRId32, aFlags, aHostName,
1027 static_cast<unsigned int>(aAddress->sa_family), aErrorCode);
1028
1029 VerifyOrExit(aErrorCode == kDNSServiceErr_NoError);
1030 VerifyOrExit((aFlags & kDNSServiceFlagsAdd) && aAddress->sa_family == AF_INET6);
1031
1032 address.CopyFrom(*reinterpret_cast<const struct sockaddr_in6 *>(aAddress));
1033 VerifyOrExit(!address.IsUnspecified() && !address.IsLinkLocal() && !address.IsMulticast() && !address.IsLoopback(),
1034 otbrLogDebug("DNSServiceGetAddrInfo ignores address %s", address.ToString().c_str()));
1035
1036 mInstanceInfo.mAddresses.push_back(address);
1037 mInstanceInfo.mTtl = aTtl;
1038
1039 otbrLogInfo("DNSServiceGetAddrInfo reply: address=%s, ttl=%" PRIu32, address.ToString().c_str(), aTtl);
1040
1041 exit:
1042 if (!mInstanceInfo.mAddresses.empty() || aErrorCode != kDNSServiceErr_NoError)
1043 {
1044 FinishResolution();
1045 }
1046 }
1047
FinishResolution(void)1048 void PublisherMDnsSd::ServiceInstanceResolution::FinishResolution(void)
1049 {
1050 ServiceSubscription * subscription = mSubscription;
1051 std::string serviceName = mSubscription->mType;
1052 DiscoveredInstanceInfo instanceInfo = mInstanceInfo;
1053
1054 // NOTE: `RemoveInstanceResolution` will free this `ServiceInstanceResolution` object.
1055 // So, We can't access `mSubscription` after `RemoveInstanceResolution`.
1056 subscription->RemoveInstanceResolution(*this);
1057
1058 // NOTE: The `ServiceSubscription` object may be freed in `OnServiceResolved`.
1059 subscription->mMDnsSd->OnServiceResolved(serviceName, instanceInfo);
1060 }
1061
Resolve(void)1062 void PublisherMDnsSd::HostSubscription::Resolve(void)
1063 {
1064 std::string fullHostName = MakeFullHostName(mHostName);
1065
1066 assert(mServiceRef == nullptr);
1067
1068 mMDnsSd->mHostResolutionBeginTime[mHostName] = Clock::now();
1069
1070 otbrLogInfo("DNSServiceGetAddrInfo %s inf %d", fullHostName.c_str(), kDNSServiceInterfaceIndexAny);
1071
1072 DNSServiceGetAddrInfo(&mServiceRef, /* flags */ 0, kDNSServiceInterfaceIndexAny,
1073 kDNSServiceProtocol_IPv6 | kDNSServiceProtocol_IPv4, fullHostName.c_str(),
1074 HandleResolveResult, this);
1075 }
1076
HandleResolveResult(DNSServiceRef aServiceRef,DNSServiceFlags aFlags,uint32_t aInterfaceIndex,DNSServiceErrorType aErrorCode,const char * aHostName,const struct sockaddr * aAddress,uint32_t aTtl,void * aContext)1077 void PublisherMDnsSd::HostSubscription::HandleResolveResult(DNSServiceRef aServiceRef,
1078 DNSServiceFlags aFlags,
1079 uint32_t aInterfaceIndex,
1080 DNSServiceErrorType aErrorCode,
1081 const char * aHostName,
1082 const struct sockaddr *aAddress,
1083 uint32_t aTtl,
1084 void * aContext)
1085 {
1086 static_cast<HostSubscription *>(aContext)->HandleResolveResult(aServiceRef, aFlags, aInterfaceIndex, aErrorCode,
1087 aHostName, aAddress, aTtl);
1088 }
1089
HandleResolveResult(DNSServiceRef aServiceRef,DNSServiceFlags aFlags,uint32_t aInterfaceIndex,DNSServiceErrorType aErrorCode,const char * aHostName,const struct sockaddr * aAddress,uint32_t aTtl)1090 void PublisherMDnsSd::HostSubscription::HandleResolveResult(DNSServiceRef aServiceRef,
1091 DNSServiceFlags aFlags,
1092 uint32_t aInterfaceIndex,
1093 DNSServiceErrorType aErrorCode,
1094 const char * aHostName,
1095 const struct sockaddr *aAddress,
1096 uint32_t aTtl)
1097 {
1098 OTBR_UNUSED_VARIABLE(aServiceRef);
1099 OTBR_UNUSED_VARIABLE(aInterfaceIndex);
1100
1101 Ip6Address address;
1102
1103 otbrLog(aErrorCode == kDNSServiceErr_NoError ? OTBR_LOG_INFO : OTBR_LOG_WARNING, OTBR_LOG_TAG,
1104 "DNSServiceGetAddrInfo reply: flags=%" PRIu32 ", host=%s, sa_family=%u, error=%" PRId32, aFlags, aHostName,
1105 static_cast<unsigned int>(aAddress->sa_family), aErrorCode);
1106
1107 VerifyOrExit(aErrorCode == kDNSServiceErr_NoError);
1108 VerifyOrExit((aFlags & kDNSServiceFlagsAdd) && aAddress->sa_family == AF_INET6);
1109
1110 address.CopyFrom(*reinterpret_cast<const struct sockaddr_in6 *>(aAddress));
1111 VerifyOrExit(!address.IsLinkLocal(),
1112 otbrLogDebug("DNSServiceGetAddrInfo ignore link-local address %s", address.ToString().c_str()));
1113
1114 mHostInfo.mHostName = aHostName;
1115 mHostInfo.mAddresses.push_back(address);
1116 mHostInfo.mTtl = aTtl;
1117
1118 otbrLogInfo("DNSServiceGetAddrInfo reply: address=%s, ttl=%" PRIu32, address.ToString().c_str(), aTtl);
1119
1120 // NOTE: This `HostSubscription` object may be freed in `OnHostResolved`.
1121 mMDnsSd->OnHostResolved(mHostName, mHostInfo);
1122
1123 exit:
1124 if (aErrorCode != kDNSServiceErr_NoError)
1125 {
1126 mMDnsSd->OnHostResolveFailed(aHostName, aErrorCode);
1127 }
1128 }
1129
1130 } // namespace Mdns
1131
1132 } // namespace otbr
1133