-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Register the DNS service for the accessory with a valid hostname (#22010
) - Call DNSServiceRegisterRecord to register a record for the hostname for all interfaces before calling DNSServiceRegister.
- Loading branch information
1 parent
f323f48
commit fcc49ee
Showing
9 changed files
with
390 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,203 @@ | ||
/* | ||
* | ||
* 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 "DnssdHostNameRegistrar.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) | ||
{ | ||
ChipLogDetail(Discovery, "Mdns: %s flags: %d", __func__, flags); | ||
|
||
auto sdCtx = reinterpret_cast<RegisterRecordContext *>(context); | ||
|
||
auto & registrar = sdCtx->mRegisterContext->mHostNameRegistrar; | ||
if (!registrar.IncrementRegistrationCount(err)) | ||
{ | ||
sdCtx->Finalize(registrar.HasRegisteredRegistrant() ? kDNSServiceErr_NoError : kDNSServiceErr_Unknown); | ||
} | ||
} | ||
|
||
} // namespace | ||
|
||
bool InterfaceRegistrant::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 InterfaceRegistrant::Register(DNSServiceRef sdRef, RegisterRecordContext * sdCtx, const char * hostname) const | ||
{ | ||
LogDetail(); | ||
|
||
uint16_t rrtype = IsIPv4() ? kDNSServiceType_A : kDNSServiceType_AAAA; | ||
uint16_t rdlen = IsIPv4() ? sizeof(in_addr) : sizeof(in6_addr); | ||
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 InterfaceRegistrant::HasValidFlags(unsigned int flags) | ||
{ | ||
return !(flags & IFF_POINTOPOINT) && (flags & IFF_RUNNING) && (flags & IFF_MULTICAST); | ||
} | ||
|
||
bool InterfaceRegistrant::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 InterfaceRegistrant::IsValidInterfaceId(uint32_t targetInterfaceId, unsigned int currentInterfaceId) | ||
{ | ||
return targetInterfaceId == kDNSServiceInterfaceIndexAny || targetInterfaceId == currentInterfaceId; | ||
} | ||
|
||
void InterfaceRegistrant::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 HostNameRegistrar::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) | ||
{ | ||
InterfaceRegistrant registrant; | ||
if (registrant.Init(ifa, addressType, interfaceId)) | ||
{ | ||
mRegistry.push_back(registrant); | ||
} | ||
} | ||
|
||
freeifaddrs(ifap); | ||
|
||
return mRegistry.size() != 0; | ||
} | ||
|
||
CHIP_ERROR HostNameRegistrar::Register(RegisterContext * registerCtx) | ||
{ | ||
// If the target interface is kDNSServiceInterfaceIndexLocalOnly, there are no interfaces to register against | ||
// the dns daemon. | ||
if (mRegistry.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 & registrant : mRegistry) | ||
{ | ||
err = registrant.Register(sdRef, sdCtx, mHostName.c_str()); | ||
|
||
// An error to register a particular registrant is not fatal unless all registrants have failed. | ||
// Otherwise, if there is an errror, it indicates that this registrant will not trigger a callback | ||
// with its registration status. So we take the registration failure into account here. | ||
if (kDNSServiceErr_NoError != err) | ||
{ | ||
VerifyOrReturnError(IncrementRegistrationCount(err), sdCtx->Finalize(err)); | ||
} | ||
} | ||
|
||
return MdnsContexts::GetInstance().Add(sdCtx, sdRef); | ||
} | ||
|
||
bool HostNameRegistrar::IncrementRegistrationCount(DNSServiceErrorType err) | ||
{ | ||
mRegistrationCount++; | ||
VerifyOrDie(mRegistrationCount <= mRegistry.size()); | ||
|
||
// A single registration success is enough to consider the whole process a success. | ||
// This is very permissive in the sense that the interface that has succeeded may not be | ||
// enough to do anything useful, but on the other hand, the failure of a single interface | ||
// to successfuly registered does not makes it obvious that it won't work. | ||
if (kDNSServiceErr_NoError == err) | ||
{ | ||
mRegistrationSuccess = true; | ||
} | ||
|
||
return mRegistrationCount != mRegistry.size(); | ||
} | ||
|
||
} // namespace Dnssd | ||
} // namespace chip |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
/* | ||
* | ||
* 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. | ||
*/ | ||
|
||
#pragma once | ||
|
||
#include <dns_sd.h> | ||
#include <lib/dnssd/platform/Dnssd.h> | ||
|
||
#include <string> | ||
#include <vector> | ||
|
||
namespace chip { | ||
namespace Dnssd { | ||
|
||
struct RegisterContext; | ||
struct RegisterRecordContext; | ||
|
||
class InterfaceRegistrant | ||
{ | ||
public: | ||
/** | ||
* Initialize an interface if it match filtering arguments. | ||
* | ||
* @param[in] ifa The interface to initialize from. | ||
* @param[in] addressType An allowed address type. | ||
* Passing in Inet::IPAddressType::kAny effectively allow all address types. | ||
* @param[in] interfaceId An allowed interface id. | ||
* Passing in kDNSServiceInterfaceIndexAny effectively allow all interface ids. | ||
* | ||
* @return A boolean indicating if the initialization from the data of the ifaddrs was allowed by the filtering parameters. | ||
*/ | ||
bool Init(const struct ifaddrs * ifa, Inet::IPAddressType addressType, uint32_t interfaceId); | ||
|
||
DNSServiceErrorType Register(DNSServiceRef sdRef, RegisterRecordContext * sdCtx, const char * hostname) const; | ||
|
||
private: | ||
static bool HasValidFlags(unsigned int flags); | ||
static bool HasValidType(Inet::IPAddressType addressType, const sa_family_t addressFamily); | ||
static bool IsValidInterfaceId(uint32_t targetInterfaceId, unsigned int currentInterfaceId); | ||
|
||
const struct in_addr * GetIPv4() const { return &mInterfaceAddress.ipv4; } | ||
const struct in6_addr * GetIPv6() const { return &mInterfaceAddress.ipv6; } | ||
|
||
bool IsIPv4() const { return mInterfaceAddressType == AF_INET; }; | ||
bool IsIPv6() const { return mInterfaceAddressType == AF_INET6; }; | ||
|
||
void LogDetail() const; | ||
|
||
union InterfaceAddress | ||
{ | ||
struct in_addr ipv4; | ||
struct in6_addr ipv6; | ||
}; | ||
|
||
uint32_t mInterfaceId; | ||
InterfaceAddress mInterfaceAddress; | ||
sa_family_t mInterfaceAddressType; | ||
}; | ||
|
||
class HostNameRegistrar | ||
{ | ||
public: | ||
bool Init(const char * hostname, Inet::IPAddressType addressType, uint32_t interfaceId); | ||
|
||
CHIP_ERROR Register(RegisterContext * registerCtx); | ||
|
||
/** | ||
* IncrementRegistrationCount is used to indicate a registrant status. | ||
* When all registrants status has been recovered, it returns false to indicate | ||
* that the registration process is finished. | ||
* | ||
* @param[in] err The registration status | ||
* | ||
* @return A boolean indicating if the registration process needs to continue. | ||
*/ | ||
bool IncrementRegistrationCount(DNSServiceErrorType err); | ||
|
||
bool HasRegisteredRegistrant() { return mRegistrationSuccess; } | ||
|
||
private: | ||
std::string mHostName; | ||
std::vector<InterfaceRegistrant> mRegistry; | ||
uint8_t mRegistrationCount = 0; | ||
bool mRegistrationSuccess = false; | ||
}; | ||
|
||
} // namespace Dnssd | ||
} // namespace chip |
Oops, something went wrong.