/* * * Copyright (c) 2021-2022 Project CHIP Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "DnssdImpl.h" #include "DnssdType.h" #include "MdnsError.h" #include "UserDefaults.h" #include #include #include #include #include #include using namespace chip::Dnssd; using namespace chip::Dnssd::Internal; using namespace chip::Platform; namespace { constexpr char kLocalDot[] = "local."; constexpr char kSRPDot[] = "default.service.arpa."; // The extra time in milliseconds that we will wait for the resolution on the SRP domain to complete. constexpr uint16_t kSRPTimeoutInMsec = 250; bool IsSupportedProtocol(DnssdServiceProtocol protocol) { return (protocol == DnssdServiceProtocol::kDnssdProtocolUdp) || (protocol == DnssdServiceProtocol::kDnssdProtocolTcp); } uint32_t GetInterfaceId(chip::Inet::InterfaceId interfaceId) { return interfaceId.IsPresent() ? interfaceId.GetPlatformInterface() : kDNSServiceInterfaceIndexAny; } std::string GetHostNameWithDomain(const char * hostname) { return std::string(hostname) + '.' + kLocalDot; } void LogOnFailure(const char * name, DNSServiceErrorType err) { if (kDNSServiceErr_NoError != err) { ChipLogError(Discovery, "%s (%s)", StringOrNullMarker(name), Error::ToString(err)); } } /** * @brief Starts a timer to wait for the resolution on the kSRPDot domain to happen. * * @param[in] timeoutSeconds The timeout in seconds. * @param[in] ResolveContext The resolve context. */ CHIP_ERROR StartSRPTimer(uint16_t timeoutInMSecs, ResolveContext * ctx) { // Check to see if a user default value exists for the SRP timeout. If it does, override the timeoutInMSecs with user default // value. To override the timeout value, use ` defaults write org.csa-iot.matter.darwin SRPTimeoutInMSecsOverride // ` See UserDefaults.mm for details. timeoutInMSecs = GetUserDefaultDnssdSRPTimeoutInMSecs().value_or(timeoutInMSecs); VerifyOrReturnValue(ctx != nullptr, CHIP_ERROR_INCORRECT_STATE); ChipLogProgress(Discovery, "Starting timer to wait for %d milliseconds for possible SRP resolve results for %s", timeoutInMSecs, ctx->instanceName.c_str()); return chip::DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Milliseconds16(timeoutInMSecs), ResolveContext::SRPTimerExpiredCallback, static_cast(ctx)); } class ScopedTXTRecord { public: ScopedTXTRecord() {} ~ScopedTXTRecord() { if (mDataSize != 0) { TXTRecordDeallocate(&mRecordRef); } } CHIP_ERROR Init(TextEntry * textEntries, size_t textEntrySize) { VerifyOrReturnError(textEntrySize <= kDnssdTextMaxSize, CHIP_ERROR_INVALID_ARGUMENT); TXTRecordCreate(&mRecordRef, sizeof(mRecordBuffer), mRecordBuffer); for (size_t i = 0; i < textEntrySize; i++) { TextEntry entry = textEntries[i]; VerifyOrReturnError(chip::CanCastTo(entry.mDataSize), CHIP_ERROR_INVALID_ARGUMENT); auto err = TXTRecordSetValue(&mRecordRef, entry.mKey, static_cast(entry.mDataSize), entry.mData); VerifyOrReturnError(err == kDNSServiceErr_NoError, CHIP_ERROR_INVALID_ARGUMENT); } mDataSize = TXTRecordGetLength(&mRecordRef); if (mDataSize == 0) { TXTRecordDeallocate(&mRecordRef); } mData = TXTRecordGetBytesPtr(&mRecordRef); return CHIP_NO_ERROR; } uint16_t size() { return mDataSize; } const void * data() { return mData; } private: uint16_t mDataSize = 0; const void * mData = nullptr; TXTRecordRef mRecordRef; char mRecordBuffer[kDnssdTextMaxSize]; }; std::shared_ptr GetCounterHolder(const char * name) { // This is a little silly, in that resolves for the same name, type, etc get // coalesced by the underlying mDNSResponder anyway. But we need to keep // track of our context/callback/etc, (even though in practice it's always // exactly the same) and the interface id (which might actually be different // for different Resolve calls). So for now just keep using a // ResolveContext to track all that. if (auto existingCtx = MdnsContexts::GetInstance().GetExistingResolveForInstanceName(name)) { return existingCtx->consumerCounter; } return std::make_shared(0); } } // namespace namespace chip { namespace Dnssd { Global MdnsContexts::sInstance; namespace { static void OnRegister(DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType err, const char * name, const char * type, const char * domain, void * context) { ChipLogProgress(Discovery, "Mdns: %s name: %s, type: %s, domain: %s, flags: %d", __func__, StringOrNullMarker(name), StringOrNullMarker(type), StringOrNullMarker(domain), flags); auto sdCtx = reinterpret_cast(context); sdCtx->Finalize(err); }; CHIP_ERROR Register(void * context, DnssdPublishCallback callback, uint32_t interfaceId, const char * type, const char * name, uint16_t port, ScopedTXTRecord & record, Inet::IPAddressType addressType, const char * hostname) { ChipLogProgress(Discovery, "Registering service %s on host %s with port %u and type: %s on interface id: %" PRIu32, StringOrNullMarker(name), StringOrNullMarker(hostname), port, StringOrNullMarker(type), interfaceId); constexpr DNSServiceFlags registerFlags = kDNSServiceFlagsNoAutoRename; RegisterContext * sdCtx = nullptr; if (CHIP_NO_ERROR == MdnsContexts::GetInstance().GetRegisterContextOfTypeAndName(type, name, &sdCtx)) { auto err = DNSServiceUpdateRecord(sdCtx->serviceRef, nullptr, registerFlags, record.size(), record.data(), 0 /* ttl */); VerifyOrReturnError(kDNSServiceErr_NoError == err, sdCtx->Finalize(err)); return CHIP_NO_ERROR; } sdCtx = chip::Platform::New(type, name, callback, context); VerifyOrReturnError(nullptr != sdCtx, CHIP_ERROR_NO_MEMORY); auto err = sdCtx->mHostNameRegistrar.Init(hostname, addressType, interfaceId); VerifyOrReturnError(kDNSServiceErr_NoError == err, sdCtx->Finalize(err)); err = DNSServiceRegister(&sdCtx->serviceRef, registerFlags, interfaceId, name, type, kLocalDot, hostname, htons(port), record.size(), record.data(), OnRegister, sdCtx); if (err != kDNSServiceErr_NoError) { // Just in case DNSServiceCreateConnection put garbage in the outparam // on failure. sdCtx->serviceRef = nullptr; return sdCtx->Finalize(err); } return MdnsContexts::GetInstance().Add(sdCtx); } static void OnBrowse(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceId, DNSServiceErrorType err, const char * name, const char * type, const char * domain, void * context) { auto sdCtx = reinterpret_cast(context); VerifyOrReturn(kDNSServiceErr_NoError == err, sdCtx->Finalize(err)); sdCtx->OnBrowse(flags, name, type, domain, interfaceId); } CHIP_ERROR BrowseOnDomain(BrowseHandler * sdCtx, uint32_t interfaceId, const char * type, const char * domain) { auto sdRef = sdCtx->serviceRef; // Mandatory copy because of kDNSServiceFlagsShareConnection auto err = DNSServiceBrowse(&sdRef, kDNSServiceFlagsShareConnection, interfaceId, type, domain, OnBrowse, sdCtx); VerifyOrReturnError(kDNSServiceErr_NoError == err, sdCtx->Finalize(err)); return CHIP_NO_ERROR; } CHIP_ERROR Browse(BrowseHandler * sdCtx, uint32_t interfaceId, const char * type) { auto err = DNSServiceCreateConnection(&sdCtx->serviceRef); if (err != kDNSServiceErr_NoError) { // Just in case DNSServiceCreateConnection put garbage in the outparam // on failure. sdCtx->serviceRef = nullptr; return sdCtx->Finalize(err); } // We will browse on both the local domain and the SRP domain. // BrowseOnDomain guarantees it will Finalize() on failure. ChipLogProgress(Discovery, "Browsing for: %s on local domain", StringOrNullMarker(type)); ReturnErrorOnFailure(BrowseOnDomain(sdCtx, interfaceId, type, kLocalDot)); ChipLogProgress(Discovery, "Browsing for: %s on %s domain", StringOrNullMarker(type), kSRPDot); ReturnErrorOnFailure(BrowseOnDomain(sdCtx, interfaceId, type, kSRPDot)); return MdnsContexts::GetInstance().Add(sdCtx); } CHIP_ERROR Browse(void * context, DnssdBrowseCallback callback, uint32_t interfaceId, const char * type, DnssdServiceProtocol protocol, intptr_t * browseIdentifier) { auto sdCtx = chip::Platform::New(context, callback, protocol); VerifyOrReturnError(nullptr != sdCtx, CHIP_ERROR_NO_MEMORY); ReturnErrorOnFailure(Browse(sdCtx, interfaceId, type)); *browseIdentifier = reinterpret_cast(sdCtx); return CHIP_NO_ERROR; } CHIP_ERROR Browse(DnssdBrowseDelegate * delegate, uint32_t interfaceId, const char * type, DnssdServiceProtocol protocol) { auto sdCtx = chip::Platform::New(delegate, protocol); VerifyOrReturnError(nullptr != sdCtx, CHIP_ERROR_NO_MEMORY); return Browse(sdCtx, interfaceId, type); } static void OnGetAddrInfo(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceId, DNSServiceErrorType err, const char * hostname, const struct sockaddr * address, uint32_t ttl, void * context) { ChipLogProgress(Discovery, "Mdns: %s flags: %d, interface: %u, hostname: %s", __func__, flags, (unsigned) interfaceId, StringOrNullMarker(hostname)); auto contextWithType = reinterpret_cast(context); VerifyOrReturn(contextWithType != nullptr, ChipLogError(Discovery, "ResolveContextWithType is null")); auto sdCtx = contextWithType->context; ReturnOnFailure(MdnsContexts::GetInstance().Has(sdCtx)); LogOnFailure(__func__, err); if (kDNSServiceErr_NoError == err) { InterfaceKey interfaceKey = { interfaceId, hostname, contextWithType->isSRPResolve }; CHIP_ERROR error = sdCtx->OnNewAddress(interfaceKey, address); // If we saw an address resolved on the SRP domain, we don't need to wait // for SRP results, so don't bother with starting a timer to wait for those. if (error == CHIP_NO_ERROR && contextWithType->isSRPResolve) { sdCtx->shouldStartSRPTimerForResolve = false; } } if (flags & kDNSServiceFlagsMoreComing) { // If we now don't need to have a timer while we wait for SRP results, ensure that there is no such // timer running. Otherwise the timer could fire before we get the rest of the results that flags // say are coming, and trigger a finalize before we have all the data that is already available. if (!sdCtx->shouldStartSRPTimerForResolve) { sdCtx->CancelSRPTimerIfRunning(); } return; } VerifyOrReturn(sdCtx->HasAddress(), sdCtx->Finalize(kDNSServiceErr_BadState)); // If either we didn't start a resolve on the SRP domain or we started a resolve on the SRP domain and got an address, // we are done. Otherwise start the timer to give the resolve on SRP domain some extra time to complete. if (!sdCtx->shouldStartSRPTimerForResolve) { ChipLogDetail(Discovery, "No need to start SRP resolve timer for %s; completing resolve", sdCtx->instanceName.c_str()); sdCtx->Finalize(); } else { if (!sdCtx->isSRPTimerRunning) { CHIP_ERROR error = StartSRPTimer(kSRPTimeoutInMsec, sdCtx); if (error != CHIP_NO_ERROR) { // If we failed to start the timer, just go ahead and report whatever information // we have gotten so far. sdCtx->Finalize(); return; } sdCtx->isSRPTimerRunning = true; } } } static void GetAddrInfo(ResolveContext * sdCtx) { auto protocol = sdCtx->protocol; for (auto & interface : sdCtx->interfaces) { if (interface.second.isDNSLookUpRequested) { continue; } auto interfaceId = interface.first.interfaceId; auto hostname = interface.second.fullyQualifiedDomainName.c_str(); auto sdRefCopy = sdCtx->serviceRef; // Mandatory copy because of kDNSServiceFlagsShareConnection ResolveContextWithType * contextWithType = (interface.first.isSRPResult) ? &sdCtx->resolveContextWithSRPType : &sdCtx->resolveContextWithNonSRPType; auto err = DNSServiceGetAddrInfo(&sdRefCopy, kDNSServiceFlagsShareConnection, interfaceId, protocol, hostname, OnGetAddrInfo, contextWithType); VerifyOrReturn(kDNSServiceErr_NoError == err, sdCtx->Finalize(err)); interface.second.isDNSLookUpRequested = true; } } static void OnResolve(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceId, DNSServiceErrorType err, const char * fullname, const char * hostname, uint16_t port, uint16_t txtLen, const unsigned char * txtRecord, void * context) { ChipLogProgress(Discovery, "Mdns: %s flags: %d, interface: %u, fullname: %s, hostname: %s, port: %u", __func__, flags, (unsigned) interfaceId, StringOrNullMarker(fullname), StringOrNullMarker(hostname), ntohs(port)); auto contextWithType = reinterpret_cast(context); VerifyOrReturn(contextWithType != nullptr, ChipLogError(Discovery, "ResolveContextWithType is null")); auto sdCtx = contextWithType->context; ReturnOnFailure(MdnsContexts::GetInstance().Has(sdCtx)); LogOnFailure(__func__, err); if (kDNSServiceErr_NoError == err) { sdCtx->OnNewInterface(interfaceId, fullname, hostname, port, txtLen, txtRecord, contextWithType->isSRPResolve); } if (!(flags & kDNSServiceFlagsMoreComing)) { VerifyOrReturn(sdCtx->HasInterface(), sdCtx->Finalize(kDNSServiceErr_BadState)); GetAddrInfo(sdCtx); } } static CHIP_ERROR ResolveWithContext(ResolveContext * sdCtx, uint32_t interfaceId, const char * type, const char * name, const char * domain, ResolveContextWithType * contextWithType) { auto sdRef = sdCtx->serviceRef; // Mandatory copy because of kDNSServiceFlagsShareConnection auto err = DNSServiceResolve(&sdRef, kDNSServiceFlagsShareConnection, interfaceId, name, type, domain, OnResolve, contextWithType); VerifyOrReturnError(kDNSServiceErr_NoError == err, sdCtx->Finalize(err)); return CHIP_NO_ERROR; } static CHIP_ERROR Resolve(ResolveContext * sdCtx, uint32_t interfaceId, chip::Inet::IPAddressType addressType, const char * type, const char * name, const char * domain) { ChipLogProgress(Discovery, "Resolve type=%s name=%s domain=%s interface=%" PRIu32, StringOrNullMarker(type), StringOrNullMarker(name), StringOrNullMarker(domain), interfaceId); auto err = DNSServiceCreateConnection(&sdCtx->serviceRef); if (err != kDNSServiceErr_NoError) { // Just in case DNSServiceCreateConnection put garbage in the outparam // on failure. sdCtx->serviceRef = nullptr; return sdCtx->Finalize(err); } // If we have a single domain from a browse, we will use that for the Resolve. // Otherwise we will try to resolve using both the local domain and the SRP domain. // ResolveWithContext guarantees it will Finalize() on failure. if (domain != nullptr) { ReturnErrorOnFailure(ResolveWithContext(sdCtx, interfaceId, type, name, domain, &sdCtx->resolveContextWithNonSRPType)); sdCtx->shouldStartSRPTimerForResolve = false; } else { ReturnErrorOnFailure(ResolveWithContext(sdCtx, interfaceId, type, name, kLocalDot, &sdCtx->resolveContextWithNonSRPType)); ReturnErrorOnFailure(ResolveWithContext(sdCtx, interfaceId, type, name, kSRPDot, &sdCtx->resolveContextWithNonSRPType)); // Set the flag to start the timer for resolve on SRP domain to complete since a resolve has been requested on the SRP // domain. sdCtx->shouldStartSRPTimerForResolve = true; } auto retval = MdnsContexts::GetInstance().Add(sdCtx); if (retval == CHIP_NO_ERROR) { (*(sdCtx->consumerCounter))++; } return retval; } static CHIP_ERROR Resolve(void * context, DnssdResolveCallback callback, uint32_t interfaceId, chip::Inet::IPAddressType addressType, const char * type, const char * name, const char * domain) { auto counterHolder = GetCounterHolder(name); auto sdCtx = chip::Platform::New(context, callback, addressType, name, BrowseContext::sContextDispatchingSuccess, std::move(counterHolder)); VerifyOrReturnError(nullptr != sdCtx, CHIP_ERROR_NO_MEMORY); return Resolve(sdCtx, interfaceId, addressType, type, name, domain); } static CHIP_ERROR Resolve(DiscoverNodeDelegate * delegate, uint32_t interfaceId, chip::Inet::IPAddressType addressType, const char * type, const char * name) { auto counterHolder = GetCounterHolder(name); auto sdCtx = chip::Platform::New(delegate, addressType, name, std::move(counterHolder)); VerifyOrReturnError(nullptr != sdCtx, CHIP_ERROR_NO_MEMORY); return Resolve(sdCtx, interfaceId, addressType, type, name, nullptr); } } // namespace CHIP_ERROR ChipDnssdInit(DnssdAsyncReturnCallback successCallback, DnssdAsyncReturnCallback errorCallback, void * context) { VerifyOrReturnError(successCallback != nullptr, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(errorCallback != nullptr, CHIP_ERROR_INVALID_ARGUMENT); successCallback(context, CHIP_NO_ERROR); return CHIP_NO_ERROR; } void ChipDnssdShutdown() { // Drop our existing advertisements now, so they don't stick around while we // are not actually in a responsive state. ChipDnssdRemoveServices(); } CHIP_ERROR ChipDnssdPublishService(const DnssdService * service, DnssdPublishCallback callback, void * context) { VerifyOrReturnError(service != nullptr, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(IsSupportedProtocol(service->mProtocol), CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(callback != nullptr, 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); auto hostname = GetHostNameWithDomain(service->mHostName); return Register(context, callback, interfaceId, regtype.c_str(), service->mName, service->mPort, record, service->mAddressType, hostname.c_str()); } CHIP_ERROR ChipDnssdRemoveServices() { assertChipStackLockedByCurrentThread(); auto err = MdnsContexts::GetInstance().RemoveAllOfType(ContextType::Register); if (CHIP_ERROR_KEY_NOT_FOUND == err) { err = CHIP_NO_ERROR; } return err; } CHIP_ERROR ChipDnssdFinalizeServiceUpdate() { return CHIP_NO_ERROR; } CHIP_ERROR ChipDnssdBrowse(const char * type, DnssdServiceProtocol protocol, chip::Inet::IPAddressType addressType, chip::Inet::InterfaceId interface, DnssdBrowseCallback callback, void * context, intptr_t * browseIdentifier) { VerifyOrReturnError(type != nullptr, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(callback != nullptr, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(IsSupportedProtocol(protocol), CHIP_ERROR_INVALID_ARGUMENT); auto regtype = GetFullTypeWithSubTypes(type, protocol); auto interfaceId = GetInterfaceId(interface); return Browse(context, callback, interfaceId, regtype.c_str(), protocol, browseIdentifier); } CHIP_ERROR ChipDnssdStopBrowse(intptr_t browseIdentifier) { auto ctx = reinterpret_cast(browseIdentifier); if (MdnsContexts::GetInstance().Has(ctx) != CHIP_NO_ERROR) { return CHIP_ERROR_NOT_FOUND; } // We know this is an actual context now, so can check the type. if (ctx->type != ContextType::Browse) { // stale pointer that got reallocated. return CHIP_ERROR_NOT_FOUND; } // We have been canceled. Don't bother delivering the partial // results we have queued up in the BrowseContext, if any. In practice // there shouldn't be anything there long-term anyway. // // Make sure to cancel all the resolves first, before we cancel the // browse (just to avoid dangling pointers in the resolves, even though we // only use them for equality compares). std::vector resolves; MdnsContexts::GetInstance().FindAllMatchingPredicate( [ctx](GenericContext * item) { return item->type == ContextType::Resolve && static_cast(item)->browseThatCausedResolve == ctx; }, resolves); for (auto & resolve : resolves) { resolve->Finalize(CHIP_ERROR_CANCELLED); } ctx->Finalize(CHIP_ERROR_CANCELLED); return CHIP_NO_ERROR; } CHIP_ERROR ChipDnssdBrowse(const char * type, DnssdServiceProtocol protocol, chip::Inet::IPAddressType addressType, chip::Inet::InterfaceId interface, DnssdBrowseDelegate * delegate) { VerifyOrReturnError(type != nullptr, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(IsSupportedProtocol(protocol), CHIP_ERROR_INVALID_ARGUMENT); auto regtype = GetFullTypeWithSubTypes(type, protocol); auto interfaceId = GetInterfaceId(interface); return Browse(delegate, interfaceId, regtype.c_str(), protocol); } CHIP_ERROR ChipDnssdStopBrowse(DnssdBrowseDelegate * delegate) { auto existingCtx = MdnsContexts::GetInstance().GetExistingBrowseForDelegate(delegate); if (existingCtx == nullptr) { return CHIP_ERROR_NOT_FOUND; } return existingCtx->Finalize(kDNSServiceErr_NoError); } CHIP_ERROR ChipDnssdResolve(DnssdService * service, chip::Inet::InterfaceId interface, DnssdResolveCallback callback, void * context) { VerifyOrReturnError(service != nullptr, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(IsSupportedProtocol(service->mProtocol), CHIP_ERROR_INVALID_ARGUMENT); auto regtype = GetFullType(service); auto interfaceId = GetInterfaceId(interface); const char * domain = nullptr; if (BrowseContext::sContextDispatchingSuccess != nullptr) { for (size_t i = 0; i < BrowseContext::sDispatchedServices->size(); ++i) { if (service == &BrowseContext::sDispatchedServices->at(i)) { domain = BrowseContext::sContextDispatchingSuccess->services[i].second.c_str(); break; } } } return Resolve(context, callback, interfaceId, service->mAddressType, regtype.c_str(), service->mName, domain); } CHIP_ERROR ChipDnssdResolve(DnssdService * service, chip::Inet::InterfaceId interface, DiscoverNodeDelegate * delegate) { VerifyOrReturnError(service != nullptr, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(IsSupportedProtocol(service->mProtocol), CHIP_ERROR_INVALID_ARGUMENT); auto regtype = GetFullType(service); auto interfaceId = GetInterfaceId(interface); return Resolve(delegate, interfaceId, service->mAddressType, regtype.c_str(), service->mName); } void ChipDnssdResolveNoLongerNeeded(const char * instanceName) { ChipLogProgress(Discovery, "No longer need resolve for %s", instanceName); auto existingCtx = MdnsContexts::GetInstance().GetExistingResolveForInstanceName(instanceName); VerifyOrReturn(existingCtx != nullptr); VerifyOrReturn(*existingCtx->consumerCounter != 0); (*existingCtx->consumerCounter)--; if (*existingCtx->consumerCounter == 0) { // No more consumers; clear out all of these resolves so they don't // stick around. Dispatch a "cancelled" failure on all of them to make // sure whatever kicked them off cleans up resources as needed. do { existingCtx->Finalize(CHIP_ERROR_CANCELLED); existingCtx = MdnsContexts::GetInstance().GetExistingResolveForInstanceName(instanceName); } while (existingCtx != nullptr); } } CHIP_ERROR ChipDnssdReconfirmRecord(const char * hostname, chip::Inet::IPAddress address, chip::Inet::InterfaceId interface) { VerifyOrReturnError(hostname != nullptr, CHIP_ERROR_INVALID_ARGUMENT); auto interfaceId = interface.GetPlatformInterface(); auto rrclass = kDNSServiceClass_IN; auto fullname = GetHostNameWithDomain(hostname); uint16_t rrtype; uint16_t rdlen; const void * rdata; in6_addr ipv6; #if INET_CONFIG_ENABLE_IPV4 in_addr ipv4; #endif // INET_CONFIG_ENABLE_IPV4 if (address.IsIPv6()) { ipv6 = address.ToIPv6(); rrtype = kDNSServiceType_AAAA; rdlen = static_cast(sizeof(in6_addr)); rdata = &ipv6; } #if INET_CONFIG_ENABLE_IPV4 else if (address.IsIPv4()) { ipv4 = address.ToIPv4(); rrtype = kDNSServiceType_A; rdlen = static_cast(sizeof(in_addr)); rdata = &ipv4; } #endif // INET_CONFIG_ENABLE_IPV4 else { return CHIP_ERROR_INVALID_ARGUMENT; } auto error = DNSServiceReconfirmRecord(0 /* DNSServiceFlags */, interfaceId, fullname.c_str(), rrtype, rrclass, rdlen, rdata); LogOnFailure(__func__, error); return Error::ToChipError(error); } } // namespace Dnssd } // namespace chip