blob: b80e5958310e68773280d937c282fead3ba00412 [file] [log] [blame]
/*
*
* 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 <cstdio>
#include <lib/support/CHIPMemString.h>
#include <lib/support/CodeUtils.h>
#include <lib/support/SafeInt.h>
#include <lib/support/logging/CHIPLogging.h>
#include <string>
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;
constexpr DNSServiceFlags kRegisterFlags = kDNSServiceFlagsNoAutoRename;
constexpr DNSServiceFlags kBrowseFlags = kDNSServiceFlagsShareConnection;
constexpr DNSServiceFlags kGetAddrInfoFlags = kDNSServiceFlagsTimeout | kDNSServiceFlagsShareConnection;
constexpr DNSServiceFlags kResolveFlags = kDNSServiceFlagsShareConnection;
constexpr DNSServiceFlags kReconfirmRecordFlags = 0;
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
// <timeoutinMsecs>` 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<void *>(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<uint8_t>(entry.mDataSize), CHIP_ERROR_INVALID_ARGUMENT);
auto err = TXTRecordSetValue(&mRecordRef, entry.mKey, static_cast<uint8_t>(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<uint32_t> 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<uint32_t>(0);
}
} // namespace
namespace chip {
namespace Dnssd {
Global<MdnsContexts> 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<RegisterContext *>(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);
RegisterContext * sdCtx = nullptr;
if (CHIP_NO_ERROR == MdnsContexts::GetInstance().GetRegisterContextOfTypeAndName(type, name, &sdCtx))
{
auto err = DNSServiceUpdateRecord(sdCtx->serviceRef, nullptr, kRegisterFlags, record.size(), record.data(), 0 /* ttl */);
VerifyOrReturnError(kDNSServiceErr_NoError == err, sdCtx->Finalize(err));
return CHIP_NO_ERROR;
}
sdCtx = chip::Platform::New<RegisterContext>(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));
DNSServiceRef sdRef;
err = DNSServiceRegister(&sdRef, kRegisterFlags, interfaceId, name, type, kLocalDot, hostname, htons(port), record.size(),
record.data(), OnRegister, sdCtx);
VerifyOrReturnError(kDNSServiceErr_NoError == err, sdCtx->Finalize(err));
return MdnsContexts::GetInstance().Add(sdCtx, sdRef);
}
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<BrowseHandler *>(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, kBrowseFlags, 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);
VerifyOrReturnError(kDNSServiceErr_NoError == err, sdCtx->Finalize(err));
// We will browse on both the local domain and the SRP domain.
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, sdCtx->serviceRef);
}
CHIP_ERROR Browse(void * context, DnssdBrowseCallback callback, uint32_t interfaceId, const char * type,
DnssdServiceProtocol protocol, intptr_t * browseIdentifier)
{
auto sdCtx = chip::Platform::New<BrowseContext>(context, callback, protocol);
VerifyOrReturnError(nullptr != sdCtx, CHIP_ERROR_NO_MEMORY);
ReturnErrorOnFailure(Browse(sdCtx, interfaceId, type));
*browseIdentifier = reinterpret_cast<intptr_t>(sdCtx);
return CHIP_NO_ERROR;
}
CHIP_ERROR Browse(DnssdBrowseDelegate * delegate, uint32_t interfaceId, const char * type, DnssdServiceProtocol protocol)
{
auto sdCtx = chip::Platform::New<BrowseWithDelegateContext>(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<ResolveContextWithType *>(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, kGetAddrInfoFlags, 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<ResolveContextWithType *>(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, kResolveFlags, 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);
VerifyOrReturnError(kDNSServiceErr_NoError == err, 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.
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, sdCtx->serviceRef);
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<ResolveContext>(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<ResolveContext>(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<GenericContext *>(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<GenericContext *> resolves;
MdnsContexts::GetInstance().FindAllMatchingPredicate(
[ctx](GenericContext * item) {
return item->type == ContextType::Resolve && static_cast<ResolveContext *>(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<uint16_t>(sizeof(in6_addr));
rdata = &ipv6;
}
#if INET_CONFIG_ENABLE_IPV4
else if (address.IsIPv4())
{
ipv4 = address.ToIPv4();
rrtype = kDNSServiceType_A;
rdlen = static_cast<uint16_t>(sizeof(in_addr));
rdata = &ipv4;
}
#endif // INET_CONFIG_ENABLE_IPV4
else
{
return CHIP_ERROR_INVALID_ARGUMENT;
}
auto error = DNSServiceReconfirmRecord(kReconfirmRecordFlags, interfaceId, fullname.c_str(), rrtype, rrclass, rdlen, rdata);
LogOnFailure(__func__, error);
return Error::ToChipError(error);
}
} // namespace Dnssd
} // namespace chip