blob: 6f98ac71b34a3811d36a5214390411c823beaac1 [file] [log] [blame]
/*
*
* Copyright (c) 2020-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 <cstdint>
#include <lib/dnssd/Discovery_ImplPlatform.h>
#include <inttypes.h>
#include <app/icd/server/ICDServerConfig.h>
#include <crypto/RandUtils.h>
#include <lib/core/CHIPConfig.h>
#include <lib/core/CHIPSafeCasts.h>
#include <lib/dnssd/IPAddressSorter.h>
#include <lib/dnssd/ServiceNaming.h>
#include <lib/dnssd/TxtFields.h>
#include <lib/dnssd/platform/Dnssd.h>
#include <lib/support/CHIPMemString.h>
#include <lib/support/CodeUtils.h>
#include <lib/support/logging/CHIPLogging.h>
#include <platform/CHIPDeviceConfig.h>
#include <platform/CHIPDeviceLayer.h>
namespace chip {
namespace Dnssd {
namespace {
static void HandleNodeResolve(void * context, DnssdService * result, const Span<Inet::IPAddress> & addresses, CHIP_ERROR error)
{
DiscoveryContext * discoveryContext = static_cast<DiscoveryContext *>(context);
if (error != CHIP_NO_ERROR && error != CHIP_ERROR_IN_PROGRESS)
{
discoveryContext->Release();
return;
}
DiscoveredNodeData nodeData;
result->ToDiscoveredCommissionNodeData(addresses, nodeData);
nodeData.Get<CommissionNodeData>().LogDetail();
discoveryContext->OnNodeDiscovered(nodeData);
// CHIP_ERROR_IN_PROGRESS indicates that more results are coming, so don't release
// the context yet.
if (error == CHIP_NO_ERROR)
{
discoveryContext->Release();
}
}
static void HandleNodeOperationalBrowse(void * context, DnssdService * result, CHIP_ERROR error)
{
DiscoveryContext * discoveryContext = static_cast<DiscoveryContext *>(context);
if (error != CHIP_NO_ERROR)
{
discoveryContext->Release();
return;
}
DiscoveredNodeData nodeData;
result->ToDiscoveredOperationalNodeBrowseData(nodeData);
nodeData.Get<OperationalNodeBrowseData>().LogDetail();
discoveryContext->OnNodeDiscovered(nodeData);
discoveryContext->Release();
}
static void HandleNodeBrowse(void * context, DnssdService * services, size_t servicesSize, bool finalBrowse, CHIP_ERROR error)
{
DiscoveryContext * discoveryContext = static_cast<DiscoveryContext *>(context);
if (error != CHIP_NO_ERROR)
{
discoveryContext->ClearBrowseIdentifier();
discoveryContext->Release();
return;
}
for (size_t i = 0; i < servicesSize; ++i)
{
discoveryContext->Retain();
// For some platforms browsed services are already resolved, so verify if resolve is really needed or call resolve callback
auto & ipAddress = services[i].mAddress;
// mType(service name) exactly matches with operational service name
bool isOperationalBrowse = strcmp(services[i].mType, kOperationalServiceName) == 0;
// For operational browse result we currently don't need IP address hence skip resolution and handle differently.
if (isOperationalBrowse)
{
HandleNodeOperationalBrowse(context, &services[i], error);
}
// check whether SRV, TXT and AAAA records were received in DNS responses
else if (strlen(services[i].mHostName) == 0 || services[i].mTextEntrySize == 0 || !ipAddress.has_value())
{
ChipDnssdResolve(&services[i], services[i].mInterface, HandleNodeResolve, context);
}
else
{
Inet::IPAddress * address = &(*ipAddress);
HandleNodeResolve(context, &services[i], Span<Inet::IPAddress>(address, 1), error);
}
}
if (finalBrowse)
{
discoveryContext->ClearBrowseIdentifier();
discoveryContext->Release();
}
}
CHIP_ERROR AddPtrRecord(DiscoveryFilter filter, const char ** entries, size_t & entriesCount, char * buffer, size_t bufferLen)
{
ReturnErrorOnFailure(MakeServiceSubtype(buffer, bufferLen, filter));
entries[entriesCount++] = buffer;
return CHIP_NO_ERROR;
}
CHIP_ERROR AddPtrRecord(DiscoveryFilterType type, const char ** entries, size_t & entriesCount, char * buffer, size_t bufferLen,
CommissioningMode value)
{
VerifyOrReturnError(value != CommissioningMode::kDisabled, CHIP_NO_ERROR);
return AddPtrRecord(DiscoveryFilter(type), entries, entriesCount, buffer, bufferLen);
}
CHIP_ERROR AddPtrRecord(DiscoveryFilterType type, const char ** entries, size_t & entriesCount, char * buffer, size_t bufferLen,
uint64_t value)
{
return AddPtrRecord(DiscoveryFilter(type, value), entries, entriesCount, buffer, bufferLen);
}
template <class T>
CHIP_ERROR AddPtrRecord(DiscoveryFilterType type, const char ** entries, size_t & entriesCount, char * buffer, size_t bufferLen,
const std::optional<T> & value)
{
VerifyOrReturnError(value.has_value(), CHIP_NO_ERROR);
return AddPtrRecord(type, entries, entriesCount, buffer, bufferLen, *value);
}
CHIP_ERROR ENFORCE_FORMAT(4, 5)
CopyTextRecordValue(char * buffer, size_t bufferLen, int minCharactersWritten, const char * format, ...)
{
va_list args;
va_start(args, format);
int charactersWritten = vsnprintf(buffer, bufferLen, format, args);
va_end(args);
return charactersWritten >= minCharactersWritten ? CHIP_NO_ERROR : CHIP_ERROR_INVALID_STRING_LENGTH;
}
CHIP_ERROR CopyTextRecordValue(char * buffer, size_t bufferLen, bool value)
{
return CopyTextRecordValue(buffer, bufferLen, 1, "%d", value);
}
CHIP_ERROR CopyTextRecordValue(char * buffer, size_t bufferLen, uint16_t value)
{
return CopyTextRecordValue(buffer, bufferLen, 1, "%u", value);
}
CHIP_ERROR CopyTextRecordValue(char * buffer, size_t bufferLen, uint32_t value)
{
return CopyTextRecordValue(buffer, bufferLen, 1, "%" PRIu32, value);
}
CHIP_ERROR CopyTextRecordValue(char * buffer, size_t bufferLen, uint16_t value1, uint16_t value2)
{
return CopyTextRecordValue(buffer, bufferLen, 3, "%u+%u", value1, value2);
}
CHIP_ERROR CopyTextRecordValue(char * buffer, size_t bufferLen, const char * value)
{
return CopyTextRecordValue(buffer, bufferLen, 0, "%s", value);
}
CHIP_ERROR CopyTextRecordValue(char * buffer, size_t bufferLen, CommissioningMode value)
{
return CopyTextRecordValue(buffer, bufferLen, static_cast<uint16_t>(value));
}
template <class T>
CHIP_ERROR CopyTextRecordValue(char * buffer, size_t bufferLen, std::optional<T> value)
{
VerifyOrReturnError(value.has_value(), CHIP_ERROR_UNINITIALIZED);
return CopyTextRecordValue(buffer, bufferLen, *value);
}
CHIP_ERROR CopyTextRecordValue(char * buffer, size_t bufferLen, std::optional<uint16_t> value1, std::optional<uint16_t> value2)
{
VerifyOrReturnError(value1.has_value(), CHIP_ERROR_UNINITIALIZED);
return value2.has_value() ? CopyTextRecordValue(buffer, bufferLen, *value1, *value2)
: CopyTextRecordValue(buffer, bufferLen, *value1);
}
CHIP_ERROR CopyTextRecordValue(char * buffer, size_t bufferLen, const std::optional<ReliableMessageProtocolConfig> & optional,
TxtFieldKey key)
{
VerifyOrReturnError((key == TxtFieldKey::kSessionIdleInterval || key == TxtFieldKey::kSessionActiveInterval ||
key == TxtFieldKey::kSessionActiveThreshold),
CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(optional.has_value(), CHIP_ERROR_UNINITIALIZED);
CHIP_ERROR err;
if (key == TxtFieldKey::kSessionActiveThreshold)
{
err = CopyTextRecordValue(buffer, bufferLen, optional->mActiveThresholdTime.count());
}
else
{
bool isIdle = (key == TxtFieldKey::kSessionIdleInterval);
auto retryInterval = isIdle ? optional->mIdleRetransTimeout : optional->mActiveRetransTimeout;
if (retryInterval > kMaxRetryInterval)
{
ChipLogProgress(Discovery, "MRP retry interval %s value exceeds allowed range of 1 hour, using maximum available",
isIdle ? "idle" : "active");
retryInterval = kMaxRetryInterval;
}
err = CopyTextRecordValue(buffer, bufferLen, retryInterval.count());
}
return err;
}
template <class T>
CHIP_ERROR CopyTxtRecord(TxtFieldKey key, char * buffer, size_t bufferLen, const T & params)
{
switch (key)
{
case TxtFieldKey::kTcpSupported:
VerifyOrReturnError(params.GetTCPSupportModes() != TCPModeAdvertise::kNone, CHIP_ERROR_UNINITIALIZED);
return CopyTextRecordValue(buffer, bufferLen, to_underlying(params.GetTCPSupportModes()));
case TxtFieldKey::kSessionIdleInterval:
#if CHIP_CONFIG_ENABLE_ICD_SERVER
// A ICD operating as a LIT should not advertise its slow polling interval
// Returning UNINITIALIZED ensures that the SII string isn't added by the AddTxtRecord
// without erroring out the action.
VerifyOrReturnError(params.GetICDModeToAdvertise() != ICDModeAdvertise::kLIT, CHIP_ERROR_UNINITIALIZED);
FALLTHROUGH;
#endif
case TxtFieldKey::kSessionActiveInterval:
case TxtFieldKey::kSessionActiveThreshold:
return CopyTextRecordValue(buffer, bufferLen, params.GetLocalMRPConfig(), key);
case TxtFieldKey::kLongIdleTimeICD:
// The ICD key is only added to the advertissment when the device supports the ICD LIT feature-set.
// Return UNINITIALIZED when the operating mode is kNone to ensure that the ICD string isn't added
// by the AddTxtRecord without erroring out the action.
VerifyOrReturnError(params.GetICDModeToAdvertise() != ICDModeAdvertise::kNone, CHIP_ERROR_UNINITIALIZED);
return CopyTextRecordValue(buffer, bufferLen, (params.GetICDModeToAdvertise() == ICDModeAdvertise::kLIT));
default:
return CHIP_ERROR_INVALID_ARGUMENT;
}
}
CHIP_ERROR CopyTxtRecord(TxtFieldKey key, char * buffer, size_t bufferLen, const CommissionAdvertisingParameters & params)
{
switch (key)
{
case TxtFieldKey::kVendorProduct:
return CopyTextRecordValue(buffer, bufferLen, params.GetVendorId(), params.GetProductId());
case TxtFieldKey::kDeviceType:
return CopyTextRecordValue(buffer, bufferLen, params.GetDeviceType());
case TxtFieldKey::kDeviceName:
return CopyTextRecordValue(buffer, bufferLen, params.GetDeviceName());
case TxtFieldKey::kLongDiscriminator:
return CopyTextRecordValue(buffer, bufferLen, params.GetLongDiscriminator());
case TxtFieldKey::kRotatingDeviceId:
return CopyTextRecordValue(buffer, bufferLen, params.GetRotatingDeviceId());
case TxtFieldKey::kPairingInstruction:
return CopyTextRecordValue(buffer, bufferLen, params.GetPairingInstruction());
case TxtFieldKey::kPairingHint:
return CopyTextRecordValue(buffer, bufferLen, params.GetPairingHint());
case TxtFieldKey::kCommissioningMode:
return CopyTextRecordValue(buffer, bufferLen, params.GetCommissioningMode());
case TxtFieldKey::kCommissionerPasscode:
return CopyTextRecordValue(buffer, bufferLen,
static_cast<uint16_t>(params.GetCommissionerPasscodeSupported().value_or(false) ? 1 : 0));
default:
return CopyTxtRecord(key, buffer, bufferLen, static_cast<BaseAdvertisingParams<CommissionAdvertisingParameters>>(params));
}
}
template <class T>
CHIP_ERROR AddTxtRecord(TxtFieldKey key, TextEntry * entries, size_t & entriesCount, char * buffer, size_t bufferLen,
const T & params)
{
CHIP_ERROR error = CopyTxtRecord(key, buffer, bufferLen, params);
VerifyOrReturnError(CHIP_ERROR_UNINITIALIZED != error, CHIP_NO_ERROR);
VerifyOrReturnError(CHIP_NO_ERROR == error, error);
entries[entriesCount++] = { Internal::txtFieldInfo[static_cast<int>(key)].keyStr, reinterpret_cast<const uint8_t *>(buffer),
strnlen(buffer, bufferLen) };
return CHIP_NO_ERROR;
}
} // namespace
void DiscoveryImplPlatform::HandleNodeIdResolve(void * context, DnssdService * result, const Span<Inet::IPAddress> & addresses,
CHIP_ERROR error)
{
DiscoveryImplPlatform * impl = static_cast<DiscoveryImplPlatform *>(context);
if (impl->mOperationalDelegate == nullptr)
{
ChipLogError(Discovery, "No delegate to handle node resolution data.");
return;
}
if (CHIP_NO_ERROR != error)
{
impl->mOperationalDelegate->OnOperationalNodeResolutionFailed(PeerId(), error);
return;
}
if (result == nullptr)
{
impl->mOperationalDelegate->OnOperationalNodeResolutionFailed(PeerId(), CHIP_ERROR_UNKNOWN_RESOURCE_ID);
return;
}
PeerId peerId;
error = ExtractIdFromInstanceName(result->mName, &peerId);
if (CHIP_NO_ERROR != error)
{
impl->mOperationalDelegate->OnOperationalNodeResolutionFailed(PeerId(), error);
return;
}
ResolvedNodeData nodeData;
Platform::CopyString(nodeData.resolutionData.hostName, result->mHostName);
nodeData.resolutionData.interfaceId = result->mInterface;
nodeData.resolutionData.port = result->mPort;
nodeData.operationalData.peerId = peerId;
size_t addressesFound = 0;
for (auto & ip : addresses)
{
if (addressesFound == ArraySize(nodeData.resolutionData.ipAddress))
{
// Out of space.
ChipLogProgress(Discovery, "Can't add more IPs to ResolvedNodeData");
break;
}
nodeData.resolutionData.ipAddress[addressesFound] = ip;
++addressesFound;
}
nodeData.resolutionData.numIPs = addressesFound;
for (size_t i = 0; i < result->mTextEntrySize; ++i)
{
ByteSpan key(reinterpret_cast<const uint8_t *>(result->mTextEntries[i].mKey), strlen(result->mTextEntries[i].mKey));
ByteSpan val(result->mTextEntries[i].mData, result->mTextEntries[i].mDataSize);
FillNodeDataFromTxt(key, val, nodeData.resolutionData);
}
nodeData.LogNodeIdResolved();
impl->mOperationalDelegate->OnOperationalNodeResolved(nodeData);
}
void DnssdService::ToDiscoveredOperationalNodeBrowseData(DiscoveredNodeData & nodeData)
{
nodeData.Set<OperationalNodeBrowseData>();
ExtractIdFromInstanceName(mName, &nodeData.Get<OperationalNodeBrowseData>().peerId);
nodeData.Get<OperationalNodeBrowseData>().hasZeroTTL = (mTtlSeconds == 0);
}
void DnssdService::ToDiscoveredCommissionNodeData(const Span<Inet::IPAddress> & addresses, DiscoveredNodeData & nodeData)
{
nodeData.Set<CommissionNodeData>();
auto & discoveredData = nodeData.Get<CommissionNodeData>();
Platform::CopyString(discoveredData.hostName, mHostName);
Platform::CopyString(discoveredData.instanceName, mName);
IPAddressSorter::Sort(addresses, mInterface);
size_t addressesFound = 0;
for (auto & ip : addresses)
{
if (addressesFound == ArraySize(discoveredData.ipAddress))
{
// Out of space.
ChipLogProgress(Discovery, "Can't add more IPs to DiscoveredNodeData");
break;
}
discoveredData.ipAddress[addressesFound] = ip;
++addressesFound;
}
discoveredData.interfaceId = mInterface;
discoveredData.numIPs = addressesFound;
discoveredData.port = mPort;
for (size_t i = 0; i < mTextEntrySize; ++i)
{
ByteSpan key(reinterpret_cast<const uint8_t *>(mTextEntries[i].mKey), strlen(mTextEntries[i].mKey));
ByteSpan val(mTextEntries[i].mData, mTextEntries[i].mDataSize);
FillNodeDataFromTxt(key, val, discoveredData);
}
}
Global<DiscoveryImplPlatform> DiscoveryImplPlatform::sManager;
CHIP_ERROR DiscoveryImplPlatform::InitImpl()
{
VerifyOrReturnError(mState == State::kUninitialized, CHIP_NO_ERROR);
mState = State::kInitializing;
CHIP_ERROR err = ChipDnssdInit(HandleDnssdInit, HandleDnssdError, this);
if (err != CHIP_NO_ERROR)
{
mState = State::kUninitialized;
return err;
}
UpdateCommissionableInstanceName();
return CHIP_NO_ERROR;
}
void DiscoveryImplPlatform::Shutdown()
{
VerifyOrReturn(mState != State::kUninitialized);
ChipDnssdShutdown();
mState = State::kUninitialized;
}
void DiscoveryImplPlatform::HandleDnssdInit(void * context, CHIP_ERROR initError)
{
DiscoveryImplPlatform * publisher = static_cast<DiscoveryImplPlatform *>(context);
if (initError == CHIP_NO_ERROR)
{
publisher->mState = State::kInitialized;
// Post an event that will start advertising
DeviceLayer::ChipDeviceEvent event;
event.Type = DeviceLayer::DeviceEventType::kDnssdInitialized;
CHIP_ERROR error = DeviceLayer::PlatformMgr().PostEvent(&event);
if (error != CHIP_NO_ERROR)
{
ChipLogError(Discovery, "Posting DNS-SD platform initialized event failed with %" CHIP_ERROR_FORMAT, error.Format());
}
}
else
{
ChipLogError(Discovery, "DNS-SD initialization failed with %" CHIP_ERROR_FORMAT, initError.Format());
publisher->mState = State::kUninitialized;
}
}
void DiscoveryImplPlatform::HandleDnssdError(void * context, CHIP_ERROR error)
{
DiscoveryImplPlatform * publisher = static_cast<DiscoveryImplPlatform *>(context);
if (error == CHIP_ERROR_FORCED_RESET)
{
// Restore dnssd state before restart, also needs to call ChipDnssdShutdown()
publisher->Shutdown();
DeviceLayer::ChipDeviceEvent event;
event.Type = DeviceLayer::DeviceEventType::kDnssdRestartNeeded;
error = DeviceLayer::PlatformMgr().PostEvent(&event);
if (error != CHIP_NO_ERROR)
{
ChipLogError(Discovery, "Failed to post DNS-SD restart event: %" CHIP_ERROR_FORMAT, error.Format());
}
}
else
{
ChipLogError(Discovery, "DNS-SD error: %" CHIP_ERROR_FORMAT, error.Format());
}
}
CHIP_ERROR DiscoveryImplPlatform::GetCommissionableInstanceName(char * instanceName, size_t maxLength) const
{
if (maxLength < (chip::Dnssd::Commission::kInstanceNameMaxLength + 1))
{
return CHIP_ERROR_NO_MEMORY;
}
return chip::Encoding::BytesToUppercaseHexString(&mCommissionableInstanceName[0], sizeof(mCommissionableInstanceName),
instanceName, maxLength);
}
CHIP_ERROR DiscoveryImplPlatform::UpdateCommissionableInstanceName()
{
uint64_t random_instance_name = chip::Crypto::GetRandU64();
static_assert(sizeof(mCommissionableInstanceName) == sizeof(random_instance_name), "Not copying the right amount of data");
memcpy(&mCommissionableInstanceName[0], &random_instance_name, sizeof(mCommissionableInstanceName));
return CHIP_NO_ERROR;
}
void DiscoveryImplPlatform::HandleDnssdPublish(void * context, const char * type, const char * instanceName, CHIP_ERROR error)
{
if (CHIP_NO_ERROR == error)
{
ChipLogProgress(Discovery, "mDNS service published: %s; instance name: %s", StringOrNullMarker(type),
StringOrNullMarker(instanceName));
}
else
{
ChipLogError(Discovery, "mDNS service published error: %" CHIP_ERROR_FORMAT, error.Format());
}
}
CHIP_ERROR DiscoveryImplPlatform::PublishService(const char * serviceType, TextEntry * textEntries, size_t textEntrySize,
const char ** subTypes, size_t subTypeSize,
const OperationalAdvertisingParameters & params)
{
return PublishService(serviceType, textEntries, textEntrySize, subTypes, subTypeSize, params.GetPort(), params.GetInterfaceId(),
params.GetMac(), DnssdServiceProtocol::kDnssdProtocolTcp, params.GetPeerId());
}
CHIP_ERROR DiscoveryImplPlatform::PublishService(const char * serviceType, TextEntry * textEntries, size_t textEntrySize,
const char ** subTypes, size_t subTypeSize,
const CommissionAdvertisingParameters & params)
{
return PublishService(serviceType, textEntries, textEntrySize, subTypes, subTypeSize, params.GetPort(), params.GetInterfaceId(),
params.GetMac(), DnssdServiceProtocol::kDnssdProtocolUdp, PeerId());
}
CHIP_ERROR DiscoveryImplPlatform::PublishService(const char * serviceType, TextEntry * textEntries, size_t textEntrySize,
const char ** subTypes, size_t subTypeSize, uint16_t port,
Inet::InterfaceId interfaceId, const chip::ByteSpan & mac,
DnssdServiceProtocol protocol, PeerId peerId)
{
DnssdService service;
ReturnErrorOnFailure(MakeHostName(service.mHostName, sizeof(service.mHostName), mac));
ReturnErrorOnFailure(protocol == DnssdServiceProtocol::kDnssdProtocolTcp
? MakeInstanceName(service.mName, sizeof(service.mName), peerId)
: GetCommissionableInstanceName(service.mName, sizeof(service.mName)));
Platform::CopyString(service.mType, serviceType);
#if INET_CONFIG_ENABLE_IPV4
service.mAddressType = Inet::IPAddressType::kAny;
#else
service.mAddressType = Inet::IPAddressType::kIPv6;
#endif
service.mInterface = interfaceId;
service.mProtocol = protocol;
service.mPort = port;
service.mTextEntries = textEntries;
service.mTextEntrySize = textEntrySize;
service.mSubTypes = subTypes;
service.mSubTypeSize = subTypeSize;
ReturnErrorOnFailure(ChipDnssdPublishService(&service, HandleDnssdPublish, this));
#ifdef DETAIL_LOGGING
printf("printEntries port=%u, mTextEntrySize=%u, mSubTypeSize=%u\n", port, static_cast<unsigned int>(textEntrySize),
static_cast<unsigned int>(subTypeSize));
for (size_t i = 0; i < textEntrySize; i++)
{
printf(" entry [%u] : %s %s\n", static_cast<unsigned int>(i), StringOrNullMarker(textEntries[i].mKey),
StringOrNullMarker((char *) (textEntries[i].mData)));
}
for (size_t i = 0; i < subTypeSize; i++)
{
printf(" type [%u] : %s\n", static_cast<unsigned int>(i), StringOrNullMarker(subTypes[i]));
}
#endif
return CHIP_NO_ERROR;
}
#define PREPARE_RECORDS(Type) \
VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE); \
TextEntry textEntries[Type##AdvertisingParameters::kTxtMaxNumber]; \
size_t textEntrySize = 0; \
const char * subTypes[Type::kSubTypeMaxNumber]; \
size_t subTypeSize = 0;
#define ADD_TXT_RECORD(Name) \
char Name##Buf[kKey##Name##MaxLength + 1]; \
ReturnErrorOnFailure(AddTxtRecord(TxtFieldKey::k##Name, textEntries, textEntrySize, Name##Buf, sizeof(Name##Buf), params));
#define ADD_PTR_RECORD(Name) \
char Name##SubTypeBuf[kSubType##Name##MaxLength + 1]; \
ReturnErrorOnFailure(AddPtrRecord(DiscoveryFilterType::k##Name, subTypes, subTypeSize, Name##SubTypeBuf, \
sizeof(Name##SubTypeBuf), params.Get##Name()));
#define PUBLISH_RECORDS(Type) \
ReturnErrorOnFailure(PublishService(k##Type##ServiceName, textEntries, textEntrySize, subTypes, subTypeSize, params));
CHIP_ERROR DiscoveryImplPlatform::Advertise(const OperationalAdvertisingParameters & params)
{
PREPARE_RECORDS(Operational);
ADD_TXT_RECORD(SessionIdleInterval);
ADD_TXT_RECORD(SessionActiveInterval);
ADD_TXT_RECORD(SessionActiveThreshold);
ADD_TXT_RECORD(TcpSupported);
ADD_TXT_RECORD(LongIdleTimeICD); // Optional, will not be added if related 'params' doesn't have a value
ADD_PTR_RECORD(CompressedFabricId);
PUBLISH_RECORDS(Operational);
return CHIP_NO_ERROR;
}
CHIP_ERROR DiscoveryImplPlatform::Advertise(const CommissionAdvertisingParameters & params)
{
PREPARE_RECORDS(Commission);
ADD_TXT_RECORD(VendorProduct);
ADD_TXT_RECORD(DeviceType);
ADD_TXT_RECORD(DeviceName);
ADD_TXT_RECORD(SessionIdleInterval);
ADD_TXT_RECORD(SessionActiveInterval);
ADD_TXT_RECORD(SessionActiveThreshold);
ADD_TXT_RECORD(TcpSupported);
ADD_TXT_RECORD(LongIdleTimeICD); // Optional, will not be added if related 'params' doesn't have a value
ADD_PTR_RECORD(VendorId);
ADD_PTR_RECORD(DeviceType);
if (params.GetCommissionAdvertiseMode() == CommssionAdvertiseMode::kCommissioner)
{
ADD_TXT_RECORD(CommissionerPasscode);
PUBLISH_RECORDS(Commissioner);
return CHIP_NO_ERROR;
}
ADD_TXT_RECORD(LongDiscriminator);
ADD_TXT_RECORD(CommissioningMode);
ADD_TXT_RECORD(RotatingDeviceId);
ADD_TXT_RECORD(PairingHint);
ADD_TXT_RECORD(PairingInstruction);
ADD_PTR_RECORD(ShortDiscriminator);
ADD_PTR_RECORD(LongDiscriminator);
ADD_PTR_RECORD(CommissioningMode);
PUBLISH_RECORDS(Commissionable);
return CHIP_NO_ERROR;
}
CHIP_ERROR DiscoveryImplPlatform::RemoveServices()
{
VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
ReturnErrorOnFailure(ChipDnssdRemoveServices());
return CHIP_NO_ERROR;
}
CHIP_ERROR DiscoveryImplPlatform::FinalizeServiceUpdate()
{
VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
return ChipDnssdFinalizeServiceUpdate();
}
bool DiscoveryImplPlatform::IsInitialized()
{
return mState == State::kInitialized;
}
CHIP_ERROR DiscoveryImplPlatform::ResolveNodeId(const PeerId & peerId)
{
// Resolve requests can only be issued once DNSSD is initialized and there is
// no caching currently
VerifyOrReturnError(mState == State::kInitialized, CHIP_ERROR_INCORRECT_STATE);
ChipLogProgress(Discovery, "Resolving " ChipLogFormatX64 ":" ChipLogFormatX64 " ...",
ChipLogValueX64(peerId.GetCompressedFabricId()), ChipLogValueX64(peerId.GetNodeId()));
DnssdService service;
ReturnErrorOnFailure(MakeInstanceName(service.mName, sizeof(service.mName), peerId));
Platform::CopyString(service.mType, kOperationalServiceName);
service.mProtocol = DnssdServiceProtocol::kDnssdProtocolTcp;
service.mAddressType = Inet::IPAddressType::kAny;
return ChipDnssdResolve(&service, Inet::InterfaceId::Null(), HandleNodeIdResolve, this);
}
void DiscoveryImplPlatform::NodeIdResolutionNoLongerNeeded(const PeerId & peerId)
{
char name[Common::kInstanceNameMaxLength + 1];
ReturnOnFailure(MakeInstanceName(name, sizeof(name), peerId));
ChipDnssdResolveNoLongerNeeded(name);
}
CHIP_ERROR DiscoveryImplPlatform::DiscoverCommissionableNodes(DiscoveryFilter filter, DiscoveryContext & context)
{
ReturnErrorOnFailure(InitImpl());
StopDiscovery(context);
if (filter.type == DiscoveryFilterType::kInstanceName)
{
// When we have the instance name, no need to browse, only need to resolve.
DnssdService service;
ReturnErrorOnFailure(MakeServiceSubtype(service.mName, sizeof(service.mName), filter));
Platform::CopyString(service.mType, kCommissionableServiceName);
service.mProtocol = DnssdServiceProtocol::kDnssdProtocolUdp;
service.mAddressType = Inet::IPAddressType::kAny;
// Increase the reference count of the context to keep it alive until HandleNodeResolve is called back.
CHIP_ERROR error = ChipDnssdResolve(&service, Inet::InterfaceId::Null(), HandleNodeResolve, context.Retain());
if (error != CHIP_NO_ERROR)
{
context.Release();
}
return error;
}
char serviceName[kMaxCommissionableServiceNameSize];
ReturnErrorOnFailure(MakeServiceTypeName(serviceName, sizeof(serviceName), filter, DiscoveryType::kCommissionableNode));
intptr_t browseIdentifier;
// Increase the reference count of the context to keep it alive until HandleNodeBrowse is called back.
CHIP_ERROR error = ChipDnssdBrowse(serviceName, DnssdServiceProtocol::kDnssdProtocolUdp, Inet::IPAddressType::kAny,
Inet::InterfaceId::Null(), HandleNodeBrowse, context.Retain(), &browseIdentifier);
if (error == CHIP_NO_ERROR)
{
context.SetBrowseIdentifier(browseIdentifier);
}
else
{
context.Release();
}
return error;
}
CHIP_ERROR DiscoveryImplPlatform::DiscoverCommissioners(DiscoveryFilter filter, DiscoveryContext & context)
{
ReturnErrorOnFailure(InitImpl());
StopDiscovery(context);
if (filter.type == DiscoveryFilterType::kInstanceName)
{
// When we have the instance name, no need to browse, only need to resolve.
DnssdService service;
ReturnErrorOnFailure(MakeServiceSubtype(service.mName, sizeof(service.mName), filter));
Platform::CopyString(service.mType, kCommissionerServiceName);
service.mProtocol = DnssdServiceProtocol::kDnssdProtocolUdp;
service.mAddressType = Inet::IPAddressType::kAny;
// Increase the reference count of the context to keep it alive until HandleNodeResolve is called back.
CHIP_ERROR error = ChipDnssdResolve(&service, Inet::InterfaceId::Null(), HandleNodeResolve, context.Retain());
if (error != CHIP_NO_ERROR)
{
context.Release();
}
}
char serviceName[kMaxCommissionerServiceNameSize];
ReturnErrorOnFailure(MakeServiceTypeName(serviceName, sizeof(serviceName), filter, DiscoveryType::kCommissionerNode));
intptr_t browseIdentifier;
// Increase the reference count of the context to keep it alive until HandleNodeBrowse is called back.
CHIP_ERROR error = ChipDnssdBrowse(serviceName, DnssdServiceProtocol::kDnssdProtocolUdp, Inet::IPAddressType::kAny,
Inet::InterfaceId::Null(), HandleNodeBrowse, context.Retain(), &browseIdentifier);
if (error == CHIP_NO_ERROR)
{
context.SetBrowseIdentifier(browseIdentifier);
}
else
{
context.Release();
}
return error;
}
CHIP_ERROR DiscoveryImplPlatform::DiscoverOperational(DiscoveryFilter filter, DiscoveryContext & context)
{
ReturnErrorOnFailure(InitImpl());
StopDiscovery(context);
char serviceName[kMaxOperationalServiceNameSize];
ReturnErrorOnFailure(MakeServiceTypeName(serviceName, sizeof(serviceName), filter, DiscoveryType::kOperational));
intptr_t browseIdentifier;
// Increase the reference count of the context to keep it alive until HandleNodeBrowse is called back.
CHIP_ERROR error = ChipDnssdBrowse(serviceName, DnssdServiceProtocol::kDnssdProtocolTcp, Inet::IPAddressType::kAny,
Inet::InterfaceId::Null(), HandleNodeBrowse, context.Retain(), &browseIdentifier);
if (error == CHIP_NO_ERROR)
{
context.SetBrowseIdentifier(browseIdentifier);
}
else
{
context.Release();
}
return error;
}
CHIP_ERROR DiscoveryImplPlatform::StartDiscovery(DiscoveryType type, DiscoveryFilter filter, DiscoveryContext & context)
{
switch (type)
{
case DiscoveryType::kCommissionableNode:
return DiscoverCommissionableNodes(filter, context);
case DiscoveryType::kCommissionerNode:
return DiscoverCommissioners(filter, context);
case DiscoveryType::kOperational:
return DiscoverOperational(filter, context);
default:
return CHIP_ERROR_INVALID_ARGUMENT;
}
}
CHIP_ERROR DiscoveryImplPlatform::StopDiscovery(DiscoveryContext & context)
{
const std::optional<intptr_t> browseIdentifier = context.GetBrowseIdentifier();
if (!browseIdentifier.has_value())
{
// No discovery going on.
return CHIP_NO_ERROR;
}
context.ClearBrowseIdentifier();
return ChipDnssdStopBrowse(*browseIdentifier);
}
CHIP_ERROR DiscoveryImplPlatform::ReconfirmRecord(const char * hostname, Inet::IPAddress address, Inet::InterfaceId interfaceId)
{
ReturnErrorOnFailure(InitImpl());
return ChipDnssdReconfirmRecord(hostname, address, interfaceId);
}
DiscoveryImplPlatform & DiscoveryImplPlatform::GetInstance()
{
return sManager.get();
}
#if CHIP_DNSSD_DEFAULT_PLATFORM
ServiceAdvertiser & GetDefaultAdvertiser()
{
return DiscoveryImplPlatform::GetInstance();
}
Resolver & GetDefaultResolver()
{
return DiscoveryImplPlatform::GetInstance();
}
#endif // CHIP_DNSSD_DEFAULT_PLATFORM
} // namespace Dnssd
} // namespace chip