/* * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace chip { namespace Dnssd { namespace { static void HandleNodeResolve(void * context, DnssdService * result, const Span & addresses, CHIP_ERROR error) { DiscoveryContext * discoveryContext = static_cast(context); if (error != CHIP_NO_ERROR && error != CHIP_ERROR_IN_PROGRESS) { discoveryContext->Release(); return; } DiscoveredNodeData nodeData; result->ToDiscoveredCommissionNodeData(addresses, nodeData); nodeData.Get().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(context); if (error != CHIP_NO_ERROR) { discoveryContext->Release(); return; } DiscoveredNodeData nodeData; result->ToDiscoveredOperationalNodeBrowseData(nodeData); nodeData.Get().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(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(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 CHIP_ERROR AddPtrRecord(DiscoveryFilterType type, const char ** entries, size_t & entriesCount, char * buffer, size_t bufferLen, const std::optional & 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(value)); } template CHIP_ERROR CopyTextRecordValue(char * buffer, size_t bufferLen, std::optional value) { VerifyOrReturnError(value.has_value(), CHIP_ERROR_UNINITIALIZED); return CopyTextRecordValue(buffer, bufferLen, *value); } CHIP_ERROR CopyTextRecordValue(char * buffer, size_t bufferLen, std::optional value1, std::optional 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 & 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 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(params.GetCommissionerPasscodeSupported().value_or(false) ? 1 : 0)); default: return CopyTxtRecord(key, buffer, bufferLen, static_cast>(params)); } } template 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(key)].keyStr, reinterpret_cast(buffer), strnlen(buffer, bufferLen) }; return CHIP_NO_ERROR; } } // namespace void DiscoveryImplPlatform::HandleNodeIdResolve(void * context, DnssdService * result, const Span & addresses, CHIP_ERROR error) { DiscoveryImplPlatform * impl = static_cast(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(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(); ExtractIdFromInstanceName(mName, &nodeData.Get().peerId); nodeData.Get().hasZeroTTL = (mTtlSeconds == 0); } void DnssdService::ToDiscoveredCommissionNodeData(const Span & addresses, DiscoveredNodeData & nodeData) { nodeData.Set(); auto & discoveredData = nodeData.Get(); 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(mTextEntries[i].mKey), strlen(mTextEntries[i].mKey)); ByteSpan val(mTextEntries[i].mData, mTextEntries[i].mDataSize); FillNodeDataFromTxt(key, val, discoveredData); } } Global 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(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(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(textEntrySize), static_cast(subTypeSize)); for (size_t i = 0; i < textEntrySize; i++) { printf(" entry [%u] : %s %s\n", static_cast(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(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 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