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 authored and vivien-apple committed Sep 9, 2022
1 parent ac30d99 commit 3a84639
Show file tree
Hide file tree
Showing 8 changed files with 345 additions and 8 deletions.
1 change: 1 addition & 0 deletions src/platform/Darwin/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ static_library("Darwin") {
"DnssdContexts.cpp",
"DnssdImpl.cpp",
"DnssdImpl.h",
"DnssdInterfaceInfos.cpp",
"InetPlatformConfig.h",
"Logging.cpp",
"MdnsError.cpp",
Expand Down
19 changes: 18 additions & 1 deletion src/platform/Darwin/DnssdContexts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ CHIP_ERROR GenericContext::Finalize(DNSServiceErrorType err)
chip::Platform::Delete(this);
}

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

MdnsContexts::~MdnsContexts()
Expand Down Expand Up @@ -274,6 +274,23 @@ void RegisterContext::DispatchSuccess()
callback(context, typeWithoutSubTypes.c_str(), mInstanceName.c_str(), CHIP_NO_ERROR);
}

RegisterRecordContext::RegisterRecordContext(RegisterContext * context)
{
type = ContextType::RegisterRecord;
mRegisterContext = context;
}

void RegisterRecordContext::DispatchFailure(DNSServiceErrorType err)
{
mRegisterContext->Finalize(err);
MdnsContexts::GetInstance().Remove(this);
}

void RegisterRecordContext::DispatchSuccess()
{
mRegisterContext->Finalize();
}

BrowseContext::BrowseContext(void * cbContext, DnssdBrowseCallback cb, DnssdServiceProtocol cbContextProtocol)
{
type = ContextType::Browse;
Expand Down
38 changes: 31 additions & 7 deletions src/platform/Darwin/DnssdImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,11 @@ std::string GetFullTypeWithSubTypes(const DnssdService * service)
return GetFullTypeWithSubTypes(service->mType, service->mProtocol, service->mSubTypes, service->mSubTypeSize);
}

std::string GetHostNameWithDomain(const char * hostname)
{
return std::string(hostname) + '.' + kLocalDot;
}

