| /* |
| * |
| * 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 |