diff --git a/src/platform/Darwin/DnssdContexts.cpp b/src/platform/Darwin/DnssdContexts.cpp index 375d0fee8bc8cf..a125064193cb87 100644 --- a/src/platform/Darwin/DnssdContexts.cpp +++ b/src/platform/Darwin/DnssdContexts.cpp @@ -264,6 +264,7 @@ void RegisterContext::DispatchFailure(DNSServiceErrorType err) { ChipLogError(Discovery, "Mdns: Register failure (%s)", Error::ToString(err)); callback(context, nullptr, nullptr, CHIP_ERROR_INTERNAL); + MdnsContexts::GetInstance().RemoveAllOfType(ContextType::RegisterRecord); MdnsContexts::GetInstance().Remove(this); } @@ -273,25 +274,27 @@ void RegisterContext::DispatchSuccess() callback(context, typeWithoutSubTypes.c_str(), mInstanceName.c_str(), CHIP_NO_ERROR); } -RegisterRecordContext::RegisterRecordContext(DnssdPublishCallback cb, void * cbContext) +RegisterRecordContext::RegisterRecordContext(DnssdPublishCallback cb, void * cbContext, uint32_t interfaceId, const char * name, + const char * sType, const char * hostname, uint16_t port, uint16_t size, + const void * data) { - type = ContextType::RegisterRecord; - context = cbContext; - callback = cb; + type = ContextType::RegisterRecord; + context = cbContext; + callback = cb; + mServiceInfo = chip::Platform::New(interfaceId, name, sType, hostname, port, size, data); } 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); + callback(context, nullptr, nullptr, Error::ToChipError(err)); + chip::Platform::Delete(this->mServiceInfo); MdnsContexts::GetInstance().Remove(this); } void RegisterRecordContext::DispatchSuccess() { - MdnsContexts::GetInstance().Remove(this); + chip::Platform::Delete(this->mServiceInfo); } BrowseContext::BrowseContext(void * cbContext, DnssdBrowseCallback cb, DnssdServiceProtocol cbContextProtocol) @@ -497,5 +500,65 @@ InterfaceInfo::~InterfaceInfo() Platform::MemoryFree(const_cast(service.mTextEntries)); } +uint32_t RegisterServiceInfo::sCount = 0; + +RegisterServiceInfo::RegisterServiceInfo() +{ + mName = nullptr; + mType = nullptr; + mHostname = nullptr; + mData = nullptr; + mInterfaceId = 0; + mPort = 0; + mSize = 0; +} + +RegisterServiceInfo::RegisterServiceInfo(uint32_t interfaceId, const char * name, const char * sType, const char * hostname, + uint16_t port, uint16_t size, const void * data) +{ + mInterfaceId = interfaceId; + size_t len = strlen(name) + 1; + mName = (char *) chip::Platform::MemoryCalloc(len, sizeof(char *)); + Platform::CopyString(mName, len, name); + len = strlen(sType) + 1; + mType = (char *) chip::Platform::MemoryCalloc(len, sizeof(char *)); + Platform::CopyString(mType, len, sType); + len = strlen(hostname) + 1; + mHostname = (char *) chip::Platform::MemoryCalloc(len, sizeof(char *)); + Platform::CopyString(mHostname, len, hostname); + mPort = port; + mSize = size; + mData = chip::Platform::MemoryCalloc(size, sizeof(void *)); + if (mData != nullptr) + { + memcpy(mData, data, size); + } + RegisterServiceInfo::sCount++; +} + +RegisterServiceInfo::~RegisterServiceInfo() +{ + if (mName) + { + Platform::MemoryFree(const_cast(mName)); + } + if (mType) + { + Platform::MemoryFree(const_cast(mType)); + } + if (mHostname) + { + Platform::MemoryFree(const_cast(mHostname)); + } + if (mData) + { + Platform::MemoryFree(mData); + } + mInterfaceId = 0; + mPort = 0; + mSize = 0; + RegisterServiceInfo::sCount--; +} + } // namespace Dnssd } // namespace chip diff --git a/src/platform/Darwin/DnssdImpl.cpp b/src/platform/Darwin/DnssdImpl.cpp index 59e19494c86438..5671e11ec519ec 100644 --- a/src/platform/Darwin/DnssdImpl.cpp +++ b/src/platform/Darwin/DnssdImpl.cpp @@ -98,6 +98,11 @@ std::string GetFullTypeWithSubTypes(const DnssdService * service) return GetFullTypeWithSubTypes(service->mType, service->mProtocol, service->mSubTypes, service->mSubTypeSize); } +std::string GetHostnameWithDomain(const char * hostname, const char * domain) +{ + return std::string(hostname) + '.' + std::string(domain); +} + void LogOnFailure(const char * name, DNSServiceErrorType err) { if (kDNSServiceErr_NoError != err) @@ -164,100 +169,131 @@ 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 Error %d", __func__, name, type, domain, flags, - err); + ChipLogDetail(Discovery, "Mdns: %s name: %s, type: %s, domain: %s, flags: %d", __func__, name, type, domain, flags); 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) +static void OnRegisterRecord(DNSServiceRef sdRef, DNSRecordRef recordRef, DNSServiceFlags flags, DNSServiceErrorType err, + void * context) { - 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); -} + RegisterRecordContext * regCtx = reinterpret_cast(context); + if (RegisterServiceInfo::sCount == 1) + { + DNSServiceRef sdRefRegister = nullptr; + auto sdCtx = chip::Platform::New(regCtx->mServiceInfo->mType, regCtx->mServiceInfo->mName, + regCtx->callback, context); + if (sdCtx) + { + ChipLogProgress(Discovery, "Registering service %s on port %u with type: %s on interface id: %u hostname %s error: %d", + regCtx->mServiceInfo->mName, regCtx->mServiceInfo->mPort, regCtx->mServiceInfo->mType, + regCtx->mServiceInfo->mInterfaceId, regCtx->mServiceInfo->mHostname, err); + + auto error = DNSServiceRegister(&sdRefRegister, kRegisterFlags, regCtx->mServiceInfo->mInterfaceId, + regCtx->mServiceInfo->mName, regCtx->mServiceInfo->mType, kLocalDot, + regCtx->mServiceInfo->mHostname, ntohs(regCtx->mServiceInfo->mPort), + regCtx->mServiceInfo->mSize, regCtx->mServiceInfo->mData, OnRegister, sdCtx); + if (error || sdRefRegister == nullptr) + { + ChipLogError(Discovery, + "Failed to register service %s on port %u with type: %s on interface id: %u hostname %s error: %d", + regCtx->mServiceInfo->mName, regCtx->mServiceInfo->mPort, regCtx->mServiceInfo->mType, + regCtx->mServiceInfo->mInterfaceId, regCtx->mServiceInfo->mHostname, err); + sdCtx->Finalize(error); + } + else + { + auto ret = MdnsContexts::GetInstance().Add(sdCtx, sdRefRegister); + if (ret != CHIP_NO_ERROR) + { + ChipLogError(Discovery, "Failed to add register service context"); + } + } + } + } + regCtx->Finalize(err); +}; -CHIP_ERROR RegisterRecord(RegisterRecordContext * registerRecordCtx, const char * type, uint32_t interfaceId, const char * hostname) +CHIP_ERROR RegisterRecord(void * context, DnssdPublishCallback callback, const char * type, uint32_t interfaceId, + const char * hostname, const char * name, uint16_t port, ScopedTXTRecord & record) { - 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)); + VerifyOrReturnError(ret == 0, CHIP_ERROR_INTERNAL); // 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) { + bool shouldRegisterRecord = + (isInterfaceIndexAny || (!isInterfaceIndexAny && (if_nametoindex(ifa->ifa_name) == interfaceId))); + if (!shouldRegisterRecord) + { + continue; + } 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) + if (!inaddr) { - 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)); + continue; } + + DNSServiceRef sdRef = nullptr; + auto err = DNSServiceCreateConnection(&sdRef); + VerifyOrReturnError((kDNSServiceErr_NoError == err || sdRef != nullptr), Error::ToChipError(err)); + + DNSRecordRef dnsRecordRef; + RegisterRecordContext * registerRecordCtx = chip::Platform::New( + callback, context, interfaceId, name, type, hostname, port, record.size(), record.data()); + VerifyOrReturnError(nullptr != registerRecordCtx, CHIP_ERROR_NO_MEMORY); + ChipLogDetail(Discovery, "Registering Record for hostname %s interface %d", hostname, if_nametoindex(ifa->ifa_name)); + 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)); + auto error = MdnsContexts::GetInstance().Add(registerRecordCtx, sdRef); + VerifyOrReturnError(CHIP_NO_ERROR == error, error); } 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) + if (!inaddr) { - 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)); + continue; } + DNSServiceRef sdRef = nullptr; + auto err = DNSServiceCreateConnection(&sdRef); + VerifyOrReturnError((kDNSServiceErr_NoError == err || sdRef != nullptr), Error::ToChipError(err)); + + DNSRecordRef dnsRecordRef; + RegisterRecordContext * registerRecordCtx = chip::Platform::New( + callback, context, interfaceId, name, type, hostname, port, record.size(), record.data()); + ChipLogDetail(Discovery, "Registering Record for hostname %s interface %d", hostname, if_nametoindex(ifa->ifa_name)); + VerifyOrReturnError(nullptr != registerRecordCtx, CHIP_ERROR_NO_MEMORY); + 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)); + auto error = MdnsContexts::GetInstance().Add(registerRecordCtx, sdRef); + VerifyOrReturnError(CHIP_NO_ERROR == error, error); } } freeifaddrs(ifaddr); @@ -278,18 +314,10 @@ CHIP_ERROR Register(void * context, DnssdPublishCallback callback, uint32_t inte return CHIP_NO_ERROR; } - 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); + hostname = GetHostnameWithDomain(hostname, kLocalDot).c_str(); + auto err = RegisterRecord(context, callback, type, interfaceId, hostname, name, port, record); VerifyOrReturnError(CHIP_NO_ERROR == err, err); - - 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); + return err; } void OnBrowseAdd(BrowseContext * context, const char * name, const char * type, const char * domain, uint32_t interfaceId) @@ -477,12 +505,14 @@ CHIP_ERROR ChipDnssdRemoveServices() { return CHIP_NO_ERROR; } + ReturnErrorOnFailure(err); err = MdnsContexts::GetInstance().RemoveAllOfType(ContextType::RegisterRecord); if (CHIP_ERROR_KEY_NOT_FOUND == err) { return CHIP_NO_ERROR; } + ReturnErrorOnFailure(err); return err; } diff --git a/src/platform/Darwin/DnssdImpl.h b/src/platform/Darwin/DnssdImpl.h index 310007cc6de58b..dfee84a10d41f9 100644 --- a/src/platform/Darwin/DnssdImpl.h +++ b/src/platform/Darwin/DnssdImpl.h @@ -49,7 +49,6 @@ struct GenericContext }; struct RegisterContext; -struct RegisterRecordContext; class MdnsContexts { @@ -105,12 +104,34 @@ struct RegisterContext : public GenericContext bool matches(const char * sType) { return mType.compare(sType) == 0; } }; +struct RegisterServiceInfo +{ + RegisterServiceInfo(); + RegisterServiceInfo(uint32_t interfaceId, const char * name, const char * sType, const char * hostname, uint16_t port, + uint16_t size, const void * data); + ~RegisterServiceInfo(); + + uint32_t mInterfaceId; + char * mName; + char * mType; + char * mHostname; + uint16_t mPort; + uint16_t mSize; + void * mData; + static uint32_t sCount; +}; + struct RegisterRecordContext : public GenericContext { + // The publish callback is only called if we fail to register a record to notify the caller that an error + // has occured while trying to register the records. Note: if we were to add a public API to register the + // records, we can have a separate callback for this. DnssdPublishCallback callback; + RegisterServiceInfo * mServiceInfo; - RegisterRecordContext(DnssdPublishCallback cb, void * cbContext); - virtual ~RegisterRecordContext(){}; + RegisterRecordContext(DnssdPublishCallback cb, void * cbContext, uint32_t interfaceId, const char * name, const char * sType, + const char * hostname, uint16_t port, uint16_t size, const void * data); + virtual ~RegisterRecordContext() {} void DispatchFailure(DNSServiceErrorType err) override; void DispatchSuccess() override; diff --git a/src/platform/Darwin/MdnsError.cpp b/src/platform/Darwin/MdnsError.cpp index c4cc4f7794b8fd..9ad52cb9840fee 100644 --- a/src/platform/Darwin/MdnsError.cpp +++ b/src/platform/Darwin/MdnsError.cpp @@ -94,6 +94,19 @@ const char * ToString(DNSServiceErrorType errorCode) } } +CHIP_ERROR ToChipError(DNSServiceErrorType errorCode) +{ + switch (errorCode) + { + case kDNSServiceErr_NoError: + return CHIP_NO_ERROR; + case kDNSServiceErr_NameConflict: + return CHIP_ERROR_MDNS_COLLISION; + default: + return CHIP_ERROR_INTERNAL; + } +} + } // namespace Error } // namespace Dnssd } // namespace chip diff --git a/src/platform/Darwin/MdnsError.h b/src/platform/Darwin/MdnsError.h index 7ae3e76a1d12fd..88d32384097f81 100644 --- a/src/platform/Darwin/MdnsError.h +++ b/src/platform/Darwin/MdnsError.h @@ -24,6 +24,7 @@ namespace Dnssd { namespace Error { const char * ToString(DNSServiceErrorType errorCode); +CHIP_ERROR ToChipError(DNSServiceErrorType errorCode); } // namespace Error } // namespace Dnssd