Skip to content

Commit

Permalink
Register the DNS service for the accessory with a valid hostname
Browse files Browse the repository at this point in the history
- Call DNSServiceRegisterRecord to register a record for the hostname for all interfaces
  before calling DNSServiceRegister.
  • Loading branch information
nivi-apple committed Sep 7, 2022
1 parent b4505ac commit 8141df6
Show file tree
Hide file tree
Showing 3 changed files with 154 additions and 17 deletions.
22 changes: 21 additions & 1 deletion src/platform/Darwin/DnssdContexts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,6 @@ CHIP_ERROR GenericContext::Finalize(DNSServiceErrorType err)
{
chip::Platform::Delete(this);
}

return (kDNSServiceErr_NoError == err) ? CHIP_NO_ERROR : CHIP_ERROR_INTERNAL;
}

Expand Down Expand Up @@ -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;
Expand Down
136 changes: 120 additions & 16 deletions src/platform/Darwin/DnssdImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "MdnsError.h"

#include <cstdio>
#include <ifaddrs.h>
#include <sstream>
#include <string.h>

Expand All @@ -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)
{
Expand Down Expand Up @@ -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<RegisterRecordContext *>(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<RegisterContext *>(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);
Expand All @@ -185,15 +278,18 @@ CHIP_ERROR Register(void * context, DnssdPublishCallback callback, uint32_t inte
return CHIP_NO_ERROR;
}

sdCtx = chip::Platform::New<RegisterContext>(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<RegisterRecordContext>(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<RegisterContext>(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)
Expand Down Expand Up @@ -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()
Expand All @@ -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;
}

Expand Down
13 changes: 13 additions & 0 deletions src/platform/Darwin/DnssdImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ namespace Dnssd {
enum class ContextType
{
Register,
RegisterRecord,
Browse,
Resolve,
};
Expand All @@ -48,6 +49,7 @@ struct GenericContext
};

struct RegisterContext;
struct RegisterRecordContext;

class MdnsContexts
{
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 8141df6

Please sign in to comment.