2 * Copyright (c) 2018, The OpenThread Authors.
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.
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.
31 * This file implements mDNS service based on mDNSResponder.
34 #include "mdns/mdns_mdnssd.hpp"
38 #include <arpa/inet.h>
45 #include "common/code_utils.hpp"
46 #include "common/logging.hpp"
47 #include "common/time.hpp"
48 #include "utils/strcpy_utils.hpp"
54 static otbrError DNSErrorToOtbrError(DNSServiceErrorType aError)
60 case kDNSServiceErr_NoError:
61 error = OTBR_ERROR_NONE;
64 case kDNSServiceErr_NoSuchKey:
65 case kDNSServiceErr_NoSuchName:
66 case kDNSServiceErr_NoSuchRecord:
67 error = OTBR_ERROR_NOT_FOUND;
70 case kDNSServiceErr_Invalid:
71 case kDNSServiceErr_BadParam:
72 case kDNSServiceErr_BadFlags:
73 case kDNSServiceErr_BadInterfaceIndex:
74 error = OTBR_ERROR_INVALID_ARGS;
77 case kDNSServiceErr_AlreadyRegistered:
78 case kDNSServiceErr_NameConflict:
79 error = OTBR_ERROR_DUPLICATED;
82 case kDNSServiceErr_Unsupported:
83 error = OTBR_ERROR_NOT_IMPLEMENTED;
87 error = OTBR_ERROR_MDNS;
94 static const char *DNSErrorToString(DNSServiceErrorType aError)
98 case kDNSServiceErr_NoError:
101 case kDNSServiceErr_Unknown:
105 case kDNSServiceErr_NoSuchName:
106 return "No Such Name";
108 case kDNSServiceErr_NoMemory:
111 case kDNSServiceErr_BadParam:
114 case kDNSServiceErr_BadReference:
115 return "Bad Reference";
117 case kDNSServiceErr_BadState:
120 case kDNSServiceErr_BadFlags:
123 case kDNSServiceErr_Unsupported:
124 return "Unsupported";
126 case kDNSServiceErr_NotInitialized:
127 return "Not Initialized";
129 case kDNSServiceErr_AlreadyRegistered:
130 return "Already Registered";
132 case kDNSServiceErr_NameConflict:
133 return "Name Conflict";
135 case kDNSServiceErr_Invalid:
138 case kDNSServiceErr_Firewall:
141 case kDNSServiceErr_Incompatible:
142 // client library incompatible with daemon
143 return "Incompatible";
145 case kDNSServiceErr_BadInterfaceIndex:
146 return "Bad Interface Index";
148 case kDNSServiceErr_Refused:
151 case kDNSServiceErr_NoSuchRecord:
152 return "No Such Record";
154 case kDNSServiceErr_NoAuth:
157 case kDNSServiceErr_NoSuchKey:
158 return "No Such Key";
160 case kDNSServiceErr_NATTraversal:
161 return "NAT Traversal";
163 case kDNSServiceErr_DoubleNAT:
166 case kDNSServiceErr_BadTime:
167 // Codes up to here existed in Tiger
170 case kDNSServiceErr_BadSig:
173 case kDNSServiceErr_BadKey:
176 case kDNSServiceErr_Transient:
179 case kDNSServiceErr_ServiceNotRunning:
180 // Background daemon not running
181 return "Service Not Running";
183 case kDNSServiceErr_NATPortMappingUnsupported:
184 // NAT doesn't support NAT-PMP or UPnP
185 return "NAT Port Mapping Unsupported";
187 case kDNSServiceErr_NATPortMappingDisabled:
188 // NAT supports NAT-PMP or UPnP but it's disabled by the administrator
189 return "NAT Port Mapping Disabled";
191 case kDNSServiceErr_NoRouter:
192 // No router currently configured (probably no network connectivity)
195 case kDNSServiceErr_PollingMode:
196 return "Polling Mode";
198 case kDNSServiceErr_Timeout:
207 PublisherMDnsSd::PublisherMDnsSd(int aProtocol, const char *aDomain, StateHandler aHandler, void *aContext)
210 , mState(State::kIdle)
211 , mStateHandler(aHandler)
214 OTBR_UNUSED_VARIABLE(aProtocol);
217 PublisherMDnsSd::~PublisherMDnsSd(void)
222 otbrError PublisherMDnsSd::Start(void)
224 mState = State::kReady;
225 mStateHandler(mContext, State::kReady);
226 return OTBR_ERROR_NONE;
229 bool PublisherMDnsSd::IsStarted(void) const
231 return mState == State::kReady;
234 void PublisherMDnsSd::Stop(void)
236 VerifyOrExit(mState == State::kReady);
238 for (Service &service : mServices)
240 otbrLog(OTBR_LOG_INFO, "[mdns] remove service %s.%s", service.mName, service.mType);
241 DNSServiceRefDeallocate(service.mService);
245 otbrLog(OTBR_LOG_INFO, "[mdns] remove all hosts");
246 DNSServiceRefDeallocate(mHostsRef);
254 void PublisherMDnsSd::UpdateFdSet(fd_set & aReadFdSet,
255 fd_set & aWriteFdSet,
256 fd_set & aErrorFdSet,
260 OTBR_UNUSED_VARIABLE(aWriteFdSet);
261 OTBR_UNUSED_VARIABLE(aErrorFdSet);
262 OTBR_UNUSED_VARIABLE(aTimeout);
264 for (Service &service : mServices)
266 assert(service.mService != nullptr);
268 int fd = DNSServiceRefSockFD(service.mService);
272 FD_SET(fd, &aReadFdSet);
280 if (mHostsRef != nullptr)
282 int fd = DNSServiceRefSockFD(mHostsRef);
286 FD_SET(fd, &aReadFdSet);
295 void PublisherMDnsSd::Process(const fd_set &aReadFdSet, const fd_set &aWriteFdSet, const fd_set &aErrorFdSet)
297 std::vector<DNSServiceRef> readyServices;
299 OTBR_UNUSED_VARIABLE(aWriteFdSet);
300 OTBR_UNUSED_VARIABLE(aErrorFdSet);
302 for (Service &service : mServices)
304 int fd = DNSServiceRefSockFD(service.mService);
306 if (FD_ISSET(fd, &aReadFdSet))
308 readyServices.push_back(service.mService);
312 if (mHostsRef != nullptr)
314 int fd = DNSServiceRefSockFD(mHostsRef);
316 if (FD_ISSET(fd, &aReadFdSet))
318 readyServices.push_back(mHostsRef);
322 for (DNSServiceRef serviceRef : readyServices)
324 DNSServiceErrorType error = DNSServiceProcessResult(serviceRef);
326 if (error != kDNSServiceErr_NoError)
328 otbrLog(OTBR_LOG_WARNING, "[mdns] DNSServiceProcessResult failed: %s", DNSErrorToString(error));
333 void PublisherMDnsSd::HandleServiceRegisterResult(DNSServiceRef aService,
334 const DNSServiceFlags aFlags,
335 DNSServiceErrorType aError,
338 const char * aDomain,
341 static_cast<PublisherMDnsSd *>(aContext)->HandleServiceRegisterResult(aService, aFlags, aError, aName, aType,
345 void PublisherMDnsSd::HandleServiceRegisterResult(DNSServiceRef aServiceRef,
346 const DNSServiceFlags aFlags,
347 DNSServiceErrorType aError,
350 const char * aDomain)
352 OTBR_UNUSED_VARIABLE(aDomain);
354 otbrError error = DNSErrorToOtbrError(aError);
355 std::string originalInstanceName;
356 ServiceIterator service = FindPublishedService(aServiceRef);
358 VerifyOrExit(service != mServices.end());
360 // mDNSResponder could auto-rename the service instance name when name conflict
361 // is detected. In this case, `aName` may not match `service->mName` and we
362 // should use the original `service->mName` to find associated SRP service.
363 originalInstanceName = service->mName;
365 otbrLog(OTBR_LOG_INFO, "[mdns] received reply for service %s.%s", originalInstanceName.c_str(), aType);
367 if (originalInstanceName != aName)
369 otbrLog(OTBR_LOG_INFO, "[mdns] service %s.%s renamed to %s.%s", originalInstanceName.c_str(), aType, aName,
373 if (aError == kDNSServiceErr_NoError)
375 otbrLog(OTBR_LOG_INFO, "[mdns] successfully registered service %s.%s", originalInstanceName.c_str(), aType);
376 if (aFlags & kDNSServiceFlagsAdd)
378 RecordService(originalInstanceName.c_str(), aType, aServiceRef);
382 DiscardService(originalInstanceName.c_str(), aType, aServiceRef);
387 otbrLog(OTBR_LOG_ERR, "[mdns] failed to register service %s.%s: %s", originalInstanceName.c_str(), aType,
388 DNSErrorToString(aError));
389 DiscardService(originalInstanceName.c_str(), aType, aServiceRef);
392 if (mServiceHandler != nullptr)
394 // TODO: pass the renewed service instance name back to SRP server handler.
395 mServiceHandler(originalInstanceName.c_str(), aType, error, mServiceHandlerContext);
402 void PublisherMDnsSd::DiscardService(const char *aName, const char *aType, DNSServiceRef aServiceRef)
404 ServiceIterator service = FindPublishedService(aName, aType);
406 if (service != mServices.end())
408 assert(aServiceRef == nullptr || aServiceRef == service->mService);
410 otbrLog(OTBR_LOG_INFO, "[mdns] remove service ref %p", service->mService);
412 DNSServiceRefDeallocate(service->mService);
413 mServices.erase(service);
417 void PublisherMDnsSd::RecordService(const char *aName, const char *aType, DNSServiceRef aServiceRef)
419 ServiceIterator service = FindPublishedService(aName, aType);
421 if (service == mServices.end())
425 otbrLog(OTBR_LOG_INFO, "[mdns] add service: %s.%s (ref: %p)", aName, aType, aServiceRef);
427 strcpy(newService.mName, aName);
428 strcpy(newService.mType, aType);
429 newService.mService = aServiceRef;
430 mServices.push_back(newService);
434 assert(service->mService == aServiceRef);
438 otbrError PublisherMDnsSd::PublishService(const char * aHostName,
442 const TxtList &aTxtList)
444 otbrError ret = OTBR_ERROR_NONE;
446 uint8_t txt[kMaxSizeOfTxtRecord];
447 uint16_t txtLength = sizeof(txt);
448 ServiceIterator service = FindPublishedService(aName, aType);
449 DNSServiceRef serviceRef = nullptr;
450 char fullHostName[kMaxSizeOfDomain];
452 if (aHostName != nullptr)
454 HostIterator host = FindPublishedHost(aHostName);
456 // Make sure that the host has been published.
457 VerifyOrExit(host != mHosts.end(), ret = OTBR_ERROR_INVALID_ARGS);
458 SuccessOrExit(error = MakeFullName(fullHostName, sizeof(fullHostName), aHostName));
461 SuccessOrExit(ret = EncodeTxtData(aTxtList, txt, txtLength));
463 if (service != mServices.end())
465 otbrLog(OTBR_LOG_INFO, "[mdns] update service %s.%s", aName, aType);
467 // Setting TTL to 0 to use default value.
468 SuccessOrExit(error = DNSServiceUpdateRecord(service->mService, nullptr, 0, txtLength, txt, /* ttl */ 0));
470 if (mServiceHandler != nullptr)
472 mServiceHandler(aName, aType, DNSErrorToOtbrError(error), mServiceHandlerContext);
477 SuccessOrExit(error = DNSServiceRegister(&serviceRef, /* flags */ 0, kDNSServiceInterfaceIndexAny, aName, aType,
478 mDomain, (aHostName != nullptr) ? fullHostName : nullptr, htons(aPort),
479 txtLength, txt, HandleServiceRegisterResult, this));
480 RecordService(aName, aType, serviceRef);
484 if (error != kDNSServiceErr_NoError)
486 ret = OTBR_ERROR_MDNS;
487 otbrLog(OTBR_LOG_ERR, "[mdns] failed to publish service for mdnssd error: %s!", DNSErrorToString(error));
492 otbrError PublisherMDnsSd::UnpublishService(const char *aName, const char *aType)
494 DiscardService(aName, aType);
495 return OTBR_ERROR_NONE;
498 otbrError PublisherMDnsSd::DiscardHost(const char *aName, bool aSendGoodbye)
500 otbrError ret = OTBR_ERROR_NONE;
502 HostIterator host = FindPublishedHost(aName);
504 VerifyOrExit(mHostsRef != nullptr && host != mHosts.end());
506 otbrLog(OTBR_LOG_INFO, "[mdns] remove host: %s (record ref: %p)", host->mName, host->mRecord);
510 // The Bonjour mDNSResponder somehow doesn't send goodbye message for the AAAA record when it is
511 // removed by `DNSServiceRemoveRecord`. Per RFC 6762, a goodbye message of a record sets its TTL
512 // to zero but the receiver should record the TTL of 1 and flushes the cache 1 second later. Here
513 // we remove the AAAA record after updating its TTL to 1 second. This has the same effect as
514 // sending a goodbye message.
515 // TODO: resolve the goodbye issue with Bonjour mDNSResponder.
516 error = DNSServiceUpdateRecord(mHostsRef, host->mRecord, kDNSServiceFlagsUnique, host->mAddress.size(),
517 &host->mAddress.front(), /* ttl */ 1);
518 // Do not SuccessOrExit so that we always erase the host entry.
520 DNSServiceRemoveRecord(mHostsRef, host->mRecord, /* flags */ 0);
525 if (error != kDNSServiceErr_NoError)
527 ret = OTBR_ERROR_MDNS;
528 otbrLog(OTBR_LOG_ERR, "[mdns] failed to remove host %s for mdnssd error: %s!", aName, DNSErrorToString(error));
533 void PublisherMDnsSd::RecordHost(const char * aName,
534 const uint8_t *aAddress,
535 uint8_t aAddressLength,
536 DNSRecordRef aRecordRef)
538 HostIterator host = FindPublishedHost(aName);
540 if (host == mHosts.end())
544 otbrLog(OTBR_LOG_INFO, "[mdns] add new host %s", aName);
546 strcpy(newHost.mName, aName);
547 std::copy(aAddress, aAddress + aAddressLength, newHost.mAddress.begin());
548 newHost.mRecord = aRecordRef;
549 mHosts.push_back(newHost);
553 otbrLog(OTBR_LOG_INFO, "[mdns] update existing host %s", host->mName);
555 // The address of the host may be updated.
556 std::copy(aAddress, aAddress + aAddressLength, host->mAddress.begin());
557 assert(host->mRecord == aRecordRef);
561 otbrError PublisherMDnsSd::PublishHost(const char *aName, const uint8_t *aAddress, uint8_t aAddressLength)
563 otbrError ret = OTBR_ERROR_NONE;
565 char fullName[kMaxSizeOfDomain];
566 HostIterator host = FindPublishedHost(aName);
568 // Supports only IPv6 for now, may support IPv4 in the future.
569 VerifyOrExit(aAddressLength == OTBR_IP6_ADDRESS_SIZE, error = OTBR_ERROR_INVALID_ARGS);
571 SuccessOrExit(ret = MakeFullName(fullName, sizeof(fullName), aName));
573 if (mHostsRef == nullptr)
575 SuccessOrExit(error = DNSServiceCreateConnection(&mHostsRef));
578 if (host != mHosts.end())
580 otbrLog(OTBR_LOG_INFO, "[mdns] update existing host %s", aName);
581 SuccessOrExit(error = DNSServiceUpdateRecord(mHostsRef, host->mRecord, kDNSServiceFlagsUnique, aAddressLength,
582 aAddress, /* ttl */ 0));
584 RecordHost(aName, aAddress, aAddressLength, host->mRecord);
585 if (mHostHandler != nullptr)
587 mHostHandler(aName, DNSErrorToOtbrError(error), mHostHandlerContext);
594 otbrLog(OTBR_LOG_INFO, "[mdns] publish new host %s", aName);
595 SuccessOrExit(error = DNSServiceRegisterRecord(mHostsRef, &record, kDNSServiceFlagsUnique,
596 kDNSServiceInterfaceIndexAny, fullName, kDNSServiceType_AAAA,
597 kDNSServiceClass_IN, aAddressLength, aAddress, /* ttl */ 0,
598 HandleRegisterHostResult, this));
599 RecordHost(aName, aAddress, aAddressLength, record);
603 if (error != kDNSServiceErr_NoError)
605 if (mHostsRef != nullptr)
607 DNSServiceRefDeallocate(mHostsRef);
611 ret = OTBR_ERROR_MDNS;
612 otbrLog(OTBR_LOG_ERR, "[mdns] failed to publish/update host %s for mdnssd error: %s!", aName,
613 DNSErrorToString(error));
618 otbrError PublisherMDnsSd::UnpublishHost(const char *aName)
620 return DiscardHost(aName);
623 void PublisherMDnsSd::HandleRegisterHostResult(DNSServiceRef aHostsConnection,
624 DNSRecordRef aHostRecord,
625 DNSServiceFlags aFlags,
626 DNSServiceErrorType aErrorCode,
629 static_cast<PublisherMDnsSd *>(aContext)->HandleRegisterHostResult(aHostsConnection, aHostRecord, aFlags,
633 void PublisherMDnsSd::HandleRegisterHostResult(DNSServiceRef aHostsConnection,
634 DNSRecordRef aHostRecord,
635 DNSServiceFlags aFlags,
636 DNSServiceErrorType aErrorCode)
638 OTBR_UNUSED_VARIABLE(aHostsConnection);
639 OTBR_UNUSED_VARIABLE(aFlags);
641 HostIterator host = FindPublishedHost(aHostRecord);
642 std::string hostName;
644 VerifyOrExit(host != mHosts.end());
645 hostName = host->mName;
647 otbrLog(OTBR_LOG_INFO, "[mdns] received reply for host %s", hostName.c_str());
649 if (aErrorCode == kDNSServiceErr_NoError)
651 otbrLog(OTBR_LOG_INFO, "[mdns] successfully registered host %s", hostName.c_str());
655 otbrLog(OTBR_LOG_WARNING, "[mdns] failed to register host %s for mdnssd error: %s", hostName.c_str(),
656 DNSErrorToString(aErrorCode));
658 DiscardHost(hostName.c_str(), /* aSendGoodbye */ false);
661 if (mHostHandler != nullptr)
663 mHostHandler(hostName.c_str(), DNSErrorToOtbrError(aErrorCode), mHostHandlerContext);
670 otbrError PublisherMDnsSd::MakeFullName(char *aFullName, size_t aFullNameLength, const char *aName)
672 otbrError error = OTBR_ERROR_NONE;
673 size_t nameLength = strlen(aName);
674 const char *domain = (mDomain == nullptr) ? "local." : mDomain;
676 VerifyOrExit(nameLength <= kMaxSizeOfHost, error = OTBR_ERROR_INVALID_ARGS);
678 assert(aFullNameLength >= nameLength + sizeof(".") + strlen(domain));
680 strcpy(aFullName, aName);
681 strcpy(aFullName + nameLength, ".");
682 strcpy(aFullName + nameLength + 1, domain);
688 PublisherMDnsSd::ServiceIterator PublisherMDnsSd::FindPublishedService(const char *aName, const char *aType)
690 return std::find_if(mServices.begin(), mServices.end(), [&aName, aType](const Service &service) {
691 return strcmp(aName, service.mName) == 0 && IsServiceTypeEqual(aType, service.mType);
695 PublisherMDnsSd::ServiceIterator PublisherMDnsSd::FindPublishedService(const DNSServiceRef &aServiceRef)
697 return std::find_if(mServices.begin(), mServices.end(),
698 [&aServiceRef](const Service &service) { return service.mService == aServiceRef; });
701 PublisherMDnsSd::HostIterator PublisherMDnsSd::FindPublishedHost(const DNSRecordRef &aRecordRef)
703 return std::find_if(mHosts.begin(), mHosts.end(),
704 [&aRecordRef](const Host &host) { return host.mRecord == aRecordRef; });
707 PublisherMDnsSd::HostIterator PublisherMDnsSd::FindPublishedHost(const char *aHostName)
709 return std::find_if(mHosts.begin(), mHosts.end(),
710 [&aHostName](const Host &host) { return strcmp(host.mName, aHostName) == 0; });
713 Publisher *Publisher::Create(int aFamily, const char *aDomain, StateHandler aHandler, void *aContext)
715 return new PublisherMDnsSd(aFamily, aDomain, aHandler, aContext);
718 void Publisher::Destroy(Publisher *aPublisher)
720 delete static_cast<PublisherMDnsSd *>(aPublisher);