diff --git a/examples/chip-tool/BUILD.gn b/examples/chip-tool/BUILD.gn index e1e92d94b62ec3..8f01267ff9c03a 100644 --- a/examples/chip-tool/BUILD.gn +++ b/examples/chip-tool/BUILD.gn @@ -61,7 +61,6 @@ static_library("chip-tool-utils") { "commands/common/Commands.cpp", "commands/common/Commands.h", "commands/common/CredentialIssuerCommands.h", - "commands/common/DeviceScanner.cpp", "commands/common/HexConversion.h", "commands/common/RemoteDataModelLogger.cpp", "commands/common/RemoteDataModelLogger.h", @@ -97,6 +96,10 @@ static_library("chip-tool-utils") { sources += [ "commands/tests/TestCommand.cpp" ] } + if (chip_device_platform == "darwin") { + sources += [ "commands/common/DeviceScanner.cpp" ] + } + public_deps = [ "${chip_root}/src/app/server", "${chip_root}/src/app/tests/suites/commands/commissioner", diff --git a/examples/chip-tool/commands/common/DeviceScanner.cpp b/examples/chip-tool/commands/common/DeviceScanner.cpp index f5c1e39fda7d9d..9a1858691758ea 100644 --- a/examples/chip-tool/commands/common/DeviceScanner.cpp +++ b/examples/chip-tool/commands/common/DeviceScanner.cpp @@ -19,34 +19,38 @@ #include "DeviceScanner.h" using namespace chip; -using namespace chip::Ble; using namespace chip::Dnssd; +#if CONFIG_NETWORK_LAYER_BLE +using namespace chip::Ble; +constexpr const char * kBleKey = "BLE"; +#endif // CONFIG_NETWORK_LAYER_BLE + CHIP_ERROR DeviceScanner::Start() { mDiscoveredResults.clear(); -#if CHIP_TOOL_DEVICE_SCANNER_USE_BLE +#if CONFIG_NETWORK_LAYER_BLE ReturnErrorOnFailure(DeviceLayer::PlatformMgrImpl().StartBleScan(this)); -#endif // CHIP_TOOL_DEVICE_SCANNER_USE_BLE +#endif // CONFIG_NETWORK_LAYER_BLE - ReturnErrorOnFailure(mDNSResolver.Init(DeviceLayer::UDPEndPointManager())); - mDNSResolver.SetCommissioningDelegate(this); + ReturnErrorOnFailure(chip::Dnssd::Resolver::Instance().Init(DeviceLayer::UDPEndPointManager())); - DiscoveryFilter filter(DiscoveryFilterType::kNone, (uint64_t) 0); - return mDNSResolver.DiscoverCommissionableNodes(filter); + char serviceName[kMaxCommissionableServiceNameSize]; + auto filter = DiscoveryFilterType::kNone; + ReturnErrorOnFailure(MakeServiceTypeName(serviceName, sizeof(serviceName), filter, DiscoveryType::kCommissionableNode)); + + return ChipDnssdBrowse(serviceName, DnssdServiceProtocol::kDnssdProtocolUdp, Inet::IPAddressType::kAny, + Inet::InterfaceId::Null(), this); } CHIP_ERROR DeviceScanner::Stop() { -#if CHIP_TOOL_DEVICE_SCANNER_USE_BLE +#if CONFIG_NETWORK_LAYER_BLE ReturnErrorOnFailure(DeviceLayer::PlatformMgrImpl().StopBleScan()); -#endif // CHIP_TOOL_DEVICE_SCANNER_USE_BLE +#endif // CONFIG_NETWORK_LAYER_BLE - mDNSResolver.SetCommissioningDelegate(nullptr); - ReturnErrorOnFailure(mDNSResolver.StopDiscovery()); - mDNSResolver.Shutdown(); - return CHIP_NO_ERROR; + return ChipDnssdStopBrowse(this); } void DeviceScanner::OnNodeDiscovered(const DiscoveredNodeData & nodeData) @@ -61,41 +65,152 @@ void DeviceScanner::OnNodeDiscovered(const DiscoveredNodeData & nodeData) productId); auto & resolutionData = nodeData.resolutionData; + + auto & instanceData = mDiscoveredResults[commissionData.instanceName]; + auto & interfaceData = instanceData[resolutionData.interfaceId.GetPlatformInterface()]; + for (size_t i = 0; i < resolutionData.numIPs; i++) { auto params = Controller::SetUpCodePairerParameters(resolutionData, i); - DeviceScannerResult result = { params, vendorId, productId, discriminator }; - mDiscoveredResults.push_back(result); + DeviceScannerResult result = { params, vendorId, productId, discriminator, chip::MakeOptional(resolutionData) }; + interfaceData.push_back(result); } nodeData.LogDetail(); } -#if CHIP_TOOL_DEVICE_SCANNER_USE_BLE +void DeviceScanner::OnBrowseAdd(chip::Dnssd::DnssdService service) +{ + ChipLogProgress(chipTool, "OnBrowseAdd: %s", service.mName); + LogErrorOnFailure(ChipDnssdResolve(&service, service.mInterface, this)); + + auto & instanceData = mDiscoveredResults[service.mName]; + auto & interfaceData = instanceData[service.mInterface.GetPlatformInterface()]; + (void) interfaceData; +} + +void DeviceScanner::OnBrowseRemove(chip::Dnssd::DnssdService service) +{ + ChipLogProgress(chipTool, "OnBrowseRemove: %s", service.mName); + auto & instanceData = mDiscoveredResults[service.mName]; + auto & interfaceData = instanceData[service.mInterface.GetPlatformInterface()]; + + // Check if the interface data has been resolved already, otherwise, just inform the + // back end that we may not need it anymore. + if (interfaceData.size() == 0) + { + ChipDnssdResolveNoLongerNeeded(service.mName); + } + + // Delete the interface placeholder. + instanceData.erase(service.mInterface.GetPlatformInterface()); + + // If there is nothing else to resolve for the given instance name, just remove it + // too. + if (instanceData.size() == 0) + { + mDiscoveredResults.erase(service.mName); + } +} + +void DeviceScanner::OnBrowseStop(CHIP_ERROR error) +{ + ChipLogProgress(chipTool, "OnBrowseStop: %" CHIP_ERROR_FORMAT, error.Format()); + + for (auto & instance : mDiscoveredResults) + { + for (auto & interface : instance.second) + { + if (interface.second.size() == 0) + { + ChipDnssdResolveNoLongerNeeded(instance.first.c_str()); + } + } + } +} + +#if CONFIG_NETWORK_LAYER_BLE void DeviceScanner::OnBleScanAdd(BLE_CONNECTION_OBJECT connObj, const ChipBLEDeviceIdentificationInfo & info) { auto discriminator = info.GetDeviceDiscriminator(); auto vendorId = static_cast(info.GetVendorId()); auto productId = info.GetProductId(); - ChipLogProgress(chipTool, "OnNodeDiscovered (BLE): discriminator: %u, vendorId: %u, productId: %u", discriminator, vendorId, - productId); + ChipLogProgress(chipTool, "OnBleScanAdd (BLE): %p, discriminator: %u, vendorId: %u, productId: %u", connObj, discriminator, + vendorId, productId); auto params = Controller::SetUpCodePairerParameters(connObj, false /* connected */); DeviceScannerResult result = { params, vendorId, productId, discriminator }; - mDiscoveredResults.push_back(result); + + auto & instanceData = mDiscoveredResults[kBleKey]; + auto & interfaceData = instanceData[chip::Inet::InterfaceId::Null().GetPlatformInterface()]; + interfaceData.push_back(result); } -void DeviceScanner::OnBleScanRemove(BLE_CONNECTION_OBJECT connObj) {} -#endif // CHIP_TOOL_DEVICE_SCANNER_USE_BLE +void DeviceScanner::OnBleScanRemove(BLE_CONNECTION_OBJECT connObj) +{ + ChipLogProgress(chipTool, "OnBleScanRemove: %p", connObj); + + auto & instanceData = mDiscoveredResults[kBleKey]; + auto & interfaceData = instanceData[chip::Inet::InterfaceId::Null().GetPlatformInterface()]; + + interfaceData.erase(std::remove_if(interfaceData.begin(), interfaceData.end(), + [connObj](const DeviceScannerResult & result) { + return result.mParams.HasDiscoveredObject() && + result.mParams.GetDiscoveredObject() == connObj; + }), + interfaceData.end()); + + if (interfaceData.size() == 0) + { + instanceData.clear(); + mDiscoveredResults.erase(kBleKey); + } +} +#endif // CONFIG_NETWORK_LAYER_BLE CHIP_ERROR DeviceScanner::Get(uint16_t index, RendezvousParameters & params) { - VerifyOrReturnError(index < mDiscoveredResults.size(), CHIP_ERROR_NOT_FOUND); + uint16_t currentIndex = 0; + for (auto & instance : mDiscoveredResults) + { + for (auto & interface : instance.second) + { + for (auto & result : interface.second) + { + if (currentIndex == index) + { + params = result.mParams; + return CHIP_NO_ERROR; + } + currentIndex++; + } + } + } - auto & result = mDiscoveredResults.at(index); - params = result.mParams; - return CHIP_NO_ERROR; + return CHIP_ERROR_NOT_FOUND; +} + +CHIP_ERROR DeviceScanner::Get(uint16_t index, Dnssd::CommonResolutionData & resolutionData) +{ + uint16_t currentIndex = 0; + for (auto & instance : mDiscoveredResults) + { + for (auto & interface : instance.second) + { + for (auto & result : interface.second) + { + if (currentIndex == index && result.mResolutionData.HasValue()) + { + resolutionData = result.mResolutionData.Value(); + return CHIP_NO_ERROR; + } + currentIndex++; + } + } + } + + return CHIP_ERROR_NOT_FOUND; } void DeviceScanner::Log() const @@ -104,14 +219,21 @@ void DeviceScanner::Log() const VerifyOrReturn(resultsCount > 0, ChipLogProgress(chipTool, "No device discovered.")); uint16_t index = 0; - for (auto & result : mDiscoveredResults) + for (auto & instance : mDiscoveredResults) { - char addr[Transport::PeerAddress::kMaxToStringSize]; - result.mParams.GetPeerAddress().ToString(addr); - - ChipLogProgress(chipTool, "\t %u - Discriminator: %u - Vendor: %u - Product: %u - %s", index, result.mDiscriminator, - result.mVendorId, result.mProductId, addr); - index++; + ChipLogProgress(chipTool, "Instance Name: %s ", instance.first.c_str()); + for (auto & interface : instance.second) + { + for (auto & result : interface.second) + { + char addr[Transport::PeerAddress::kMaxToStringSize]; + result.mParams.GetPeerAddress().ToString(addr); + + ChipLogProgress(chipTool, "\t %u - Discriminator: %u - Vendor: %u - Product: %u - %s", index, result.mDiscriminator, + result.mVendorId, result.mProductId, addr); + index++; + } + } } } diff --git a/examples/chip-tool/commands/common/DeviceScanner.h b/examples/chip-tool/commands/common/DeviceScanner.h index 57dd6affc8ef46..5ea7d036bb3d66 100644 --- a/examples/chip-tool/commands/common/DeviceScanner.h +++ b/examples/chip-tool/commands/common/DeviceScanner.h @@ -18,54 +18,62 @@ #pragma once +#include + +#if CHIP_DEVICE_LAYER_TARGET_DARWIN + #include -#include +#include +#include #include #if CONFIG_NETWORK_LAYER_BLE -#if CHIP_DEVICE_LAYER_TARGET_DARWIN #include -#define CHIP_TOOL_DEVICE_SCANNER_USE_BLE 1 -#endif // CHIP_DEVICE_LAYER_TARGET_DARWIN #endif // CONFIG_NETWORK_LAYER_BLE -#ifndef CHIP_TOOL_DEVICE_SCANNER_USE_BLE -#define CHIP_TOOL_DEVICE_SCANNER_USE_BLE 0 -#endif // CHIP_TOOL_DEVICE_SCANNER_USE_BLE - struct DeviceScannerResult { chip::Controller::SetUpCodePairerParameters mParams; chip::VendorId mVendorId; uint16_t mProductId; uint16_t mDiscriminator; + chip::Optional mResolutionData; }; -class DeviceScanner : public chip::Dnssd::CommissioningResolveDelegate -#if CHIP_TOOL_DEVICE_SCANNER_USE_BLE +class DeviceScanner : public chip::Dnssd::CommissioningResolveDelegate, + public chip::Dnssd::DnssdBrowseDelegate +#if CONFIG_NETWORK_LAYER_BLE , public chip::DeviceLayer::BleScannerDelegate -#endif // CHIP_TOOL_DEVICE_SCANNER_USE_BLE +#endif // CONFIG_NETWORK_LAYER_BLE { public: CHIP_ERROR Start(); CHIP_ERROR Stop(); CHIP_ERROR Get(uint16_t index, chip::RendezvousParameters & params); + CHIP_ERROR Get(uint16_t index, chip::Dnssd::CommonResolutionData & resolutionData); void Log() const; /////////// CommissioningResolveDelegate Interface ///////// void OnNodeDiscovered(const chip::Dnssd::DiscoveredNodeData & nodeData) override; -#if CHIP_TOOL_DEVICE_SCANNER_USE_BLE + /////////// DnssdBrowseDelegate Interface ///////// + void OnBrowseAdd(chip::Dnssd::DnssdService service) override; + void OnBrowseRemove(chip::Dnssd::DnssdService service) override; + void OnBrowseStop(CHIP_ERROR error) override; + +#if CONFIG_NETWORK_LAYER_BLE /////////// BleScannerDelegate Interface ///////// void OnBleScanAdd(BLE_CONNECTION_OBJECT connObj, const chip::Ble::ChipBLEDeviceIdentificationInfo & info) override; void OnBleScanRemove(BLE_CONNECTION_OBJECT connObj) override; -#endif // CHIP_TOOL_DEVICE_SCANNER_USE_BLE +#endif // CONFIG_NETWORK_LAYER_BLE private: - std::vector mDiscoveredResults; - chip::Dnssd::ResolverProxy mDNSResolver; + std::unordered_map>> + mDiscoveredResults; }; DeviceScanner & GetDeviceScanner(); + +#endif // CHIP_DEVICE_LAYER_TARGET_DARWIN diff --git a/examples/chip-tool/commands/discover/DiscoverCommissionablesCommand.cpp b/examples/chip-tool/commands/discover/DiscoverCommissionablesCommand.cpp index ed5d33aa0ed274..9e65a87b16bac7 100644 --- a/examples/chip-tool/commands/discover/DiscoverCommissionablesCommand.cpp +++ b/examples/chip-tool/commands/discover/DiscoverCommissionablesCommand.cpp @@ -38,29 +38,41 @@ void DiscoverCommissionablesCommandBase::OnDiscoveredDevice(const chip::Dnssd::D CHIP_ERROR DiscoverCommissionablesStartCommand::RunCommand() { +#if CHIP_DEVICE_LAYER_TARGET_DARWIN VerifyOrReturnError(IsInteractive(), CHIP_ERROR_INCORRECT_STATE); ReturnErrorOnFailure(GetDeviceScanner().Start()); SetCommandExitStatus(CHIP_NO_ERROR); return CHIP_NO_ERROR; +#else + return CHIP_ERROR_NOT_IMPLEMENTED; +#endif // CHIP_DEVICE_LAYER_TARGET_DARWIN } CHIP_ERROR DiscoverCommissionablesStopCommand::RunCommand() { +#if CHIP_DEVICE_LAYER_TARGET_DARWIN VerifyOrReturnError(IsInteractive(), CHIP_ERROR_INCORRECT_STATE); ReturnErrorOnFailure(GetDeviceScanner().Stop()); SetCommandExitStatus(CHIP_NO_ERROR); return CHIP_NO_ERROR; +#else + return CHIP_ERROR_NOT_IMPLEMENTED; +#endif // CHIP_DEVICE_LAYER_TARGET_DARWIN } CHIP_ERROR DiscoverCommissionablesListCommand::RunCommand() { +#if CHIP_DEVICE_LAYER_TARGET_DARWIN VerifyOrReturnError(IsInteractive(), CHIP_ERROR_INCORRECT_STATE); GetDeviceScanner().Log(); SetCommandExitStatus(CHIP_NO_ERROR); return CHIP_NO_ERROR; +#else + return CHIP_ERROR_NOT_IMPLEMENTED; +#endif // CHIP_DEVICE_LAYER_TARGET_DARWIN } CHIP_ERROR DiscoverCommissionablesCommand::RunCommand() diff --git a/examples/chip-tool/commands/pairing/Commands.h b/examples/chip-tool/commands/pairing/Commands.h index 213c9e3ceb58f5..6d02253c13de92 100644 --- a/examples/chip-tool/commands/pairing/Commands.h +++ b/examples/chip-tool/commands/pairing/Commands.h @@ -199,6 +199,15 @@ class PairAlreadyDiscoveredByIndexWithWiFi : public PairingCommand {} }; +class PairAlreadyDiscoveredByIndexWithCode : public PairingCommand +{ +public: + PairAlreadyDiscoveredByIndexWithCode(CredentialIssuerCommands * credsIssuerConfig) : + PairingCommand("already-discovered-by-index-with-code", PairingMode::AlreadyDiscoveredByIndexWithCode, + PairingNetworkType::None, credsIssuerConfig) + {} +}; + class StartUdcServerCommand : public CHIPCommand { public: @@ -228,6 +237,7 @@ void registerCommandsPairing(Commands & commands, CredentialIssuerCommands * cre make_unique(credsIssuerConfig), make_unique(credsIssuerConfig), make_unique(credsIssuerConfig), + make_unique(credsIssuerConfig), make_unique(credsIssuerConfig), make_unique(credsIssuerConfig), make_unique(credsIssuerConfig), diff --git a/examples/chip-tool/commands/pairing/PairingCommand.cpp b/examples/chip-tool/commands/pairing/PairingCommand.cpp index ed48fd912f91a3..d5b0401b662269 100644 --- a/examples/chip-tool/commands/pairing/PairingCommand.cpp +++ b/examples/chip-tool/commands/pairing/PairingCommand.cpp @@ -82,6 +82,9 @@ CHIP_ERROR PairingCommand::RunInternal(NodeId remoteId) case PairingMode::AlreadyDiscoveredByIndex: err = PairWithMdnsOrBleByIndex(remoteId, mIndex); break; + case PairingMode::AlreadyDiscoveredByIndexWithCode: + err = PairWithMdnsOrBleByIndexWithCode(remoteId, mIndex); + break; } return err; @@ -173,6 +176,7 @@ CHIP_ERROR PairingCommand::Pair(NodeId remoteId, PeerAddress address) CHIP_ERROR PairingCommand::PairWithMdnsOrBleByIndex(NodeId remoteId, uint16_t index) { +#if CHIP_DEVICE_LAYER_TARGET_DARWIN VerifyOrReturnError(IsInteractive(), CHIP_ERROR_INCORRECT_STATE); RendezvousParameters params; @@ -190,6 +194,56 @@ CHIP_ERROR PairingCommand::PairWithMdnsOrBleByIndex(NodeId remoteId, uint16_t in err = CurrentCommissioner().PairDevice(remoteId, params, commissioningParams); } return err; +#else + return CHIP_ERROR_NOT_IMPLEMENTED; +#endif // CHIP_DEVICE_LAYER_TARGET_DARWIN +} + +CHIP_ERROR PairingCommand::PairWithMdnsOrBleByIndexWithCode(NodeId remoteId, uint16_t index) +{ +#if CHIP_DEVICE_LAYER_TARGET_DARWIN + VerifyOrReturnError(IsInteractive(), CHIP_ERROR_INCORRECT_STATE); + + Dnssd::CommonResolutionData resolutionData; + auto err = GetDeviceScanner().Get(index, resolutionData); + if (CHIP_ERROR_NOT_FOUND == err) + { + // There is no device with this index that has some resolution data. This could simply + // be because the device is a ble device. In this case let's fall back to looking for + // a device with this index and some RendezvousParameters. + chip::SetupPayload payload; + bool isQRCode = strncmp(mOnboardingPayload, kQRCodePrefix, strlen(kQRCodePrefix)) == 0; + if (isQRCode) + { + ReturnErrorOnFailure(QRCodeSetupPayloadParser(mOnboardingPayload).populatePayload(payload)); + VerifyOrReturnError(payload.isValidQRCodePayload(), CHIP_ERROR_INVALID_ARGUMENT); + } + else + { + ReturnErrorOnFailure(ManualSetupPayloadParser(mOnboardingPayload).populatePayload(payload)); + VerifyOrReturnError(payload.isValidManualCode(), CHIP_ERROR_INVALID_ARGUMENT); + } + + mSetupPINCode = payload.setUpPINCode; + return PairWithMdnsOrBleByIndex(remoteId, index); + } + + err = CHIP_NO_ERROR; + if (mPaseOnly.ValueOr(false)) + { + err = CurrentCommissioner().EstablishPASEConnection(remoteId, mOnboardingPayload, DiscoveryType::kDiscoveryNetworkOnly, + MakeOptional(resolutionData)); + } + else + { + auto commissioningParams = GetCommissioningParameters(); + err = CurrentCommissioner().PairDevice(remoteId, mOnboardingPayload, commissioningParams, + DiscoveryType::kDiscoveryNetworkOnly, MakeOptional(resolutionData)); + } + return err; +#else + return CHIP_ERROR_NOT_IMPLEMENTED; +#endif // CHIP_DEVICE_LAYER_TARGET_DARWIN } CHIP_ERROR PairingCommand::PairWithMdns(NodeId remoteId) diff --git a/examples/chip-tool/commands/pairing/PairingCommand.h b/examples/chip-tool/commands/pairing/PairingCommand.h index 5c2a356d6aec73..a92ec4d1c67172 100644 --- a/examples/chip-tool/commands/pairing/PairingCommand.h +++ b/examples/chip-tool/commands/pairing/PairingCommand.h @@ -36,6 +36,7 @@ enum class PairingMode SoftAP, AlreadyDiscovered, AlreadyDiscoveredByIndex, + AlreadyDiscoveredByIndexWithCode, OnNetwork, }; @@ -122,6 +123,12 @@ class PairingCommand : public CHIPCommand, AddArgument("index", 0, UINT16_MAX, &mIndex); AddArgument("pase-only", 0, 1, &mPaseOnly); break; + case PairingMode::AlreadyDiscoveredByIndexWithCode: + AddArgument("skip-commissioning-complete", 0, 1, &mSkipCommissioningComplete); + AddArgument("payload", &mOnboardingPayload); + AddArgument("index", 0, UINT16_MAX, &mIndex); + AddArgument("pase-only", 0, 1, &mPaseOnly); + break; } switch (filterType) @@ -180,6 +187,7 @@ class PairingCommand : public CHIPCommand, CHIP_ERROR PairWithCode(NodeId remoteId); CHIP_ERROR PaseWithCode(NodeId remoteId); CHIP_ERROR PairWithMdnsOrBleByIndex(NodeId remoteId, uint16_t index); + CHIP_ERROR PairWithMdnsOrBleByIndexWithCode(NodeId remoteId, uint16_t index); CHIP_ERROR Unpair(NodeId remoteId); chip::Controller::CommissioningParameters GetCommissioningParameters(); diff --git a/src/controller/CHIPDeviceController.cpp b/src/controller/CHIPDeviceController.cpp index a7936f6f1560ba..4c0e21619d427d 100644 --- a/src/controller/CHIPDeviceController.cpp +++ b/src/controller/CHIPDeviceController.cpp @@ -587,7 +587,7 @@ CHIP_ERROR DeviceCommissioner::GetDeviceBeingCommissioned(NodeId deviceId, Commi } CHIP_ERROR DeviceCommissioner::PairDevice(NodeId remoteDeviceId, const char * setUpCode, const CommissioningParameters & params, - DiscoveryType discoveryType) + DiscoveryType discoveryType, Optional resolutionData) { MATTER_TRACE_EVENT_SCOPE("PairDevice", "DeviceCommissioner"); @@ -598,13 +598,16 @@ CHIP_ERROR DeviceCommissioner::PairDevice(NodeId remoteDeviceId, const char * se } ReturnErrorOnFailure(mDefaultCommissioner->SetCommissioningParameters(params)); - return mSetUpCodePairer.PairDevice(remoteDeviceId, setUpCode, SetupCodePairerBehaviour::kCommission, discoveryType); + return mSetUpCodePairer.PairDevice(remoteDeviceId, setUpCode, SetupCodePairerBehaviour::kCommission, discoveryType, + resolutionData); } -CHIP_ERROR DeviceCommissioner::PairDevice(NodeId remoteDeviceId, const char * setUpCode, DiscoveryType discoveryType) +CHIP_ERROR DeviceCommissioner::PairDevice(NodeId remoteDeviceId, const char * setUpCode, DiscoveryType discoveryType, + Optional resolutionData) { MATTER_TRACE_EVENT_SCOPE("PairDevice", "DeviceCommissioner"); - return mSetUpCodePairer.PairDevice(remoteDeviceId, setUpCode, SetupCodePairerBehaviour::kCommission, discoveryType); + return mSetUpCodePairer.PairDevice(remoteDeviceId, setUpCode, SetupCodePairerBehaviour::kCommission, discoveryType, + resolutionData); } CHIP_ERROR DeviceCommissioner::PairDevice(NodeId remoteDeviceId, RendezvousParameters & params) @@ -622,10 +625,12 @@ CHIP_ERROR DeviceCommissioner::PairDevice(NodeId remoteDeviceId, RendezvousParam return Commission(remoteDeviceId, commissioningParams); } -CHIP_ERROR DeviceCommissioner::EstablishPASEConnection(NodeId remoteDeviceId, const char * setUpCode, DiscoveryType discoveryType) +CHIP_ERROR DeviceCommissioner::EstablishPASEConnection(NodeId remoteDeviceId, const char * setUpCode, DiscoveryType discoveryType, + Optional resolutionData) { MATTER_TRACE_EVENT_SCOPE("EstablishPASEConnection", "DeviceCommissioner"); - return mSetUpCodePairer.PairDevice(remoteDeviceId, setUpCode, SetupCodePairerBehaviour::kPaseOnly, discoveryType); + return mSetUpCodePairer.PairDevice(remoteDeviceId, setUpCode, SetupCodePairerBehaviour::kPaseOnly, discoveryType, + resolutionData); } CHIP_ERROR DeviceCommissioner::EstablishPASEConnection(NodeId remoteDeviceId, RendezvousParameters & params) diff --git a/src/controller/CHIPDeviceController.h b/src/controller/CHIPDeviceController.h index 3f6f5c65c89fc0..2f686ac455160a 100644 --- a/src/controller/CHIPDeviceController.h +++ b/src/controller/CHIPDeviceController.h @@ -425,10 +425,13 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController, * @param[in] remoteDeviceId The remote device Id. * @param[in] setUpCode The setup code for connecting to the device * @param[in] discoveryType The network discovery type, defaults to DiscoveryType::kAll. + * @param[in] resolutionData Optional resolution data previously discovered on the network for the target device. */ - CHIP_ERROR PairDevice(NodeId remoteDeviceId, const char * setUpCode, DiscoveryType discoveryType = DiscoveryType::kAll); + CHIP_ERROR PairDevice(NodeId remoteDeviceId, const char * setUpCode, DiscoveryType discoveryType = DiscoveryType::kAll, + Optional resolutionData = NullOptional); CHIP_ERROR PairDevice(NodeId remoteDeviceId, const char * setUpCode, const CommissioningParameters & CommissioningParameters, - DiscoveryType discoveryType = DiscoveryType::kAll); + DiscoveryType discoveryType = DiscoveryType::kAll, + Optional resolutionData = NullOptional); /** * @brief @@ -491,9 +494,11 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController, * @param[in] remoteDeviceId The remote device Id. * @param[in] setUpCode The setup code for connecting to the device * @param[in] discoveryType The network discovery type, defaults to DiscoveryType::kAll. + * @param[in] resolutionData Optional resolution data previously discovered on the network for the target device. */ CHIP_ERROR EstablishPASEConnection(NodeId remoteDeviceId, const char * setUpCode, - DiscoveryType discoveryType = DiscoveryType::kAll); + DiscoveryType discoveryType = DiscoveryType::kAll, + Optional resolutionData = NullOptional); /** * @brief diff --git a/src/controller/SetUpCodePairer.cpp b/src/controller/SetUpCodePairer.cpp index de32224c4fc9c6..27272caebf1fd4 100644 --- a/src/controller/SetUpCodePairer.cpp +++ b/src/controller/SetUpCodePairer.cpp @@ -37,9 +37,13 @@ namespace chip { namespace Controller { CHIP_ERROR SetUpCodePairer::PairDevice(NodeId remoteId, const char * setUpCode, SetupCodePairerBehaviour commission, - DiscoveryType discoveryType) + DiscoveryType discoveryType, Optional resolutionData) { VerifyOrReturnError(mSystemLayer != nullptr, CHIP_ERROR_INCORRECT_STATE); + if (resolutionData.HasValue()) + { + VerifyOrReturnError(discoveryType != DiscoveryType::kAll, CHIP_ERROR_INVALID_ARGUMENT); + } SetupPayload payload; mConnectionType = commission; @@ -62,12 +66,16 @@ CHIP_ERROR SetUpCodePairer::PairDevice(NodeId remoteId, const char * setUpCode, ResetDiscoveryState(); - ReturnErrorOnFailure(Connect(payload)); + if (resolutionData.HasValue()) + { + NotifyCommissionableDeviceDiscovered(resolutionData.Value()); + return CHIP_NO_ERROR; + } + ReturnErrorOnFailure(Connect(payload)); return mSystemLayer->StartTimer(System::Clock::Milliseconds32(kDeviceDiscoveredTimeout), OnDeviceDiscoveredTimeoutCallback, this); } - CHIP_ERROR SetUpCodePairer::Connect(SetupPayload & payload) { CHIP_ERROR err = CHIP_NO_ERROR; @@ -358,20 +366,23 @@ void SetUpCodePairer::NotifyCommissionableDeviceDiscovered(const Dnssd::Discover ChipLogProgress(Controller, "Discovered device to be commissioned over DNS-SD"); - auto & resolutionData = nodeData.resolutionData; + NotifyCommissionableDeviceDiscovered(nodeData.resolutionData); +} +void SetUpCodePairer::NotifyCommissionableDeviceDiscovered(const Dnssd::CommonResolutionData & resolutionData) +{ if (mDiscoveryType == DiscoveryType::kDiscoveryNetworkOnlyWithoutPASEAutoRetry) { // If the discovery type does not want the PASE auto retry mechanism, we will just store // a single IP. So the discovery process is stopped as it won't be of any help anymore. StopConnectOverIP(); - mDiscoveredParameters.emplace_back(nodeData.resolutionData, 0); + mDiscoveredParameters.emplace_back(resolutionData, 0); } else { for (size_t i = 0; i < resolutionData.numIPs; i++) { - mDiscoveredParameters.emplace_back(nodeData.resolutionData, i); + mDiscoveredParameters.emplace_back(resolutionData, i); } } diff --git a/src/controller/SetUpCodePairer.h b/src/controller/SetUpCodePairer.h index 1fd1ebffdb903e..b6a93f4eeedfc8 100644 --- a/src/controller/SetUpCodePairer.h +++ b/src/controller/SetUpCodePairer.h @@ -79,8 +79,9 @@ class DLL_EXPORT SetUpCodePairer : public DevicePairingDelegate virtual ~SetUpCodePairer() {} CHIP_ERROR PairDevice(chip::NodeId remoteId, const char * setUpCode, - SetupCodePairerBehaviour connectionType = SetupCodePairerBehaviour::kCommission, - DiscoveryType discoveryType = DiscoveryType::kAll); + SetupCodePairerBehaviour connectionType = SetupCodePairerBehaviour::kCommission, + DiscoveryType discoveryType = DiscoveryType::kAll, + Optional resolutionData = NullOptional); // Called by the DeviceCommissioner to notify that we have discovered a new device. void NotifyCommissionableDeviceDiscovered(const chip::Dnssd::DiscoveredNodeData & nodeData); @@ -151,6 +152,8 @@ class DLL_EXPORT SetUpCodePairer : public DevicePairingDelegate kTransportTypeCount, }; + void NotifyCommissionableDeviceDiscovered(const chip::Dnssd::CommonResolutionData & resolutionData); + static void OnDeviceDiscoveredTimeoutCallback(System::Layer * layer, void * context); #if CONFIG_NETWORK_LAYER_BLE diff --git a/src/lib/dnssd/Discovery_ImplPlatform.cpp b/src/lib/dnssd/Discovery_ImplPlatform.cpp index 675e7ac238106b..5ce64f9b21ed4e 100644 --- a/src/lib/dnssd/Discovery_ImplPlatform.cpp +++ b/src/lib/dnssd/Discovery_ImplPlatform.cpp @@ -48,38 +48,7 @@ static void HandleNodeResolve(void * context, DnssdService * result, const Span< } DiscoveredNodeData nodeData; - - Platform::CopyString(nodeData.resolutionData.hostName, result->mHostName); - Platform::CopyString(nodeData.commissionData.instanceName, result->mName); - - nodeData.resolutionData.interfaceId = result->mInterface; - - IPAddressSorter::Sort(addresses, result->mInterface); - - 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 DiscoveredNodeData"); - break; - } - nodeData.resolutionData.ipAddress[addressesFound] = ip; - ++addressesFound; - } - - nodeData.resolutionData.numIPs = addressesFound; - - nodeData.resolutionData.port = result->mPort; - - 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); - FillNodeDataFromTxt(key, val, nodeData.commissionData); - } + result->ToDiscoveredNodeData(addresses, nodeData); nodeData.LogDetail(); proxy->OnNodeDiscovered(nodeData); @@ -354,6 +323,42 @@ CHIP_ERROR AddTxtRecord(TxtFieldKey key, TextEntry * entries, size_t & entriesCo } // namespace +void DnssdService::ToDiscoveredNodeData(const Span & addresses, DiscoveredNodeData & nodeData) +{ + auto & resolutionData = nodeData.resolutionData; + auto & commissionData = nodeData.commissionData; + + Platform::CopyString(resolutionData.hostName, mHostName); + Platform::CopyString(commissionData.instanceName, mName); + + IPAddressSorter::Sort(addresses, mInterface); + + size_t addressesFound = 0; + for (auto & ip : addresses) + { + if (addressesFound == ArraySize(resolutionData.ipAddress)) + { + // Out of space. + ChipLogProgress(Discovery, "Can't add more IPs to DiscoveredNodeData"); + break; + } + resolutionData.ipAddress[addressesFound] = ip; + ++addressesFound; + } + + resolutionData.interfaceId = mInterface; + resolutionData.numIPs = addressesFound; + resolutionData.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, resolutionData); + FillNodeDataFromTxt(key, val, commissionData); + } +} + DiscoveryImplPlatform DiscoveryImplPlatform::sManager; DiscoveryImplPlatform::DiscoveryImplPlatform() = default; diff --git a/src/lib/dnssd/platform/Dnssd.h b/src/lib/dnssd/platform/Dnssd.h index 7c21f9e1012952..d2d834d00f7034 100644 --- a/src/lib/dnssd/platform/Dnssd.h +++ b/src/lib/dnssd/platform/Dnssd.h @@ -36,6 +36,8 @@ #include #include +#include "DnssdBrowseDelegate.h" + namespace chip { namespace Dnssd { @@ -78,6 +80,8 @@ struct DnssdService Optional mAddress; // Time to live in seconds. Per rfc6762 section 10, because we have a hostname, our default TTL is 120 seconds uint32_t mTtlSeconds = 120; + + void ToDiscoveredNodeData(const Span & addresses, DiscoveredNodeData & nodeData); }; /** @@ -229,6 +233,32 @@ CHIP_ERROR ChipDnssdBrowse(const char * type, DnssdServiceProtocol protocol, chi */ CHIP_ERROR ChipDnssdStopBrowse(intptr_t browseIdentifier); +#if CHIP_DEVICE_LAYER_TARGET_DARWIN +/** + * This function continuously browses the services published by mDNS + * and reports any addition/removal of services. + * + * @param[in] type The service type. + * @param[in] protocol The service protocol. + * @param[in] addressType The protocol version of the IP address. + * @param[in] interface The interface to send queries. + * @param[in] delegate The delegate to notify when a service is found/removed. + * + * @retval CHIP_NO_ERROR The browse succeeds. + * @retval CHIP_ERROR_INVALID_ARGUMENT The type or the delegate is nullptr. + * @retval Error code The browse fails. + * + */ +CHIP_ERROR ChipDnssdBrowse(const char * type, DnssdServiceProtocol protocol, chip::Inet::IPAddressType addressType, + chip::Inet::InterfaceId interface, DnssdBrowseDelegate * delegate); + +/** + * Stop an ongoing browse, if supported by this backend. If successful, this + * will call the OnBrowseStop method of the delegate. + */ +CHIP_ERROR ChipDnssdStopBrowse(DnssdBrowseDelegate * delegate); +#endif // CHIP_DEVICE_LAYER_TARGET_DARWIN + /** * This function resolves the services published by mDNS * @@ -245,6 +275,23 @@ CHIP_ERROR ChipDnssdStopBrowse(intptr_t browseIdentifier); CHIP_ERROR ChipDnssdResolve(DnssdService * browseResult, chip::Inet::InterfaceId interface, DnssdResolveCallback callback, void * context); +#if CHIP_DEVICE_LAYER_TARGET_DARWIN +/** + * This function resolves the services published by mDNS + * + * @param[in] browseResult The service entry returned by @ref ChipDnssdBrowse + * @param[in] interface The interface to send queries. + * @param[in] delegate The delegate to notify when a service is resolved. + * + * @retval CHIP_NO_ERROR The resolve succeeds. + * @retval CHIP_ERROR_INVALID_ARGUMENT The name, type or delegate is nullptr. + * @retval Error code The resolve fails. + * + */ +CHIP_ERROR ChipDnssdResolve(DnssdService * browseResult, chip::Inet::InterfaceId interface, + CommissioningResolveDelegate * delegate); +#endif // CHIP_DEVICE_LAYER_TARGET_DARWIN + /** * This function notifies the implementation that a resolve result is no longer * needed by some consumer, to allow implementations to stop unnecessary resolve diff --git a/src/lib/dnssd/platform/DnssdBrowseDelegate.h b/src/lib/dnssd/platform/DnssdBrowseDelegate.h new file mode 100644 index 00000000000000..8d95a03b8d020e --- /dev/null +++ b/src/lib/dnssd/platform/DnssdBrowseDelegate.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2023 Project CHIP Authors + * All rights reserved. + * + * 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 + +namespace chip { +namespace Dnssd { + +struct DnssdService; + +/** + * A delegate that can be notified of service additions/removals as a mdns browse proceeds. + */ +class DLL_EXPORT DnssdBrowseDelegate +{ +public: + virtual ~DnssdBrowseDelegate() {} + + /** + * @brief + * Called when a service is added. + * + * @param[in] service The service. + */ + virtual void OnBrowseAdd(DnssdService service) = 0; + + /** + * @brief + * Called when a service is removed. + * + * @param[in] service The service. + */ + virtual void OnBrowseRemove(DnssdService service) = 0; + + /** + * @brief + * Called when the browse stops. + * + * @param error Error cause, if any + */ + virtual void OnBrowseStop(CHIP_ERROR error) = 0; +}; + +} // namespace Dnssd +} // namespace chip diff --git a/src/platform/Darwin/BUILD.gn b/src/platform/Darwin/BUILD.gn index 5188068b7a3036..1769d4a29f3576 100644 --- a/src/platform/Darwin/BUILD.gn +++ b/src/platform/Darwin/BUILD.gn @@ -63,6 +63,7 @@ static_library("Darwin") { "DnssdHostNameRegistrar.cpp", "DnssdImpl.cpp", "DnssdImpl.h", + "DnssdType.cpp", "InetPlatformConfig.h", "LoggingImpl.cpp", "MdnsError.cpp", diff --git a/src/platform/Darwin/DnssdContexts.cpp b/src/platform/Darwin/DnssdContexts.cpp index 27bb5140a55651..96ae760c3475a8 100644 --- a/src/platform/Darwin/DnssdContexts.cpp +++ b/src/platform/Darwin/DnssdContexts.cpp @@ -15,17 +15,25 @@ * limitations under the License. */ #include "DnssdImpl.h" +#include "DnssdType.h" #include "MdnsError.h" #include #include using namespace chip::Dnssd; +using namespace chip::Dnssd::Internal; namespace { constexpr uint8_t kDnssdKeyMaxSize = 32; constexpr uint8_t kDnssdTxtRecordMaxEntries = 20; +constexpr const char * kLocalDot = "local."; + +bool IsLocalDomain(const char * domain) +{ + return strcmp(kLocalDot, domain) == 0; +} std::string GetHostNameWithoutDomain(const char * hostnameWithDomain) { @@ -39,17 +47,6 @@ std::string GetHostNameWithoutDomain(const char * hostnameWithDomain) return hostname; } -std::string GetFullTypeWithoutSubTypes(std::string fullType) -{ - size_t position = fullType.find(","); - if (position != std::string::npos) - { - fullType.erase(position); - } - - return fullType; -} - void GetTextEntries(DnssdService & service, const unsigned char * data, uint16_t len) { uint16_t recordCount = TXTRecordGetCount(len, data); @@ -107,6 +104,19 @@ DNSServiceProtocol GetProtocol(const chip::Inet::IPAddressType & addressType) #endif } +DnssdService GetService(const char * name, const char * type, DnssdServiceProtocol protocol, uint32_t interfaceId) +{ + DnssdService service = {}; + service.mInterface = chip::Inet::InterfaceId(interfaceId); + service.mProtocol = protocol; + + auto baseType = GetBaseType(type); + chip::Platform::CopyString(service.mType, baseType.c_str()); + chip::Platform::CopyString(service.mName, name); + + return service; +} + } // namespace namespace chip { @@ -289,6 +299,19 @@ ResolveContext * MdnsContexts::GetExistingResolveForInstanceName(const char * in return nullptr; } +BrowseWithDelegateContext * MdnsContexts::GetExistingBrowseForDelegate(DnssdBrowseDelegate * delegate) +{ + for (auto & ctx : mContexts) + { + if (ctx->type == ContextType::BrowseWithDelegate && (static_cast(ctx))->Matches(delegate)) + { + return static_cast(ctx); + } + } + + return nullptr; +} + RegisterContext::RegisterContext(const char * sType, const char * instanceName, DnssdPublishCallback cb, void * cbContext) { type = ContextType::Register; @@ -348,6 +371,96 @@ void BrowseContext::DispatchPartialSuccess() services.clear(); } +void BrowseContext::OnBrowse(DNSServiceFlags flags, const char * name, const char * type, const char * domain, uint32_t interfaceId) +{ + (flags & kDNSServiceFlagsAdd) ? OnBrowseAdd(name, type, domain, interfaceId) : OnBrowseRemove(name, type, domain, interfaceId); + + if (!(flags & kDNSServiceFlagsMoreComing)) + { + DispatchPartialSuccess(); + } +} + +void BrowseContext::OnBrowseAdd(const char * name, const char * type, const char * domain, uint32_t interfaceId) +{ + ChipLogProgress(Discovery, "Mdns: %s name: %s, type: %s, domain: %s, interface: %" PRIu32, __func__, StringOrNullMarker(name), + StringOrNullMarker(type), StringOrNullMarker(domain), interfaceId); + + VerifyOrReturn(IsLocalDomain(domain)); + auto service = GetService(name, type, protocol, interfaceId); + services.push_back(service); +} + +void BrowseContext::OnBrowseRemove(const char * name, const char * type, const char * domain, uint32_t interfaceId) +{ + ChipLogProgress(Discovery, "Mdns: %s name: %s, type: %s, domain: %s, interface: %" PRIu32, __func__, StringOrNullMarker(name), + StringOrNullMarker(type), StringOrNullMarker(domain), interfaceId); + + VerifyOrReturn(name != nullptr); + VerifyOrReturn(IsLocalDomain(domain)); + + services.erase(std::remove_if(services.begin(), services.end(), + [name, type, interfaceId](const DnssdService & service) { + return strcmp(name, service.mName) == 0 && type == GetFullType(&service) && + service.mInterface == chip::Inet::InterfaceId(interfaceId); + }), + services.end()); +} + +BrowseWithDelegateContext::BrowseWithDelegateContext(DnssdBrowseDelegate * delegate, DnssdServiceProtocol cbContextProtocol) +{ + type = ContextType::BrowseWithDelegate; + context = static_cast(delegate); + protocol = cbContextProtocol; +} + +void BrowseWithDelegateContext::DispatchFailure(const char * errorStr, CHIP_ERROR err) +{ + ChipLogError(Discovery, "Mdns: Browse failure (%s)", errorStr); + + auto delegate = static_cast(context); + delegate->OnBrowseStop(err); + MdnsContexts::GetInstance().Remove(this); +} + +void BrowseWithDelegateContext::DispatchSuccess() +{ + auto delegate = static_cast(context); + delegate->OnBrowseStop(CHIP_NO_ERROR); + MdnsContexts::GetInstance().Remove(this); +} + +void BrowseWithDelegateContext::OnBrowse(DNSServiceFlags flags, const char * name, const char * type, const char * domain, + uint32_t interfaceId) +{ + (flags & kDNSServiceFlagsAdd) ? OnBrowseAdd(name, type, domain, interfaceId) : OnBrowseRemove(name, type, domain, interfaceId); +} + +void BrowseWithDelegateContext::OnBrowseAdd(const char * name, const char * type, const char * domain, uint32_t interfaceId) +{ + ChipLogProgress(Discovery, "Mdns: %s name: %s, type: %s, domain: %s, interface: %" PRIu32, __func__, StringOrNullMarker(name), + StringOrNullMarker(type), StringOrNullMarker(domain), interfaceId); + + VerifyOrReturn(IsLocalDomain(domain)); + + auto delegate = static_cast(context); + auto service = GetService(name, type, protocol, interfaceId); + delegate->OnBrowseAdd(service); +} + +void BrowseWithDelegateContext::OnBrowseRemove(const char * name, const char * type, const char * domain, uint32_t interfaceId) +{ + ChipLogProgress(Discovery, "Mdns: %s name: %s, type: %s, domain: %s, interface: %" PRIu32, __func__, StringOrNullMarker(name), + StringOrNullMarker(type), StringOrNullMarker(domain), interfaceId); + + VerifyOrReturn(name != nullptr); + VerifyOrReturn(IsLocalDomain(domain)); + + auto delegate = static_cast(context); + auto service = GetService(name, type, protocol, interfaceId); + delegate->OnBrowseRemove(service); +} + ResolveContext::ResolveContext(void * cbContext, DnssdResolveCallback cb, chip::Inet::IPAddressType cbAddressType, const char * instanceNameToResolve, BrowseContext * browseCausingResolve, std::shared_ptr && consumerCounterToUse) : @@ -361,6 +474,18 @@ ResolveContext::ResolveContext(void * cbContext, DnssdResolveCallback cb, chip:: consumerCounter = std::move(consumerCounterToUse); } +ResolveContext::ResolveContext(CommissioningResolveDelegate * delegate, chip::Inet::IPAddressType cbAddressType, + const char * instanceNameToResolve, std::shared_ptr && consumerCounterToUse) : + browseThatCausedResolve(nullptr) +{ + type = ContextType::Resolve; + context = delegate; + callback = nullptr; + protocol = GetProtocol(cbAddressType); + instanceName = instanceNameToResolve; + consumerCounter = std::move(consumerCounterToUse); +} + ResolveContext::~ResolveContext() {} void ResolveContext::DispatchFailure(const char * errorStr, CHIP_ERROR err) @@ -370,7 +495,14 @@ void ResolveContext::DispatchFailure(const char * errorStr, CHIP_ERROR err) // ChipDnssdResolveNoLongerNeeded don't find us and try to also remove us. bool needDelete = MdnsContexts::GetInstance().RemoveWithoutDeleting(this); - callback(context, nullptr, Span(), err); + if (nullptr == callback) + { + // Nothing to do. + } + else + { + callback(context, nullptr, Span(), err); + } if (needDelete) { @@ -395,7 +527,20 @@ void ResolveContext::DispatchSuccess() } ChipLogProgress(Discovery, "Mdns: Resolve success on interface %" PRIu32, interface.first); - callback(context, &interface.second.service, Span(ips.data(), ips.size()), CHIP_NO_ERROR); + + auto & service = interface.second.service; + auto addresses = Span(ips.data(), ips.size()); + if (nullptr == callback) + { + auto delegate = static_cast(context); + DiscoveredNodeData nodeData; + service.ToDiscoveredNodeData(addresses, nodeData); + delegate->OnNodeDiscovered(nodeData); + } + else + { + callback(context, &service, addresses, CHIP_NO_ERROR); + } break; } diff --git a/src/platform/Darwin/DnssdImpl.cpp b/src/platform/Darwin/DnssdImpl.cpp index c3e839e3308249..d5cf5d554e7db7 100644 --- a/src/platform/Darwin/DnssdImpl.cpp +++ b/src/platform/Darwin/DnssdImpl.cpp @@ -15,13 +15,11 @@ * limitations under the License. */ #include "DnssdImpl.h" +#include "DnssdType.h" #include "MdnsError.h" #include -#include -#include -#include #include #include #include @@ -29,12 +27,11 @@ #include using namespace chip::Dnssd; +using namespace chip::Dnssd::Internal; namespace { -constexpr const char * kLocalDot = "local."; -constexpr const char * kProtocolTcp = "._tcp"; -constexpr const char * kProtocolUdp = "._udp"; +constexpr const char * kLocalDot = "local."; constexpr DNSServiceFlags kRegisterFlags = kDNSServiceFlagsNoAutoRename; constexpr DNSServiceFlags kBrowseFlags = 0; @@ -52,51 +49,6 @@ uint32_t GetInterfaceId(chip::Inet::InterfaceId interfaceId) return interfaceId.IsPresent() ? interfaceId.GetPlatformInterface() : kDNSServiceInterfaceIndexAny; } -std::string GetFullType(const char * type, DnssdServiceProtocol protocol) -{ - std::ostringstream typeBuilder; - typeBuilder << type; - typeBuilder << (protocol == DnssdServiceProtocol::kDnssdProtocolUdp ? kProtocolUdp : kProtocolTcp); - return typeBuilder.str(); -} - -std::string GetFullType(const DnssdService * service) -{ - return GetFullType(service->mType, service->mProtocol); -} - -std::string GetFullTypeWithSubTypes(const char * type, DnssdServiceProtocol protocol, const char * subTypes[], size_t subTypeSize) -{ - std::ostringstream typeBuilder; - typeBuilder << type; - typeBuilder << (protocol == DnssdServiceProtocol::kDnssdProtocolUdp ? kProtocolUdp : kProtocolTcp); - for (int i = 0; i < (int) subTypeSize; i++) - { - typeBuilder << ","; - typeBuilder << subTypes[i]; - } - return typeBuilder.str(); -} - -std::string GetFullTypeWithSubTypes(const char * type, DnssdServiceProtocol protocol) -{ - auto fullType = GetFullType(type, protocol); - - std::string subtypeDelimiter = "._sub."; - size_t position = fullType.find(subtypeDelimiter); - if (position != std::string::npos) - { - fullType = fullType.substr(position + subtypeDelimiter.size()) + "," + fullType.substr(0, position); - } - - return fullType; -} - -std::string GetFullTypeWithSubTypes(const DnssdService * service) -{ - return GetFullTypeWithSubTypes(service->mType, service->mProtocol, service->mSubTypes, service->mSubTypeSize); -} - std::string GetHostNameWithDomain(const char * hostname) { return std::string(hostname) + '.' + kLocalDot; @@ -159,6 +111,21 @@ class ScopedTXTRecord char mRecordBuffer[kDnssdTextMaxSize]; }; +std::shared_ptr GetCounterHolder(const char * name) +{ + // This is a little silly, in that resolves for the same name, type, etc get + // coalesced by the underlying mDNSResponder anyway. But we need to keep + // track of our context/callback/etc, (even though in practice it's always + // exactly the same) and the interface id (which might actually be different + // for different Resolve calls). So for now just keep using a + // ResolveContext to track all that. + if (auto existingCtx = MdnsContexts::GetInstance().GetExistingResolveForInstanceName(name)) + { + return existingCtx->consumerCounter; + } + return std::make_shared(0); +} + } // namespace namespace chip { @@ -206,62 +173,22 @@ CHIP_ERROR Register(void * context, DnssdPublishCallback callback, uint32_t inte return MdnsContexts::GetInstance().Add(sdCtx, sdRef); } -void OnBrowseAdd(BrowseContext * context, const char * name, const char * type, const char * domain, uint32_t interfaceId) -{ - ChipLogProgress(Discovery, "Mdns: %s name: %s, type: %s, domain: %s, interface: %" PRIu32, __func__, StringOrNullMarker(name), - StringOrNullMarker(type), StringOrNullMarker(domain), interfaceId); - - VerifyOrReturn(strcmp(kLocalDot, domain) == 0); - - DnssdService service = {}; - service.mInterface = Inet::InterfaceId(interfaceId); - service.mProtocol = context->protocol; - - Platform::CopyString(service.mName, name); - Platform::CopyString(service.mType, type); - - // only the first token after '.' should be included in the type - for (char * p = service.mType; *p != '\0'; p++) - { - if (*p == '.') - { - *p = '\0'; - break; - } - } - - context->services.push_back(service); -} - -void OnBrowseRemove(BrowseContext * context, const char * name, const char * type, const char * domain, uint32_t interfaceId) -{ - ChipLogProgress(Discovery, "Mdns: %s name: %s, type: %s, domain: %s, interface: %" PRIu32, __func__, StringOrNullMarker(name), - StringOrNullMarker(type), StringOrNullMarker(domain), interfaceId); - - VerifyOrReturn(name != nullptr); - VerifyOrReturn(strcmp(kLocalDot, domain) == 0); - - context->services.erase(std::remove_if(context->services.begin(), context->services.end(), - [name, type, interfaceId](const DnssdService & service) { - return strcmp(name, service.mName) == 0 && type == GetFullType(&service) && - service.mInterface == Inet::InterfaceId(interfaceId); - }), - context->services.end()); -} - static void OnBrowse(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceId, DNSServiceErrorType err, const char * name, const char * type, const char * domain, void * context) { - auto sdCtx = reinterpret_cast(context); + auto sdCtx = reinterpret_cast(context); VerifyOrReturn(kDNSServiceErr_NoError == err, sdCtx->Finalize(err)); + sdCtx->OnBrowse(flags, name, type, domain, interfaceId); +} - (flags & kDNSServiceFlagsAdd) ? OnBrowseAdd(sdCtx, name, type, domain, interfaceId) - : OnBrowseRemove(sdCtx, name, type, domain, interfaceId); +CHIP_ERROR Browse(BrowseHandler * sdCtx, uint32_t interfaceId, const char * type) +{ + ChipLogProgress(Discovery, "Browsing for: %s", StringOrNullMarker(type)); + DNSServiceRef sdRef; + auto err = DNSServiceBrowse(&sdRef, kBrowseFlags, interfaceId, type, kLocalDot, OnBrowse, sdCtx); + VerifyOrReturnError(kDNSServiceErr_NoError == err, sdCtx->Finalize(err)); - if (!(flags & kDNSServiceFlagsMoreComing)) - { - sdCtx->DispatchPartialSuccess(); - } + return MdnsContexts::GetInstance().Add(sdCtx, sdRef); } CHIP_ERROR Browse(void * context, DnssdBrowseCallback callback, uint32_t interfaceId, const char * type, @@ -270,16 +197,19 @@ CHIP_ERROR Browse(void * context, DnssdBrowseCallback callback, uint32_t interfa auto sdCtx = chip::Platform::New(context, callback, protocol); VerifyOrReturnError(nullptr != sdCtx, CHIP_ERROR_NO_MEMORY); - ChipLogProgress(Discovery, "Browsing for: %s", StringOrNullMarker(type)); - DNSServiceRef sdRef; - auto err = DNSServiceBrowse(&sdRef, kBrowseFlags, interfaceId, type, kLocalDot, OnBrowse, sdCtx); - VerifyOrReturnError(kDNSServiceErr_NoError == err, sdCtx->Finalize(err)); - - ReturnErrorOnFailure(MdnsContexts::GetInstance().Add(sdCtx, sdRef)); + ReturnErrorOnFailure(Browse(sdCtx, interfaceId, type)); *browseIdentifier = reinterpret_cast(sdCtx); return CHIP_NO_ERROR; } +CHIP_ERROR Browse(DnssdBrowseDelegate * delegate, uint32_t interfaceId, const char * type, DnssdServiceProtocol protocol) +{ + auto sdCtx = chip::Platform::New(delegate, protocol); + VerifyOrReturnError(nullptr != sdCtx, CHIP_ERROR_NO_MEMORY); + + return Browse(sdCtx, interfaceId, type); +} + static void OnGetAddrInfo(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceId, DNSServiceErrorType err, const char * hostname, const struct sockaddr * address, uint32_t ttl, void * context) { @@ -337,32 +267,12 @@ static void OnResolve(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t inter } } -static CHIP_ERROR Resolve(void * context, DnssdResolveCallback callback, uint32_t interfaceId, - chip::Inet::IPAddressType addressType, const char * type, const char * name) +static CHIP_ERROR Resolve(ResolveContext * sdCtx, uint32_t interfaceId, chip::Inet::IPAddressType addressType, const char * type, + const char * name) { ChipLogProgress(Discovery, "Resolve type=%s name=%s interface=%" PRIu32, StringOrNullMarker(type), StringOrNullMarker(name), interfaceId); - // This is a little silly, in that resolves for the same name, type, etc get - // coalesced by the underlying mDNSResponder anyway. But we need to keep - // track of our context/callback/etc, (even though in practice it's always - // exactly the same) and the interface id (which might actually be different - // for different Resolve calls). So for now just keep using a - // ResolveContext to track all that. - std::shared_ptr counterHolder; - if (auto existingCtx = MdnsContexts::GetInstance().GetExistingResolveForInstanceName(name)) - { - counterHolder = existingCtx->consumerCounter; - } - else - { - counterHolder = std::make_shared(0); - } - - auto sdCtx = chip::Platform::New(context, callback, addressType, name, - BrowseContext::sContextDispatchingSuccess, std::move(counterHolder)); - VerifyOrReturnError(nullptr != sdCtx, CHIP_ERROR_NO_MEMORY); - auto err = DNSServiceCreateConnection(&sdCtx->serviceRef); VerifyOrReturnError(kDNSServiceErr_NoError == err, sdCtx->Finalize(err)); @@ -370,7 +280,7 @@ static CHIP_ERROR Resolve(void * context, DnssdResolveCallback callback, uint32_ err = DNSServiceResolve(&sdRefCopy, kResolveFlags, interfaceId, name, type, kLocalDot, OnResolve, sdCtx); VerifyOrReturnError(kDNSServiceErr_NoError == err, sdCtx->Finalize(err)); - CHIP_ERROR retval = MdnsContexts::GetInstance().Add(sdCtx, sdCtx->serviceRef); + auto retval = MdnsContexts::GetInstance().Add(sdCtx, sdCtx->serviceRef); if (retval == CHIP_NO_ERROR) { (*(sdCtx->consumerCounter))++; @@ -378,6 +288,27 @@ static CHIP_ERROR Resolve(void * context, DnssdResolveCallback callback, uint32_ return retval; } +static CHIP_ERROR Resolve(void * context, DnssdResolveCallback callback, uint32_t interfaceId, + chip::Inet::IPAddressType addressType, const char * type, const char * name) +{ + auto counterHolder = GetCounterHolder(name); + auto sdCtx = chip::Platform::New(context, callback, addressType, name, + BrowseContext::sContextDispatchingSuccess, std::move(counterHolder)); + VerifyOrReturnError(nullptr != sdCtx, CHIP_ERROR_NO_MEMORY); + + return Resolve(sdCtx, interfaceId, addressType, type, name); +} + +static CHIP_ERROR Resolve(CommissioningResolveDelegate * delegate, uint32_t interfaceId, chip::Inet::IPAddressType addressType, + const char * type, const char * name) +{ + auto counterHolder = GetCounterHolder(name); + auto sdCtx = chip::Platform::New(delegate, addressType, name, std::move(counterHolder)); + VerifyOrReturnError(nullptr != sdCtx, CHIP_ERROR_NO_MEMORY); + + return Resolve(sdCtx, interfaceId, addressType, type, name); +} + } // namespace CHIP_ERROR ChipDnssdInit(DnssdAsyncReturnCallback successCallback, DnssdAsyncReturnCallback errorCallback, void * context) @@ -482,6 +413,29 @@ CHIP_ERROR ChipDnssdStopBrowse(intptr_t browseIdentifier) return CHIP_NO_ERROR; } +CHIP_ERROR ChipDnssdBrowse(const char * type, DnssdServiceProtocol protocol, chip::Inet::IPAddressType addressType, + chip::Inet::InterfaceId interface, DnssdBrowseDelegate * delegate) +{ + VerifyOrReturnError(type != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(IsSupportedProtocol(protocol), CHIP_ERROR_INVALID_ARGUMENT); + + auto regtype = GetFullTypeWithSubTypes(type, protocol); + auto interfaceId = GetInterfaceId(interface); + return Browse(delegate, interfaceId, regtype.c_str(), protocol); +} + +CHIP_ERROR ChipDnssdStopBrowse(DnssdBrowseDelegate * delegate) +{ + auto existingCtx = MdnsContexts::GetInstance().GetExistingBrowseForDelegate(delegate); + if (existingCtx == nullptr) + { + return CHIP_ERROR_NOT_FOUND; + } + + return existingCtx->Finalize(kDNSServiceErr_NoError); +} + CHIP_ERROR ChipDnssdResolve(DnssdService * service, chip::Inet::InterfaceId interface, DnssdResolveCallback callback, void * context) { @@ -493,6 +447,16 @@ CHIP_ERROR ChipDnssdResolve(DnssdService * service, chip::Inet::InterfaceId inte return Resolve(context, callback, interfaceId, service->mAddressType, regtype.c_str(), service->mName); } +CHIP_ERROR ChipDnssdResolve(DnssdService * service, chip::Inet::InterfaceId interface, CommissioningResolveDelegate * delegate) +{ + VerifyOrReturnError(service != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(IsSupportedProtocol(service->mProtocol), CHIP_ERROR_INVALID_ARGUMENT); + + auto regtype = GetFullType(service); + auto interfaceId = GetInterfaceId(interface); + return Resolve(delegate, interfaceId, service->mAddressType, regtype.c_str(), service->mName); +} + void ChipDnssdResolveNoLongerNeeded(const char * instanceName) { ChipLogProgress(Discovery, "No longer need resolve for %s", instanceName); diff --git a/src/platform/Darwin/DnssdImpl.h b/src/platform/Darwin/DnssdImpl.h index 9b624bae73e4f3..1dd6319222599e 100644 --- a/src/platform/Darwin/DnssdImpl.h +++ b/src/platform/Darwin/DnssdImpl.h @@ -33,6 +33,7 @@ enum class ContextType { Register, Browse, + BrowseWithDelegate, Resolve, }; @@ -54,6 +55,7 @@ struct GenericContext CHIP_ERROR FinalizeInternal(const char * errorStr, CHIP_ERROR err); }; +struct BrowseWithDelegateContext; struct RegisterContext; struct ResolveContext; @@ -93,6 +95,12 @@ class MdnsContexts */ ResolveContext * GetExistingResolveForInstanceName(const char * instanceName); + /** + * Return a pointer to an existing BrowserWithDelegateContext for the given + * delegate, if any. Returns nullptr if there are none. + */ + BrowseWithDelegateContext * GetExistingBrowseForDelegate(DnssdBrowseDelegate * delegate); + /** * Remove context from the list, if it's present in the list. Return * whether it was present. @@ -141,14 +149,24 @@ struct RegisterContext : public GenericContext bool matches(const char * sType) { return mType.compare(sType) == 0; } }; -struct BrowseContext : public GenericContext +struct BrowseHandler : public GenericContext +{ + virtual ~BrowseHandler() {} + + DnssdServiceProtocol protocol; + + virtual void OnBrowse(DNSServiceFlags flags, const char * name, const char * type, const char * domain, + uint32_t interfaceId) = 0; + virtual void OnBrowseAdd(const char * name, const char * type, const char * domain, uint32_t interfaceId) = 0; + virtual void OnBrowseRemove(const char * name, const char * type, const char * domain, uint32_t interfaceId) = 0; +}; + +struct BrowseContext : public BrowseHandler { DnssdBrowseCallback callback; std::vector services; - DnssdServiceProtocol protocol; BrowseContext(void * cbContext, DnssdBrowseCallback cb, DnssdServiceProtocol cbContextProtocol); - virtual ~BrowseContext() {} void DispatchFailure(const char * errorStr, CHIP_ERROR err) override; void DispatchSuccess() override; @@ -156,6 +174,10 @@ struct BrowseContext : public GenericContext // Dispatch what we have found so far, but don't stop browsing. void DispatchPartialSuccess(); + void OnBrowse(DNSServiceFlags flags, const char * name, const char * type, const char * domain, uint32_t interfaceId) override; + void OnBrowseAdd(const char * name, const char * type, const char * domain, uint32_t interfaceId) override; + void OnBrowseRemove(const char * name, const char * type, const char * domain, uint32_t interfaceId) override; + // While we are dispatching partial success, sContextDispatchingSuccess will // be set to the BrowseContext doing the dispatch. This allows resolves // triggered by the browse dispatch to be associated with the browse. This @@ -171,6 +193,20 @@ struct BrowseContext : public GenericContext static BrowseContext * sContextDispatchingSuccess; }; +struct BrowseWithDelegateContext : public BrowseHandler +{ + BrowseWithDelegateContext(DnssdBrowseDelegate * delegate, DnssdServiceProtocol cbContextProtocol); + + void DispatchFailure(const char * errorStr, CHIP_ERROR err) override; + void DispatchSuccess() override; + + void OnBrowse(DNSServiceFlags flags, const char * name, const char * type, const char * domain, uint32_t interfaceId) override; + void OnBrowseAdd(const char * name, const char * type, const char * domain, uint32_t interfaceId) override; + void OnBrowseRemove(const char * name, const char * type, const char * domain, uint32_t interfaceId) override; + + bool Matches(DnssdBrowseDelegate * otherDelegate) const { return context == otherDelegate; } +}; + struct InterfaceInfo { InterfaceInfo(); @@ -198,6 +234,8 @@ struct ResolveContext : public GenericContext ResolveContext(void * cbContext, DnssdResolveCallback cb, chip::Inet::IPAddressType cbAddressType, const char * instanceNameToResolve, BrowseContext * browseCausingResolve, std::shared_ptr && consumerCounterToUse); + ResolveContext(CommissioningResolveDelegate * delegate, chip::Inet::IPAddressType cbAddressType, + const char * instanceNameToResolve, std::shared_ptr && consumerCounterToUse); virtual ~ResolveContext(); void DispatchFailure(const char * errorStr, CHIP_ERROR err) override; diff --git a/src/platform/Darwin/DnssdType.cpp b/src/platform/Darwin/DnssdType.cpp new file mode 100644 index 00000000000000..e113fa45ff2029 --- /dev/null +++ b/src/platform/Darwin/DnssdType.cpp @@ -0,0 +1,99 @@ +/* + * + * Copyright (c) 2021-2023 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 "DnssdType.h" + +#include + +constexpr const char * kProtocolTcp = "._tcp"; +constexpr const char * kProtocolUdp = "._udp"; + +namespace chip { +namespace Dnssd { +namespace Internal { + +std::string GetFullType(const char * type, DnssdServiceProtocol protocol) +{ + std::ostringstream typeBuilder; + typeBuilder << type; + typeBuilder << (protocol == DnssdServiceProtocol::kDnssdProtocolUdp ? kProtocolUdp : kProtocolTcp); + return typeBuilder.str(); +} + +std::string GetFullType(const DnssdService * service) +{ + return GetFullType(service->mType, service->mProtocol); +} + +std::string GetFullTypeWithSubTypes(const char * type, DnssdServiceProtocol protocol) +{ + auto fullType = GetFullType(type, protocol); + + std::string subtypeDelimiter = "._sub."; + size_t position = fullType.find(subtypeDelimiter); + if (position != std::string::npos) + { + fullType = fullType.substr(position + subtypeDelimiter.size()) + "," + fullType.substr(0, position); + } + + return fullType; +} + +std::string GetFullTypeWithSubTypes(const char * type, DnssdServiceProtocol protocol, const char * subTypes[], size_t subTypeSize) +{ + std::ostringstream typeBuilder; + typeBuilder << type; + typeBuilder << (protocol == DnssdServiceProtocol::kDnssdProtocolUdp ? kProtocolUdp : kProtocolTcp); + for (int i = 0; i < (int) subTypeSize; i++) + { + typeBuilder << ","; + typeBuilder << subTypes[i]; + } + return typeBuilder.str(); +} + +std::string GetFullTypeWithSubTypes(const DnssdService * service) +{ + return GetFullTypeWithSubTypes(service->mType, service->mProtocol, service->mSubTypes, service->mSubTypeSize); +} + +std::string GetFullTypeWithoutSubTypes(std::string fullType) +{ + size_t position = fullType.find(","); + if (position != std::string::npos) + { + fullType.erase(position); + } + + return fullType; +} + +std::string GetBaseType(const char * fulltype) +{ + std::string type(fulltype); + size_t position = type.find("."); + if (position != std::string::npos) + { + type.erase(position); + } + + return type; +} + +} // namespace Internal +} // namespace Dnssd +} // namespace chip diff --git a/src/platform/Darwin/DnssdType.h b/src/platform/Darwin/DnssdType.h new file mode 100644 index 00000000000000..c5904beaf03ef4 --- /dev/null +++ b/src/platform/Darwin/DnssdType.h @@ -0,0 +1,40 @@ +/* + * + * Copyright (c) 2021-2023 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 +#include + +namespace chip { +namespace Dnssd { +namespace Internal { + +std::string GetFullType(const char * type, DnssdServiceProtocol protocol); +std::string GetFullType(const DnssdService * service); + +std::string GetFullTypeWithSubTypes(const DnssdService * service); +std::string GetFullTypeWithSubTypes(const char * type, DnssdServiceProtocol protocol); +std::string GetFullTypeWithSubTypes(const char * type, DnssdServiceProtocol protocol, const char * subTypes[], size_t subTypeSize); + +std::string GetFullTypeWithoutSubTypes(std::string fullType); + +std::string GetBaseType(const char * fulltype); + +} // namespace Internal +} // namespace Dnssd +} // namespace chip