diff --git a/src/platform/Darwin/DnssdContexts.cpp b/src/platform/Darwin/DnssdContexts.cpp index c195b268fbe747..375d0fee8bc8cf 100644 --- a/src/platform/Darwin/DnssdContexts.cpp +++ b/src/platform/Darwin/DnssdContexts.cpp @@ -129,7 +129,6 @@ CHIP_ERROR GenericContext::Finalize(DNSServiceErrorType err) { chip::Platform::Delete(this); } - return (kDNSServiceErr_NoError == err) ? CHIP_NO_ERROR : CHIP_ERROR_INTERNAL; } @@ -274,6 +273,27 @@ void RegisterContext::DispatchSuccess() callback(context, typeWithoutSubTypes.c_str(), mInstanceName.c_str(), CHIP_NO_ERROR); } +RegisterRecordContext::RegisterRecordContext(DnssdPublishCallback cb, void * cbContext) +{ + type = ContextType::RegisterRecord; + context = cbContext; + callback = cb; +} + +void RegisterRecordContext::DispatchFailure(DNSServiceErrorType err) +{ + ChipLogError(Discovery, "Mdns: Register Record Failure (%s)", Error::ToString(err)); + // Return CHIP_ERROR_MDNS_COLLISION error so that the caller can handle a name conflict + CHIP_ERROR error = (err == kDNSServiceErr_NameConflict) ? CHIP_ERROR_MDNS_COLLISION : CHIP_ERROR_INTERNAL; + callback(context, nullptr, error); + MdnsContexts::GetInstance().Remove(this); +} + +void RegisterRecordContext::DispatchSuccess() +{ + MdnsContexts::GetInstance().Remove(this); +} + BrowseContext::BrowseContext(void * cbContext, DnssdBrowseCallback cb, DnssdServiceProtocol cbContextProtocol) { type = ContextType::Browse; diff --git a/src/platform/Darwin/DnssdImpl.cpp b/src/platform/Darwin/DnssdImpl.cpp index 9a989490dc812c..59e19494c86438 100644 --- a/src/platform/Darwin/DnssdImpl.cpp +++ b/src/platform/Darwin/DnssdImpl.cpp @@ -18,6 +18,7 @@ #include "MdnsError.h" #include +#include #include #include @@ -36,10 +37,11 @@ constexpr const char * kLocalDot = "local."; constexpr const char * kProtocolTcp = "._tcp"; constexpr const char * kProtocolUdp = "._udp"; -constexpr DNSServiceFlags kRegisterFlags = kDNSServiceFlagsNoAutoRename; -constexpr DNSServiceFlags kBrowseFlags = 0; -constexpr DNSServiceFlags kGetAddrInfoFlags = kDNSServiceFlagsTimeout | kDNSServiceFlagsShareConnection; -constexpr DNSServiceFlags kResolveFlags = kDNSServiceFlagsShareConnection; +constexpr DNSServiceFlags kRegisterFlags = kDNSServiceFlagsNoAutoRename; +constexpr DNSServiceFlags kRegisterRecordFlags = kDNSServiceFlagsShared; +constexpr DNSServiceFlags kBrowseFlags = 0; +constexpr DNSServiceFlags kGetAddrInfoFlags = kDNSServiceFlagsTimeout | kDNSServiceFlagsShareConnection; +constexpr DNSServiceFlags kResolveFlags = kDNSServiceFlagsShareConnection; bool IsSupportedProtocol(DnssdServiceProtocol protocol) { @@ -162,17 +164,108 @@ MdnsContexts MdnsContexts::sInstance; namespace { +static void OnRegisterRecord(DNSServiceRef sdRef, DNSRecordRef recordRef, DNSServiceFlags flags, DNSServiceErrorType err, + void * context) +{ + if (err != kDNSServiceErr_NoError) + { + ChipLogError(Discovery, "Mdns: %s Registered Record failed. Error: %d", __func__, err); + auto sdCtx = reinterpret_cast(context); + sdCtx->Finalize(err); + } +}; + static void OnRegister(DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType err, const char * name, const char * type, const char * domain, void * context) { - ChipLogDetail(Discovery, "Mdns: %s name: %s, type: %s, domain: %s, flags: %d", __func__, name, type, domain, flags); - + ChipLogDetail(Discovery, "Mdns: %s name: %s, type: %s, domain: %s, flags: %d Error %d", __func__, name, type, domain, flags, + err); auto sdCtx = reinterpret_cast(context); sdCtx->Finalize(err); }; +CHIP_ERROR RegisterService(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceId, const char * name, const char * type, + const char * domain, const char * hostname, uint16_t port, uint16_t txtLen, const void * txtRecord, + DNSServiceRegisterReply callback, RegisterContext * sdCtx) +{ + auto err = DNSServiceRegister(&sdRef, flags, interfaceId, name, type, domain, hostname, ntohs(port), txtLen, txtRecord, + callback, sdCtx); + ChipLogProgress(Discovery, "Registering service %s on port %u with type: %s on interface id: %u hostname %s error: %d" PRIu32, + name, port, type, interfaceId, hostname, err); + VerifyOrReturnError(kDNSServiceErr_NoError == err, sdCtx->Finalize(err)); + return MdnsContexts::GetInstance().Add(sdCtx, sdRef); +} + +CHIP_ERROR RegisterRecord(RegisterRecordContext * registerRecordCtx, const char * type, uint32_t interfaceId, const char * hostname) +{ + DNSServiceRef sdRef; + auto err = DNSServiceCreateConnection(&sdRef); + VerifyOrReturnError((kDNSServiceErr_NoError == err || sdRef != nullptr), registerRecordCtx->Finalize(err)); + + auto error = MdnsContexts::GetInstance().Add(registerRecordCtx, sdRef); + VerifyOrReturnError(CHIP_NO_ERROR == error, error); + // Get all the interface addresses + struct ifaddrs * ifaddr; + auto ret = getifaddrs(&ifaddr); + if (ret) + { + ChipLogError(Discovery, "Getting interface addresses failed %d.", ret); + freeifaddrs(ifaddr); + } + VerifyOrReturnError(ret == 0, registerRecordCtx->Finalize(ret)); + + // Call DNSServiceRegisterRecord for each interface if interface id is of type any so we can register the address for each + // interface Otherwise just register record for the given interface id + bool isInterfaceIndexAny = (interfaceId == kDNSServiceInterfaceIndexAny); + for (struct ifaddrs * ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) + { + if (ifa->ifa_addr->sa_family == AF_INET) + { + struct sockaddr_in * inaddr = (struct sockaddr_in *) ifa->ifa_addr; + bool shouldRegisterRecord = + (isInterfaceIndexAny || (!isInterfaceIndexAny && (if_nametoindex(ifa->ifa_name) == interfaceId))); + if (inaddr && shouldRegisterRecord) + { + ChipLogDetail(Discovery, "Registering Record for hostname %s interface %d", hostname, + if_nametoindex(ifa->ifa_name)); + DNSRecordRef dnsRecordRef; + err = DNSServiceRegisterRecord(sdRef, &dnsRecordRef, kRegisterRecordFlags, if_nametoindex(ifa->ifa_name), hostname, + kDNSServiceType_A, kDNSServiceClass_IN, 4, &inaddr->sin_addr, 0, OnRegisterRecord, + registerRecordCtx); + if (err || dnsRecordRef == nullptr) + { + freeifaddrs(ifaddr); + } + VerifyOrReturnError(kDNSServiceErr_NoError == err, registerRecordCtx->Finalize(err)); + } + } + else if (ifa->ifa_addr->sa_family == AF_INET6) + { + struct sockaddr_in6 * inaddr = (struct sockaddr_in6 *) ifa->ifa_addr; + bool shouldRegisterRecord = + (isInterfaceIndexAny || (!isInterfaceIndexAny && (if_nametoindex(ifa->ifa_name) == interfaceId))); + if (inaddr && shouldRegisterRecord) + { + ChipLogDetail(Discovery, "Registering Record for hostname %s interface %d", hostname, + if_nametoindex(ifa->ifa_name)); + DNSRecordRef dnsRecordRef; + err = DNSServiceRegisterRecord(sdRef, &dnsRecordRef, kRegisterRecordFlags, if_nametoindex(ifa->ifa_name), hostname, + kDNSServiceType_AAAA, kDNSServiceClass_IN, 16, &inaddr->sin6_addr, 0, + OnRegisterRecord, registerRecordCtx); + if (err || dnsRecordRef == nullptr) + { + freeifaddrs(ifaddr); + } + VerifyOrReturnError(kDNSServiceErr_NoError == err, registerRecordCtx->Finalize(err)); + } + } + } + freeifaddrs(ifaddr); + return CHIP_NO_ERROR; +} + CHIP_ERROR Register(void * context, DnssdPublishCallback callback, uint32_t interfaceId, const char * type, const char * name, - uint16_t port, ScopedTXTRecord & record) + const char * hostname, uint16_t port, ScopedTXTRecord & record) { ChipLogProgress(Discovery, "Publishing service %s on port %u with type: %s on interface id: %" PRIu32, name, port, type, interfaceId); @@ -185,15 +278,18 @@ CHIP_ERROR Register(void * context, DnssdPublishCallback callback, uint32_t inte return CHIP_NO_ERROR; } - sdCtx = chip::Platform::New(type, name, callback, context); - VerifyOrReturnError(nullptr != sdCtx, CHIP_ERROR_NO_MEMORY); - - DNSServiceRef sdRef; - auto err = DNSServiceRegister(&sdRef, kRegisterFlags, interfaceId, name, type, kLocalDot, nullptr, ntohs(port), record.size(), - record.data(), OnRegister, sdCtx); - VerifyOrReturnError(kDNSServiceErr_NoError == err, sdCtx->Finalize(err)); + std::string hostnameStr = std::string(hostname) + '.' + kLocalDot; + hostname = hostnameStr.c_str(); + RegisterRecordContext * registerRecordCtx = chip::Platform::New(callback, context); + VerifyOrReturnError(nullptr != registerRecordCtx, CHIP_ERROR_NO_MEMORY); + auto err = RegisterRecord(registerRecordCtx, type, interfaceId, hostname); + VerifyOrReturnError(CHIP_NO_ERROR == err, err); - return MdnsContexts::GetInstance().Add(sdCtx, sdRef); + DNSServiceRef sdRef = nullptr; + sdCtx = chip::Platform::New(type, callback, context); + VerifyOrReturnError(nullptr != sdCtx, CHIP_ERROR_NO_MEMORY); + return RegisterService(sdRef, kRegisterFlags, interfaceId, name, type, kLocalDot, hostname, port, record.size(), record.data(), + OnRegister, sdCtx); } void OnBrowseAdd(BrowseContext * context, const char * name, const char * type, const char * domain, uint32_t interfaceId) @@ -362,13 +458,16 @@ CHIP_ERROR ChipDnssdPublishService(const DnssdService * service, DnssdPublishCal VerifyOrReturnError(service != nullptr, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(IsSupportedProtocol(service->mProtocol), CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(callback != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(service->mHostName, CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(strcmp(service->mHostName, "") != 0, CHIP_ERROR_INVALID_ARGUMENT); ScopedTXTRecord record; ReturnErrorOnFailure(record.Init(service->mTextEntries, service->mTextEntrySize)); auto regtype = GetFullTypeWithSubTypes(service); auto interfaceId = GetInterfaceId(service->mInterface); - return Register(context, callback, interfaceId, regtype.c_str(), service->mName, service->mPort, record); + + return Register(context, callback, interfaceId, regtype.c_str(), service->mName, service->mHostName, service->mPort, record); } CHIP_ERROR ChipDnssdRemoveServices() @@ -379,6 +478,11 @@ CHIP_ERROR ChipDnssdRemoveServices() return CHIP_NO_ERROR; } + err = MdnsContexts::GetInstance().RemoveAllOfType(ContextType::RegisterRecord); + if (CHIP_ERROR_KEY_NOT_FOUND == err) + { + return CHIP_NO_ERROR; + } return err; } diff --git a/src/platform/Darwin/DnssdImpl.h b/src/platform/Darwin/DnssdImpl.h index 67ee638f98c720..310007cc6de58b 100644 --- a/src/platform/Darwin/DnssdImpl.h +++ b/src/platform/Darwin/DnssdImpl.h @@ -30,6 +30,7 @@ namespace Dnssd { enum class ContextType { Register, + RegisterRecord, Browse, Resolve, }; @@ -48,6 +49,7 @@ struct GenericContext }; struct RegisterContext; +struct RegisterRecordContext; class MdnsContexts { @@ -103,6 +105,17 @@ struct RegisterContext : public GenericContext bool matches(const char * sType) { return mType.compare(sType) == 0; } }; +struct RegisterRecordContext : public GenericContext +{ + DnssdPublishCallback callback; + + RegisterRecordContext(DnssdPublishCallback cb, void * cbContext); + virtual ~RegisterRecordContext(){}; + + void DispatchFailure(DNSServiceErrorType err) override; + void DispatchSuccess() override; +}; + struct BrowseContext : public GenericContext { DnssdBrowseCallback callback;