void LogOnFailure(const char * name, DNSServiceErrorType err)
{
if (kDNSServiceErr_NoError != err)
Expand Down Expand Up @@ -168,14 +173,18 @@ static void OnRegister(DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErr
ChipLogDetail(Discovery, "Mdns: %s name: %s, type: %s, domain: %s, flags: %d", __func__, name, type, domain, flags);

auto sdCtx = reinterpret_cast<RegisterContext *>(context);
sdCtx->Finalize(err);
VerifyOrReturn(kDNSServiceErr_NoError == err, sdCtx->Finalize(err));

// Once a service has been properly published it is normally unreachable because the hostname has not been yet
// registered against the dns daemon. Register the records mapping the hostname to our IP.
VerifyOrReturn(CHIP_NO_ERROR == sdCtx->mInterfacesInfos.RegisterRecords(sdCtx), sdCtx->Finalize(kDNSServiceErr_Unknown));
};

CHIP_ERROR Register(void * context, DnssdPublishCallback callback, uint32_t interfaceId, const char * type, const char * name,
uint16_t port, ScopedTXTRecord & record)
uint16_t port, ScopedTXTRecord & record, Inet::IPAddressType addressType, const char * hostname)
{
ChipLogProgress(Discovery, "Publishing service %s on port %u with type: %s on interface id: %" PRIu32, name, port, type,
interfaceId);
ChipLogDetail(Discovery, "Registering service %s on host %s with port %u and type: %s on interface id: %" PRIu32, name,
hostname, port, type, interfaceId);

RegisterContext * sdCtx = nullptr;
if (CHIP_NO_ERROR == MdnsContexts::GetInstance().GetRegisterContextOfType(type, &sdCtx))
Expand All @@ -188,8 +197,11 @@ CHIP_ERROR Register(void * context, DnssdPublishCallback callback, uint32_t inte
sdCtx = chip::Platform::New<RegisterContext>(type, name, callback, context);
VerifyOrReturnError(nullptr != sdCtx, CHIP_ERROR_NO_MEMORY);

VerifyOrReturnError(sdCtx->mInterfacesInfos.Init(hostname, addressType, interfaceId),
sdCtx->Finalize(kDNSServiceErr_BadInterfaceIndex));

DNSServiceRef sdRef;
auto err = DNSServiceRegister(&sdRef, kRegisterFlags, interfaceId, name, type, kLocalDot, nullptr, ntohs(port), record.size(),
auto err = DNSServiceRegister(&sdRef, kRegisterFlags, interfaceId, name, type, kLocalDot, hostname, ntohs(port), record.size(),
record.data(), OnRegister, sdCtx);
VerifyOrReturnError(kDNSServiceErr_NoError == err, sdCtx->Finalize(err));

Expand Down Expand Up @@ -362,22 +374,34 @@ 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(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);
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()
{
auto err = MdnsContexts::GetInstance().RemoveAllOfType(ContextType::Register);
if (CHIP_ERROR_KEY_NOT_FOUND == err)
{
return CHIP_NO_ERROR;
err = CHIP_NO_ERROR;
}
ReturnErrorOnFailure(err);

err = MdnsContexts::GetInstance().RemoveAllOfType(ContextType::RegisterRecord);
if (CHIP_ERROR_KEY_NOT_FOUND == err)
{
err = CHIP_NO_ERROR;
}
ReturnErrorOnFailure(err);

return err;
}
Expand Down
15 changes: 15 additions & 0 deletions src/platform/Darwin/DnssdImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
#include <dns_sd.h>
#include <lib/dnssd/platform/Dnssd.h>

#include "DnssdInterfaceInfos.h"

#include <map>
#include <string>
#include <vector>
Expand All @@ -30,6 +32,7 @@ namespace Dnssd {
enum class ContextType
{
Register,
RegisterRecord,
Browse,
Resolve,
};
Expand Down Expand Up @@ -93,6 +96,7 @@ struct RegisterContext : public GenericContext
DnssdPublishCallback callback;
std::string mType;
std::string mInstanceName;
InterfacesInfos mInterfacesInfos;

RegisterContext(const char * sType, const char * instanceName, DnssdPublishCallback cb, void * cbContext);
virtual ~RegisterContext() {}
Expand All @@ -103,6 +107,17 @@ struct RegisterContext : public GenericContext
bool matches(const char * sType) { return mType.compare(sType) == 0; }
};

struct RegisterRecordContext : public GenericContext
{
RegisterContext * mRegisterContext;

RegisterRecordContext(RegisterContext * context);
virtual ~RegisterRecordContext(){};

void DispatchFailure(DNSServiceErrorType err) override;
void DispatchSuccess() override;
};

struct BrowseContext : public GenericContext
{
DnssdBrowseCallback callback;
Expand Down
188 changes: 188 additions & 0 deletions src/platform/Darwin/DnssdInterfaceInfos.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
/*
*
* Copyright (c) 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 "DnssdInterfaceInfos.h"
#include "DnssdImpl.h"

#include <arpa/inet.h>
#include <ifaddrs.h>
#include <net/ethernet.h>
#include <net/if_dl.h>
#include <netdb.h>

constexpr DNSServiceFlags kRegisterRecordFlags = kDNSServiceFlagsShared;

namespace chip {
namespace Dnssd {

namespace {

static void OnRegisterRecord(DNSServiceRef sdRef, DNSRecordRef recordRef, DNSServiceFlags flags, DNSServiceErrorType err,
void * context)
{
ChipLogError(Discovery, "Mdns: %s flags: %d", __func__, flags);

auto sdCtx = reinterpret_cast<RegisterRecordContext *>(context);
VerifyOrReturn(kDNSServiceErr_NoError == err, sdCtx->Finalize(err));

if (sdCtx->mRegisterContext->mInterfacesInfos.IncrementRegistrationCount())
{
sdCtx->Finalize();
}
}

} // namespace

bool InterfaceInfos::Init(const struct ifaddrs * ifa, Inet::IPAddressType addressType, uint32_t interfaceId)
{
VerifyOrReturnValue(ifa != nullptr, false);
VerifyOrReturnValue(ifa->ifa_addr != nullptr, false);
VerifyOrReturnValue(HasValidFlags(ifa->ifa_flags), false);
VerifyOrReturnValue(IsValidInterfaceId(interfaceId, if_nametoindex(ifa->ifa_name)), false);
VerifyOrReturnValue(HasValidType(addressType, ifa->ifa_addr->sa_family), false);

// The incoming interface id can be kDNSServiceInterfaceIndexAny, but here what needs to be done is to
// associate a given ip with a specific interface id, so the incoming interface id is used as a hint
// to validate if the current interface is of interest or not, but it is not what is stored internally.
mInterfaceId = if_nametoindex(ifa->ifa_name);
mInterfaceAddressType = ifa->ifa_addr->sa_family;

if (IsIPv4())
{
mInterfaceAddress.ipv4 = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_addr)->sin_addr;
}
else
{
mInterfaceAddress.ipv6 = reinterpret_cast<struct sockaddr_in6 *>(ifa->ifa_addr)->sin6_addr;
}

return true;
}

DNSServiceErrorType InterfaceInfos::Register(DNSServiceRef sdRef, RegisterRecordContext * sdCtx, const char * hostname) const
{
LogDetail();

uint16_t rrtype = IsIPv4() ? kDNSServiceType_A : kDNSServiceType_AAAA;
uint16_t rdlen = IsIPv4() ? 4 : 16;
const void * rdata = IsIPv4() ? static_cast<const void *>(GetIPv4()) : static_cast<const void *>(GetIPv6());

DNSRecordRef dnsRecordRef;
return DNSServiceRegisterRecord(sdRef, &dnsRecordRef, kRegisterRecordFlags, mInterfaceId, hostname, rrtype, kDNSServiceClass_IN,
rdlen, rdata, 0, OnRegisterRecord, sdCtx);
}

bool InterfaceInfos::HasValidFlags(unsigned int flags)
{
return !(flags & IFF_POINTOPOINT) && (flags & IFF_RUNNING) && (flags & IFF_MULTICAST);
}

bool InterfaceInfos::HasValidType(Inet::IPAddressType addressType, const sa_family_t addressFamily)
{
bool useIPv4 = addressType == Inet::IPAddressType::kIPv4 || addressType == Inet::IPAddressType::kAny;
bool useIPv6 = addressType == Inet::IPAddressType::kIPv6 || addressType == Inet::IPAddressType::kAny;

return (useIPv6 && addressFamily == AF_INET6) || (useIPv4 && addressFamily == AF_INET);
}

bool InterfaceInfos::IsValidInterfaceId(uint32_t targetInterfaceId, unsigned int currentInterfaceId)
{
return targetInterfaceId == kDNSServiceInterfaceIndexAny || targetInterfaceId == currentInterfaceId;
}

void InterfaceInfos::LogDetail() const
{
char interfaceName[IFNAMSIZ] = {};
VerifyOrReturn(if_indextoname(mInterfaceId, interfaceName) != nullptr);

if (IsIPv4())
{
char addr[INET_ADDRSTRLEN] = {};
inet_ntop(AF_INET, GetIPv4(), addr, sizeof(addr));
ChipLogDetail(Discovery, "\tInterface: %s (%u) ipv4: %s", interfaceName, mInterfaceId, addr);
}
else
{
char addr[INET6_ADDRSTRLEN] = {};
inet_ntop(AF_INET6, GetIPv6(), addr, sizeof(addr));
ChipLogDetail(Discovery, "\tInterface: %s (%u) ipv6: %s", interfaceName, mInterfaceId, addr);
}
}

bool InterfacesInfos::Init(const char * hostname, Inet::IPAddressType addressType, uint32_t interfaceId)
{
mHostName = hostname;

// When the target interface is kDNSServiceInterfaceIndexLocalOnly, there is no need to map the hostname to
// an IP address.
VerifyOrReturnValue(interfaceId != kDNSServiceInterfaceIndexLocalOnly, true);

struct ifaddrs * ifap;
VerifyOrReturnValue(getifaddrs(&ifap) >= 0, false);

for (struct ifaddrs * ifa = ifap; ifa; ifa = ifa->ifa_next)
{
InterfaceInfos interfaceInfos;
if (interfaceInfos.Init(ifa, addressType, interfaceId))
{
mInterfaceInfos.push_back(interfaceInfos);
}
}

freeifaddrs(ifap);

return mInterfaceInfos.size();
}

CHIP_ERROR InterfacesInfos::RegisterRecords(RegisterContext * registerCtx)
{
// If the target interface is kDNSServiceInterfaceIndexLocalOnly, there is no interfaces to register against
// the dns daemon.
if (mInterfaceInfos.size() == 0)
{
return registerCtx->Finalize();
}

DNSServiceRef sdRef;
auto err = DNSServiceCreateConnection(&sdRef);
VerifyOrReturnError(kDNSServiceErr_NoError == err, CHIP_ERROR_INTERNAL);

RegisterRecordContext * sdCtx = Platform::New<RegisterRecordContext>(registerCtx);
VerifyOrReturnError(nullptr != sdCtx, CHIP_ERROR_NO_MEMORY);

ChipLogDetail(Discovery, "Mdns: Mapping %s to:", mHostName.c_str());

for (auto & interfaceInfos : mInterfaceInfos)
{
err = interfaceInfos.Register(sdRef, sdCtx, mHostName.c_str());
VerifyOrReturnError(kDNSServiceErr_NoError == err, sdCtx->Finalize(err));
}

return MdnsContexts::GetInstance().Add(sdCtx, sdRef);
}

bool InterfacesInfos::IncrementRegistrationCount()
{
mInterfaceRegistrationCount++;

VerifyOrDie(mInterfaceRegistrationCount <= mInterfaceInfos.size());

return mInterfaceRegistrationCount == mInterfaceInfos.size();
}

} // namespace Dnssd
} // namespace chip
Loading

0 comments on commit 3a84639

Please sign in to comment.