From 12965205fe66cd825ece1c7caa0d670d1ccc89e6 Mon Sep 17 00:00:00 2001 From: Kamil Kasperczyk <66371704+kkasperczyk-no@users.noreply.github.com> Date: Tue, 13 Jul 2021 11:15:30 +0200 Subject: [PATCH] Add support for accessory DNS browse and resolve on Thread devices. (#8132) * Add support for accessory DNS browse and resolve on Thread devices. Currently Thread platform doesn't provide mDNS browse and resolve implementation. Moreover there is no way to trigger browse or resolve queries on accessory devices to perform operational discovery from accessory device perspective. For all: * Added set of dns shell commands allowing to invoke dns browse and dns resolve commands. For Thread: * Implemented ChipMdnsBrowse and ChipMdnsResolve. * Implemented ThreadStackMgr DnsResolve and DnsBrowse commands using OpenThread API for performing DNS queries. * Changed SRP service buffers sizes to allow handling Commissionable Discovery records (currently it was assumed to support only Operational Discovery and sizes are not big enough). * Added configs allowing to decide whether Thread DNS client and Thread Commissionable Discovery should be enabled (to avoid potential memory consumption problems on different platforms as Commissionable Discovery data are pretty heavy). For nrfconnect: * Enabled DNS client and commissionable discovery support by default. * Extended shell commands documentation about dns. What this change doesn't introduce: * Support for commissionable discovery on Thread devices, it only allows for handling commissionable discovery records responses when performing dns browse. * DNS browsing/resolving support on platforms other than nrfconnect, as it needs to be enabled individually by setting CHIP_DEVICE_CONFIG_ENABLE_THREAD_DNS_CLIENT * Addressed review comments --- config/nrfconnect/app/sample-defaults.conf | 1 - config/zephyr/Kconfig | 7 + docs/guides/nrfconnect_examples_cli.md | 39 +++ src/include/platform/CHIPDeviceConfig.h | 18 ++ src/include/platform/ThreadStackManager.h | 33 ++- src/lib/mdns/Discovery_ImplPlatform.cpp | 10 +- src/lib/shell/Commands.h | 6 + src/lib/shell/Engine.cpp | 3 + src/lib/shell/commands/BUILD.gn | 4 + src/lib/shell/commands/Dns.cpp | 141 ++++++++++ ...nericThreadStackManagerImpl_OpenThread.cpp | 253 +++++++++++++++++- ...GenericThreadStackManagerImpl_OpenThread.h | 70 ++++- src/platform/OpenThread/MdnsImpl.cpp | 32 +++ .../nrfconnect/CHIPDevicePlatformConfig.h | 4 + 14 files changed, 608 insertions(+), 13 deletions(-) create mode 100644 src/lib/shell/commands/Dns.cpp diff --git a/config/nrfconnect/app/sample-defaults.conf b/config/nrfconnect/app/sample-defaults.conf index f9c65c06d0bb5d..28f95614f7fe9e 100644 --- a/config/nrfconnect/app/sample-defaults.conf +++ b/config/nrfconnect/app/sample-defaults.conf @@ -66,7 +66,6 @@ CONFIG_OPENTHREAD_FTD=n CONFIG_OPENTHREAD_SLAAC=y CONFIG_OPENTHREAD_DHCP6_CLIENT=y CONFIG_OPENTHREAD_SNTP_CLIENT=y -CONFIG_OPENTHREAD_DNS_CLIENT=y CONFIG_OPENTHREAD_MTD_NETDIAG=y CONFIG_OPENTHREAD_ENABLE_SERVICE=y CONFIG_OPENTHREAD_MANUAL_START=y diff --git a/config/zephyr/Kconfig b/config/zephyr/Kconfig index 32f548f455bd84..30ce4da510ba20 100644 --- a/config/zephyr/Kconfig +++ b/config/zephyr/Kconfig @@ -86,6 +86,13 @@ config CHIP_ENABLE_DNSSD_SRP help Enables DNS-SD SRP client support +config CHIP_ENABLE_DNS_CLIENT + bool "Enable support for DNS client" + default y + imply OPENTHREAD_DNS_CLIENT + help + Enables DNS client support used for resolving and browsing services. + config APP_LINK_WITH_CHIP bool "Link 'app' with Connected Home over IP" default y diff --git a/docs/guides/nrfconnect_examples_cli.md b/docs/guides/nrfconnect_examples_cli.md index 3b4e75e79aa8d8..01cd09aed2ad9e 100644 --- a/docs/guides/nrfconnect_examples_cli.md +++ b/docs/guides/nrfconnect_examples_cli.md @@ -307,3 +307,42 @@ Prints the information about the NFC tag emulation status. uart:~$ matter nfc state NFC tag emulation is disabled ``` + +### dns + +Handles a group of commands that are used to trigger performing DNS queries. You +must use this command together with one of the additional subcommands listed +below. + +#### browse + +Browses for DNS services of `_matterc_udp` type and prints the received +response. Takes no argument. + +```shell +uart:~$ matter dns browse +Browsing ... +DNS browse succeeded: + Hostname: 0E824F0CA6DE309C + Vendor ID: 9050 + Product ID: 20043 + Long discriminator: 3840 + Device type: 0 + Device name: + Commissioning mode: 0 + IP addresses: + fd08:b65e:db8e:f9c7:2cc2:2043:1366:3b31 +``` + +#### resolve + +Resolves the specified Matter node service given by the and +. + +```shell +uart:~$ matter dns resolve +Resolving ... +DNS resolve for 000000014A77CBB3-0000000000BC5C01 succeeded: + IP address: fd08:b65e:db8e:f9c7:8052:1a8e:4dd4:e1f3 + Port: 11097 +``` diff --git a/src/include/platform/CHIPDeviceConfig.h b/src/include/platform/CHIPDeviceConfig.h index ad1fa207a66dd8..a475df7d1a7f4a 100644 --- a/src/include/platform/CHIPDeviceConfig.h +++ b/src/include/platform/CHIPDeviceConfig.h @@ -659,6 +659,24 @@ #define CHIP_DEVICE_CONFIG_THREAD_SRP_MAX_SERVICES 3 #endif +/** + * CHIP_DEVICE_CONFIG_ENABLE_THREAD_COMMISSIONABLE_DISCOVERY + * + * Enable support to Commissionable Discovery for Thread devices. + */ +#ifndef CHIP_DEVICE_CONFIG_ENABLE_THREAD_COMMISSIONABLE_DISCOVERY +#define CHIP_DEVICE_CONFIG_ENABLE_THREAD_COMMISSIONABLE_DISCOVERY 0 +#endif + +/** + * CHIP_DEVICE_CONFIG_ENABLE_THREAD_DNS_CLIENT + * + * Enable support to DNS client usage for resolving and browsing services in CHIP. + */ +#ifndef CHIP_DEVICE_CONFIG_ENABLE_THREAD_DNS_CLIENT +#define CHIP_DEVICE_CONFIG_ENABLE_THREAD_DNS_CLIENT 0 +#endif + // -------------------- Thread Configuration -------------------- /** diff --git a/src/include/platform/ThreadStackManager.h b/src/include/platform/ThreadStackManager.h index e78e89530674eb..d253518ca584de 100644 --- a/src/include/platform/ThreadStackManager.h +++ b/src/include/platform/ThreadStackManager.h @@ -29,7 +29,8 @@ namespace chip { namespace Mdns { struct TextEntry; -} +struct MdnsService; +} // namespace Mdns namespace DeviceLayer { @@ -56,6 +57,12 @@ template class GenericThreadStackManagerImpl_FreeRTOS; } // namespace Internal +#if CHIP_DEVICE_CONFIG_ENABLE_THREAD_DNS_CLIENT +// Declaration of callback types corresponding to MdnsResolveCallback and MdnsBrowseCallback to avoid circular including. +using DnsResolveCallback = void (*)(void * context, chip::Mdns::MdnsService * result, CHIP_ERROR error); +using DnsBrowseCallback = void (*)(void * context, chip::Mdns::MdnsService * services, size_t servicesSize, CHIP_ERROR error); +#endif // CHIP_DEVICE_CONFIG_ENABLE_THREAD_DNS_CLIENT + /** * Provides features for initializing and interacting with the Thread stack on * a chip-enabled device. @@ -86,11 +93,17 @@ class ThreadStackManager CHIP_ERROR SetThreadEnabled(bool val); #if CHIP_DEVICE_CONFIG_ENABLE_THREAD_SRP_CLIENT - CHIP_ERROR AddSrpService(const char * aInstanceName, const char * aName, uint16_t aPort, chip::Mdns::TextEntry * aTxtEntries, - size_t aTxtEntiresSize, uint32_t aLeaseInterval, uint32_t aKeyLeaseInterval); + CHIP_ERROR + AddSrpService(const char * aInstanceName, const char * aName, uint16_t aPort, chip::Mdns::TextEntry * aTxtEntries, + size_t aTxtEntiresSize, uint32_t aLeaseInterval, uint32_t aKeyLeaseInterval); CHIP_ERROR RemoveSrpService(const char * aInstanceName, const char * aName); CHIP_ERROR RemoveAllSrpServices(); CHIP_ERROR SetupSrpHost(const char * aHostName); + +#if CHIP_DEVICE_CONFIG_ENABLE_THREAD_DNS_CLIENT + CHIP_ERROR DnsBrowse(const char * aServiceName, DnsBrowseCallback aCallback, void * aContext); + CHIP_ERROR DnsResolve(const char * aServiceName, const char * aInstanceName, DnsResolveCallback aCallback, void * aContext); +#endif // CHIP_DEVICE_CONFIG_ENABLE_THREAD_DNS_CLIENT #endif // CHIP_DEVICE_CONFIG_ENABLE_THREAD_SRP_CLIENT private: @@ -249,6 +262,20 @@ inline CHIP_ERROR ThreadStackManager::SetupSrpHost(const char * aHostName) { return static_cast(this)->_SetupSrpHost(aHostName); } + +#if CHIP_DEVICE_CONFIG_ENABLE_THREAD_DNS_CLIENT +inline CHIP_ERROR ThreadStackManager::DnsBrowse(const char * aServiceName, DnsBrowseCallback aCallback, void * aContext) +{ + return static_cast(this)->_DnsBrowse(aServiceName, aCallback, aContext); +} + +inline CHIP_ERROR ThreadStackManager::DnsResolve(const char * aServiceName, const char * aInstanceName, + DnsResolveCallback aCallback, void * aContext) +{ + return static_cast(this)->_DnsResolve(aServiceName, aInstanceName, aCallback, aContext); +} + +#endif // CHIP_DEVICE_CONFIG_ENABLE_THREAD_DNS_CLIENT #endif // CHIP_DEVICE_CONFIG_ENABLE_THREAD_SRP_CLIENT inline bool ThreadStackManager::IsThreadProvisioned() diff --git a/src/lib/mdns/Discovery_ImplPlatform.cpp b/src/lib/mdns/Discovery_ImplPlatform.cpp index 5ea0345fad056f..3a500eaac9a213 100644 --- a/src/lib/mdns/Discovery_ImplPlatform.cpp +++ b/src/lib/mdns/Discovery_ImplPlatform.cpp @@ -450,7 +450,15 @@ void DiscoveryImplPlatform::HandleNodeBrowse(void * context, MdnsService * servi { for (size_t i = 0; i < servicesSize; ++i) { - ChipMdnsResolve(&services[i], INET_NULL_INTERFACEID, HandleNodeResolve, context); + // For some platforms browsed services are already resolved, so verify if resolve is really needed or call resolve callback + if (!services[i].mAddress.HasValue()) + { + ChipMdnsResolve(&services[i], services[i].mInterface, HandleNodeResolve, context); + } + else + { + HandleNodeResolve(context, &services[i], error); + } } } diff --git a/src/lib/shell/Commands.h b/src/lib/shell/Commands.h index 84a325b4279f6c..24bfe62a5c5182 100644 --- a/src/lib/shell/Commands.h +++ b/src/lib/shell/Commands.h @@ -68,5 +68,11 @@ void RegisterWiFiCommands(); */ void RegisterNFCCommands(); +/** + * This function registers the DNS client commands. + * + */ +void RegisterDnsCommands(); + } // namespace Shell } // namespace chip diff --git a/src/lib/shell/Engine.cpp b/src/lib/shell/Engine.cpp index d57252a8c3b68b..583e08196a3434 100644 --- a/src/lib/shell/Engine.cpp +++ b/src/lib/shell/Engine.cpp @@ -111,6 +111,9 @@ void Engine::RegisterDefaultCommands() #if CHIP_DEVICE_CONFIG_ENABLE_NFC RegisterNFCCommands(); #endif +#if CHIP_DEVICE_CONFIG_ENABLE_MDNS + RegisterDnsCommands(); +#endif } } // namespace Shell diff --git a/src/lib/shell/commands/BUILD.gn b/src/lib/shell/commands/BUILD.gn index b4ec8c1efabc2d..83cf85cf15d560 100644 --- a/src/lib/shell/commands/BUILD.gn +++ b/src/lib/shell/commands/BUILD.gn @@ -45,6 +45,10 @@ source_set("commands") { sources += [ "NFC.cpp" ] } + if (chip_mdns != "none" && chip_device_platform != "none") { + sources += [ "Dns.cpp" ] + } + public_deps = [ "${chip_root}/src/lib/shell:shell_core" ] if (chip_device_platform != "none") { diff --git a/src/lib/shell/commands/Dns.cpp b/src/lib/shell/commands/Dns.cpp new file mode 100644 index 00000000000000..b6cc208d768080 --- /dev/null +++ b/src/lib/shell/commands/Dns.cpp @@ -0,0 +1,141 @@ +/* + * + * Copyright (c) 2021 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace chip { +namespace Shell { + +static chip::Shell::Engine sShellDnsSubcommands; +static bool isResolverStarted; + +class DnsShellResolverDelegate : public chip::Mdns::ResolverDelegate +{ +public: + void OnNodeIdResolved(const chip::Mdns::ResolvedNodeData & nodeData) override + { + streamer_printf(streamer_get(), "DNS resolve for " ChipLogFormatX64 "-" ChipLogFormatX64 " succeeded:\n", + ChipLogValueX64(nodeData.mPeerId.GetFabricId()), ChipLogValueX64(nodeData.mPeerId.GetNodeId())); + streamer_printf(streamer_get(), " IP address: %s\n", nodeData.mAddress.ToString(ipAddressBuf)); + streamer_printf(streamer_get(), " Port: %d\n", nodeData.mPort); + } + + void OnNodeIdResolutionFailed(const PeerId & peerId, CHIP_ERROR error) override {} + + void OnNodeDiscoveryComplete(const chip::Mdns::DiscoveredNodeData & nodeData) override + { + if (!nodeData.IsValid()) + { + streamer_printf(streamer_get(), "DNS browse failed - not found valid services \n"); + return; + } + streamer_printf(streamer_get(), "DNS browse succeeded: \n"); + streamer_printf(streamer_get(), " Hostname: %s\n", nodeData.hostName); + streamer_printf(streamer_get(), " Vendor ID: %d\n", nodeData.vendorId); + streamer_printf(streamer_get(), " Product ID: %d\n", nodeData.productId); + streamer_printf(streamer_get(), " Long discriminator: %d\n", nodeData.longDiscriminator); + streamer_printf(streamer_get(), " Device type: %d\n", nodeData.deviceType); + streamer_printf(streamer_get(), " Device name: %s\n", nodeData.deviceName); + streamer_printf(streamer_get(), " Commissioning mode: %d\n", nodeData.commissioningMode); + streamer_printf(streamer_get(), " IP addresses:\n"); + for (uint8_t i = 0; i < nodeData.kMaxIPAddresses; i++) + { + if (nodeData.ipAddress[i] != chip::Inet::IPAddress::Any) + streamer_printf(streamer_get(), " %s\n", nodeData.ipAddress[i].ToString(ipAddressBuf)); + } + } + +private: + char ipAddressBuf[chip::Inet::kMaxIPAddressStringLength]; +}; + +static DnsShellResolverDelegate sDnsShellResolverDelegate; + +CHIP_ERROR DnsHelpHandler(int argc, char ** argv) +{ + sShellDnsSubcommands.ForEachCommand(PrintCommandHelp, nullptr); + return CHIP_NO_ERROR; +} + +static CHIP_ERROR ResolveHandler(int argc, char ** argv) +{ + streamer_printf(streamer_get(), "Resolving ...\r\n"); + + VerifyOrReturnError(argc == 2, CHIP_ERROR_INVALID_ARGUMENT); + + PeerId peerId; + peerId.SetFabricId(strtoull(argv[0], NULL, 10)); + peerId.SetNodeId(strtoull(argv[1], NULL, 10)); + + return chip::Mdns::Resolver::Instance().ResolveNodeId(peerId, Inet::kIPAddressType_Any); +} + +static CHIP_ERROR BrowseHandler(int argc, char ** argv) +{ + streamer_printf(streamer_get(), "Browsing ...\r\n"); + + return chip::Mdns::Resolver::Instance().FindCommissionableNodes(); +} + +static CHIP_ERROR DnsHandler(int argc, char ** argv) +{ + if (!isResolverStarted) + { + chip::Mdns::Resolver::Instance().StartResolver(&chip::DeviceLayer::InetLayer, chip::Mdns::kMdnsPort); + chip::Mdns::Resolver::Instance().SetResolverDelegate(&sDnsShellResolverDelegate); + + isResolverStarted = true; + } + + if (argc == 0) + { + DnsHelpHandler(argc, argv); + return CHIP_NO_ERROR; + } + return sShellDnsSubcommands.ExecCommand(argc, argv); +} + +void RegisterDnsCommands() +{ + static const shell_command_t sDnsSubCommands[] = { + { &ResolveHandler, "resolve", + "Resolve the DNS service. Usage: dns resolve (e.g. dns resolve 5544332211 1)" }, + { &BrowseHandler, "browse", "Browse the services published by mdns. Usage: dns browse" }, + }; + + static const shell_command_t sDnsCommand = { &DnsHandler, "dns", "Dns client commands" }; + + // Register `dns` subcommands with the local shell dispatcher. + sShellDnsSubcommands.RegisterCommands(sDnsSubCommands, ArraySize(sDnsSubCommands)); + + // Register the root `dns` command with the top-level shell. + Engine::Root().RegisterCommands(&sDnsCommand, 1); +} + +} // namespace Shell +} // namespace chip diff --git a/src/platform/OpenThread/GenericThreadStackManagerImpl_OpenThread.cpp b/src/platform/OpenThread/GenericThreadStackManagerImpl_OpenThread.cpp index 20146bff517f5c..69346732331bca 100644 --- a/src/platform/OpenThread/GenericThreadStackManagerImpl_OpenThread.cpp +++ b/src/platform/OpenThread/GenericThreadStackManagerImpl_OpenThread.cpp @@ -1000,7 +1000,7 @@ void GenericThreadStackManagerImpl_OpenThread::OnSrpClientNotificatio otSrpClientService * next = nullptr; using Service = typename SrpClient::Service; - // Free memory for all removed services. + // Clear memory for all removed services. do { next = otService->mNext; @@ -1059,6 +1059,13 @@ void GenericThreadStackManagerImpl_OpenThread::OnSrpClientStateChange Encoding::BigEndian::HostSwap16(aServerSockAddr->mAddress.mFields.m16[5]), Encoding::BigEndian::HostSwap16(aServerSockAddr->mAddress.mFields.m16[6]), Encoding::BigEndian::HostSwap16(aServerSockAddr->mAddress.mFields.m16[7])); + +#if CHIP_DEVICE_CONFIG_ENABLE_THREAD_DNS_CLIENT + // Set DNS server config to be set at the SRP server address + otDnsQueryConfig dnsConfig = *otDnsClientGetDefaultConfig(ThreadStackMgrImpl().OTInstance()); + dnsConfig.mServerSockAddr.mAddress = aServerSockAddr->mAddress; + otDnsClientSetDefaultConfig(ThreadStackMgrImpl().OTInstance(), &dnsConfig); +#endif } else { @@ -1237,6 +1244,250 @@ CHIP_ERROR GenericThreadStackManagerImpl_OpenThread::_SetupSrpHost(co return error; } +#if CHIP_DEVICE_CONFIG_ENABLE_THREAD_DNS_CLIENT +template +CHIP_ERROR GenericThreadStackManagerImpl_OpenThread::FromOtDnsResponseToMdnsData( + otDnsServiceInfo & serviceInfo, const char * serviceType, chip::Mdns::MdnsService & mdnsService, + DnsServiceTxtEntries & serviceTxtEntries) +{ + char protocol[chip::Mdns::kMdnsProtocolTextMaxSize + 1]; + + if (strchr(serviceInfo.mHostNameBuffer, '.') == nullptr) + return CHIP_ERROR_INVALID_ARGUMENT; + + // Extract from the .. the part. + size_t substringSize = strchr(serviceInfo.mHostNameBuffer, '.') - serviceInfo.mHostNameBuffer; + strncpy(mdnsService.mHostName, serviceInfo.mHostNameBuffer, substringSize); + // Append string terminating character. + mdnsService.mHostName[substringSize] = '\0'; + + if (strchr(serviceType, '.') == nullptr) + return CHIP_ERROR_INVALID_ARGUMENT; + + // Extract from the ... the part. + substringSize = strchr(serviceType, '.') - serviceType; + strncpy(mdnsService.mType, serviceType, substringSize); + // Append string terminating character. + mdnsService.mType[substringSize] = '\0'; + + // Extract from the ... the part. + const char * protocolSubstringStart = serviceType + substringSize + 1; + + if (strchr(protocolSubstringStart, '.') == nullptr) + return CHIP_ERROR_INVALID_ARGUMENT; + + substringSize = strchr(protocolSubstringStart, '.') - protocolSubstringStart; + strncpy(protocol, protocolSubstringStart, substringSize); + // Append string terminating character. + protocol[substringSize] = '\0'; + + if (strncmp(protocol, "_udp", chip::Mdns::kMdnsProtocolTextMaxSize) == 0) + { + mdnsService.mProtocol = chip::Mdns::MdnsServiceProtocol::kMdnsProtocolUdp; + } + else if (strncmp(protocol, "_tcp", chip::Mdns::kMdnsProtocolTextMaxSize) == 0) + { + mdnsService.mProtocol = chip::Mdns::MdnsServiceProtocol::kMdnsProtocolTcp; + } + else + { + mdnsService.mProtocol = chip::Mdns::MdnsServiceProtocol::kMdnsProtocolUnknown; + } + mdnsService.mPort = serviceInfo.mPort; + mdnsService.mInterface = INET_NULL_INTERFACEID; + mdnsService.mAddressType = Inet::kIPAddressType_IPv6; + mdnsService.mAddress = chip::Optional(ToIPAddress(serviceInfo.mHostAddress)); + + otDnsTxtEntryIterator iterator; + otDnsInitTxtEntryIterator(&iterator, serviceInfo.mTxtData, serviceInfo.mTxtDataSize); + + otDnsTxtEntry txtEntry; + + uint8_t entryIndex = 0; + while ((otDnsGetNextTxtEntry(&iterator, &txtEntry) == OT_ERROR_NONE) && entryIndex < kMaxDnsServiceTxtEntriesNumber) + { + if (txtEntry.mKey && strlen(txtEntry.mKey) < kMaxDnsServiceTxtKeySize && txtEntry.mValue && + txtEntry.mValueLength <= kMaxDnsServiceTxtValueSize) + { + strcpy(serviceTxtEntries.mTxtKeyBuffers[entryIndex], txtEntry.mKey); + serviceTxtEntries.mTxtEntries[entryIndex].mKey = serviceTxtEntries.mTxtKeyBuffers[entryIndex]; + serviceTxtEntries.mTxtEntries[entryIndex].mDataSize = txtEntry.mValueLength; + memcpy(serviceTxtEntries.mTxtValueBuffers[entryIndex], txtEntry.mValue, txtEntry.mValueLength); + serviceTxtEntries.mTxtEntries[entryIndex].mData = serviceTxtEntries.mTxtValueBuffers[entryIndex]; + entryIndex++; + } + } + + mdnsService.mTextEntries = serviceTxtEntries.mTxtEntries; + mdnsService.mTextEntrySize = entryIndex; + + return CHIP_NO_ERROR; +} + +template +void GenericThreadStackManagerImpl_OpenThread::OnDnsBrowseResult(otError aError, const otDnsBrowseResponse * aResponse, + void * aContext) +{ + CHIP_ERROR error; + DnsResult browseResult; + // type buffer size is kMdnsTypeAndProtocolMaxSize + . + kMaxDomainNameSize + . + termination character + char type[chip::Mdns::kMdnsTypeAndProtocolMaxSize + SrpClient::kMaxDomainNameSize + 3]; + // hostname buffer size is kMdnsHostNameMaxSize + . + kMaxDomainNameSize + . + termination character + char hostname[chip::Mdns::kMdnsHostNameMaxSize + SrpClient::kMaxDomainNameSize + 3]; + + uint8_t txtBuffer[kMaxDnsServiceTxtEntriesNumber * + (kMaxDnsServiceTxtKeySize + kMaxDnsServiceTxtValueSize + sizeof(chip::Mdns::TextEntry))]; + otDnsServiceInfo serviceInfo; + uint16_t index = 0; + bool wasAnythingBrowsed; + + if (ThreadStackMgrImpl().mDnsBrowseCallback == nullptr) + { + ChipLogError(DeviceLayer, "Invalid dns browse callback"); + return; + } + + ThreadStackMgrImpl().LockThreadStack(); + + VerifyOrExit(aError == OT_ERROR_NONE, error = MapOpenThreadError(aError)); + + error = MapOpenThreadError(otDnsBrowseResponseGetServiceName(aResponse, type, sizeof(type))); + + VerifyOrExit(error == CHIP_NO_ERROR, ); + + while (otDnsBrowseResponseGetServiceInstance(aResponse, index, browseResult.mMdnsService.mName, + sizeof(browseResult.mMdnsService.mName)) == OT_ERROR_NONE) + { + serviceInfo.mHostNameBuffer = hostname; + serviceInfo.mHostNameBufferSize = sizeof(hostname); + serviceInfo.mTxtData = txtBuffer; + serviceInfo.mTxtDataSize = sizeof(txtBuffer); + + error = MapOpenThreadError(otDnsBrowseResponseGetServiceInfo(aResponse, browseResult.mMdnsService.mName, &serviceInfo)); + + VerifyOrExit(error == CHIP_NO_ERROR, ); + + if (FromOtDnsResponseToMdnsData(serviceInfo, type, browseResult.mMdnsService, browseResult.mServiceTxtEntry) == + CHIP_NO_ERROR) + { + // Invoke callback for every service one by one instead of for the whole list due to large memory size needed to + // allocate on + // stack. + ThreadStackMgrImpl().mDnsBrowseCallback(aContext, &browseResult.mMdnsService, 1, MapOpenThreadError(aError)); + wasAnythingBrowsed = true; + } + index++; + } + +exit: + + ThreadStackMgrImpl().UnlockThreadStack(); + + // In case no service was found invoke callback to notify about failure. In other case it was already called before. + if (!wasAnythingBrowsed) + ThreadStackMgrImpl().mDnsBrowseCallback(aContext, nullptr, 0, error); +} + +template +CHIP_ERROR GenericThreadStackManagerImpl_OpenThread::_DnsBrowse(const char * aServiceName, DnsBrowseCallback aCallback, + void * aContext) +{ + CHIP_ERROR error = CHIP_NO_ERROR; + + Impl()->LockThreadStack(); + const otDnsQueryConfig * defaultConfig = otDnsClientGetDefaultConfig(mOTInst); + + VerifyOrExit(aServiceName, error = CHIP_ERROR_INVALID_ARGUMENT); + + mDnsBrowseCallback = aCallback; + + // Append default SRP domain name to the service name. + // fullServiceName buffer size is kMdnsTypeAndProtocolMaxSize + . separator + kDefaultDomainNameSize + termination character. + char fullServiceName[chip::Mdns::kMdnsTypeAndProtocolMaxSize + 1 + SrpClient::kDefaultDomainNameSize + 1]; + snprintf(fullServiceName, sizeof(fullServiceName), "%s.%s", aServiceName, SrpClient::kDefaultDomainName); + + error = otDnsClientBrowse(mOTInst, fullServiceName, OnDnsBrowseResult, aContext, defaultConfig); + +exit: + + Impl()->UnlockThreadStack(); + + return error; +} + +template +void GenericThreadStackManagerImpl_OpenThread::OnDnsResolveResult(otError aError, const otDnsServiceResponse * aResponse, + void * aContext) +{ + CHIP_ERROR error; + DnsResult resolveResult; + // type buffer size is kMdnsTypeAndProtocolMaxSize + . + kMaxDomainNameSize + . + termination character + char type[chip::Mdns::kMdnsTypeAndProtocolMaxSize + SrpClient::kMaxDomainNameSize + 3]; + // hostname buffer size is kMdnsHostNameMaxSize + . + kMaxDomainNameSize + . + termination character + char hostname[chip::Mdns::kMdnsHostNameMaxSize + SrpClient::kMaxDomainNameSize + 3]; + uint8_t txtBuffer[kMaxDnsServiceTxtEntriesNumber * + (kMaxDnsServiceTxtKeySize + kMaxDnsServiceTxtValueSize + sizeof(chip::Mdns::TextEntry))]; + otDnsServiceInfo serviceInfo; + + if (ThreadStackMgrImpl().mDnsResolveCallback == nullptr) + { + ChipLogError(DeviceLayer, "Invalid dns browse callback"); + return; + } + + ThreadStackMgrImpl().LockThreadStack(); + + VerifyOrExit(aError == OT_ERROR_NONE, error = MapOpenThreadError(aError)); + + error = MapOpenThreadError(otDnsServiceResponseGetServiceName(aResponse, resolveResult.mMdnsService.mName, + sizeof(resolveResult.mMdnsService.mName), type, sizeof(type))); + + VerifyOrExit(error == CHIP_NO_ERROR, ); + + serviceInfo.mHostNameBuffer = hostname; + serviceInfo.mHostNameBufferSize = sizeof(hostname); + serviceInfo.mTxtData = txtBuffer; + serviceInfo.mTxtDataSize = sizeof(txtBuffer); + + error = MapOpenThreadError(otDnsServiceResponseGetServiceInfo(aResponse, &serviceInfo)); + + VerifyOrExit(error == CHIP_NO_ERROR, ); + + error = FromOtDnsResponseToMdnsData(serviceInfo, type, resolveResult.mMdnsService, resolveResult.mServiceTxtEntry); + +exit: + + ThreadStackMgrImpl().UnlockThreadStack(); + ThreadStackMgrImpl().mDnsResolveCallback(aContext, &(resolveResult.mMdnsService), error); +} + +template +CHIP_ERROR GenericThreadStackManagerImpl_OpenThread::_DnsResolve(const char * aServiceName, const char * aInstanceName, + DnsResolveCallback aCallback, void * aContext) +{ + CHIP_ERROR error = CHIP_NO_ERROR; + + Impl()->LockThreadStack(); + const otDnsQueryConfig * defaultConfig = otDnsClientGetDefaultConfig(mOTInst); + + VerifyOrExit(aServiceName && aInstanceName, error = CHIP_ERROR_INVALID_ARGUMENT); + + mDnsResolveCallback = aCallback; + + // Append default SRP domain name to the service name. + // fullServiceName buffer size is kMdnsTypeAndProtocolMaxSize + . separator + kDefaultDomainNameSize + termination character. + char fullServiceName[chip::Mdns::kMdnsTypeAndProtocolMaxSize + 1 + SrpClient::kDefaultDomainNameSize + 1]; + snprintf(fullServiceName, sizeof(fullServiceName), "%s.%s", aServiceName, SrpClient::kDefaultDomainName); + + error = otDnsClientResolveService(mOTInst, aInstanceName, fullServiceName, OnDnsResolveResult, aContext, defaultConfig); + +exit: + + Impl()->UnlockThreadStack(); + + return error; +} +#endif // CHIP_DEVICE_CONFIG_ENABLE_THREAD_DNS_CLIENT #endif // CHIP_DEVICE_CONFIG_ENABLE_THREAD_SRP_CLIENT } // namespace Internal diff --git a/src/platform/OpenThread/GenericThreadStackManagerImpl_OpenThread.h b/src/platform/OpenThread/GenericThreadStackManagerImpl_OpenThread.h index 34b3911a30522a..cd542ec69f4481 100644 --- a/src/platform/OpenThread/GenericThreadStackManagerImpl_OpenThread.h +++ b/src/platform/OpenThread/GenericThreadStackManagerImpl_OpenThread.h @@ -32,6 +32,10 @@ #include #endif +#if CHIP_DEVICE_CONFIG_ENABLE_THREAD_DNS_CLIENT +#include +#endif + #include #include @@ -93,11 +97,16 @@ class GenericThreadStackManagerImpl_OpenThread void _OnWoBLEAdvertisingStop(void); #if CHIP_DEVICE_CONFIG_ENABLE_THREAD_SRP_CLIENT - CHIP_ERROR _AddSrpService(const char * aInstanceName, const char * aName, uint16_t aPort, chip::Mdns::TextEntry * aTxtEntries, - size_t aTxtEntiresSize, uint32_t aLeaseInterval, uint32_t aKeyLeaseInterval); + CHIP_ERROR + _AddSrpService(const char * aInstanceName, const char * aName, uint16_t aPort, chip::Mdns::TextEntry * aTxtEntries, + size_t aTxtEntiresSize, uint32_t aLeaseInterval, uint32_t aKeyLeaseInterval); CHIP_ERROR _RemoveSrpService(const char * aInstanceName, const char * aName); CHIP_ERROR _RemoveAllSrpServices(); CHIP_ERROR _SetupSrpHost(const char * aHostName); +#if CHIP_DEVICE_CONFIG_ENABLE_THREAD_DNS_CLIENT + CHIP_ERROR _DnsBrowse(const char * aServiceName, DnsBrowseCallback aCallback, void * aContext); + CHIP_ERROR _DnsResolve(const char * aServiceName, const char * aInstanceName, DnsResolveCallback aCallback, void * aContext); +#endif // CHIP_DEVICE_CONFIG_ENABLE_THREAD_DNS_CLIENT #endif // CHIP_DEVICE_CONFIG_ENABLE_THREAD_SRP_CLIENT // ===== Members available to the implementation subclass. @@ -119,14 +128,25 @@ class GenericThreadStackManagerImpl_OpenThread struct SrpClient { - static constexpr uint8_t kMaxServicesNumber = CHIP_DEVICE_CONFIG_THREAD_SRP_MAX_SERVICES; - static constexpr uint8_t kMaxInstanceNameSize = chip::Mdns::kMdnsInstanceNameMaxSize; - static constexpr uint8_t kMaxNameSize = chip::Mdns::kMdnsTypeAndProtocolMaxSize; - static constexpr uint8_t kMaxHostNameSize = 16; - // Thread only supports operational discovery + static constexpr uint8_t kMaxServicesNumber = CHIP_DEVICE_CONFIG_THREAD_SRP_MAX_SERVICES; + static constexpr uint8_t kMaxInstanceNameSize = chip::Mdns::kMdnsInstanceNameMaxSize; + static constexpr uint8_t kMaxNameSize = chip::Mdns::kMdnsTypeAndProtocolMaxSize; + static constexpr uint8_t kMaxHostNameSize = 16; + static constexpr const char * kDefaultDomainName = "default.service.arpa"; + static constexpr uint8_t kDefaultDomainNameSize = 20; + static constexpr uint8_t kMaxDomainNameSize = 32; + +#if CHIP_DEVICE_CONFIG_ENABLE_EXTENDED_DISCOVERY + // Thread supports both operational and commissionable discovery, so buffers sizes must be worst case. + static constexpr uint8_t kMaxTxtEntriesNumber = chip::Mdns::CommissionAdvertisingParameters::kNumAdvertisingTxtEntries; + static constexpr uint8_t kMaxTxtValueSize = chip::Mdns::CommissionAdvertisingParameters::kTxtMaxValueSize; + static constexpr uint8_t kMaxTxtKeySize = chip::Mdns::OperationalAdvertisingParameters::kTxtMaxKeySize; +#else + // Thread only supports operational discovery. static constexpr uint8_t kMaxTxtEntriesNumber = chip::Mdns::OperationalAdvertisingParameters::kNumAdvertisingTxtEntries; static constexpr uint8_t kMaxTxtValueSize = chip::Mdns::OperationalAdvertisingParameters::kTxtMaxValueSize; static constexpr uint8_t kMaxTxtKeySize = chip::Mdns::OperationalAdvertisingParameters::kTxtMaxKeySize; +#endif // CHIP_DEVICE_CONFIG_ENABLE_EXTENDED_DISCOVERY struct Service { @@ -149,6 +169,42 @@ class GenericThreadStackManagerImpl_OpenThread const otSrpClientService * aRemovedServices, void * aContext); static void OnSrpClientStateChange(const otSockAddr * aServerSockAddr, void * aContext); +#if CHIP_DEVICE_CONFIG_ENABLE_THREAD_DNS_CLIENT +#if CHIP_DEVICE_CONFIG_ENABLE_THREAD_COMMISSIONABLE_DISCOVERY + // Thread supports both operational and commissionable discovery, so buffers sizes must be worst case. + static constexpr uint8_t kMaxDnsServiceTxtEntriesNumber = + chip::Mdns::CommissionAdvertisingParameters::kNumAdvertisingTxtEntries; + static constexpr uint8_t kMaxDnsServiceTxtValueSize = chip::Mdns::CommissionAdvertisingParameters::kTxtMaxValueSize; + static constexpr uint8_t kMaxDnsServiceTxtKeySize = chip::Mdns::OperationalAdvertisingParameters::kTxtMaxKeySize; +#else + // Thread only supports operational discovery. + static constexpr uint8_t kMaxDnsServiceTxtEntriesNumber = + chip::Mdns::OperationalAdvertisingParameters::kNumAdvertisingTxtEntries; + static constexpr uint8_t kMaxDnsServiceTxtValueSize = chip::Mdns::OperationalAdvertisingParameters::kTxtMaxValueSize; + static constexpr uint8_t kMaxDnsServiceTxtKeySize = chip::Mdns::OperationalAdvertisingParameters::kTxtMaxKeySize; +#endif // CHIP_DEVICE_CONFIG_ENABLE_THREAD_COMMISSIONABLE_DISCOVERY + + DnsBrowseCallback mDnsBrowseCallback; + DnsResolveCallback mDnsResolveCallback; + + struct DnsServiceTxtEntries + { + chip::Mdns::TextEntry mTxtEntries[kMaxDnsServiceTxtEntriesNumber]; + uint8_t mTxtValueBuffers[kMaxDnsServiceTxtEntriesNumber][kMaxDnsServiceTxtValueSize]; + char mTxtKeyBuffers[kMaxDnsServiceTxtEntriesNumber][kMaxDnsServiceTxtKeySize]; + }; + + struct DnsResult + { + chip::Mdns::MdnsService mMdnsService; + DnsServiceTxtEntries mServiceTxtEntry; + }; + + static void OnDnsBrowseResult(otError aError, const otDnsBrowseResponse * aResponse, void * aContext); + static void OnDnsResolveResult(otError aError, const otDnsServiceResponse * aResponse, void * aContext); + static CHIP_ERROR FromOtDnsResponseToMdnsData(otDnsServiceInfo & serviceInfo, const char * serviceType, + chip::Mdns::MdnsService & mdnsService, DnsServiceTxtEntries & serviceTxtEntries); +#endif // CHIP_DEVICE_CONFIG_ENABLE_THREAD_DNS_CLIENT #endif // CHIP_DEVICE_CONFIG_ENABLE_THREAD_SRP_CLIENT static void OnJoinerComplete(otError aError, void * aContext); diff --git a/src/platform/OpenThread/MdnsImpl.cpp b/src/platform/OpenThread/MdnsImpl.cpp index 831da3e2248026..d581c89b443563 100644 --- a/src/platform/OpenThread/MdnsImpl.cpp +++ b/src/platform/OpenThread/MdnsImpl.cpp @@ -39,6 +39,7 @@ const char * GetProtocolString(MdnsServiceProtocol protocol) CHIP_ERROR ChipMdnsPublishService(const MdnsService * service) { +#if CHIP_DEVICE_CONFIG_ENABLE_THREAD_SRP_CLIENT CHIP_ERROR result = CHIP_NO_ERROR; VerifyOrExit(service, result = CHIP_ERROR_INVALID_ARGUMENT); @@ -56,15 +57,23 @@ CHIP_ERROR ChipMdnsPublishService(const MdnsService * service) exit: return result; +#else + return CHIP_ERROR_NOT_IMPLEMENTED; +#endif // CHIP_DEVICE_CONFIG_ENABLE_THREAD_SRP_CLIENT } CHIP_ERROR ChipMdnsStopPublish() { +#if CHIP_DEVICE_CONFIG_ENABLE_THREAD_SRP_CLIENT return ThreadStackMgr().RemoveAllSrpServices(); +#else + return CHIP_ERROR_NOT_IMPLEMENTED; +#endif // CHIP_DEVICE_CONFIG_ENABLE_THREAD_SRP_CLIENT } CHIP_ERROR ChipMdnsStopPublishService(const MdnsService * service) { +#if CHIP_DEVICE_CONFIG_ENABLE_THREAD_SRP_CLIENT if (service == nullptr) return CHIP_ERROR_INVALID_ARGUMENT; @@ -72,17 +81,40 @@ CHIP_ERROR ChipMdnsStopPublishService(const MdnsService * service) snprintf(serviceType, sizeof(serviceType), "%s.%s", service->mType, GetProtocolString(service->mProtocol)); return ThreadStackMgr().RemoveSrpService(service->mName, serviceType); +#else + return CHIP_ERROR_NOT_IMPLEMENTED; +#endif // CHIP_DEVICE_CONFIG_ENABLE_THREAD_SRP_CLIENT } CHIP_ERROR ChipMdnsBrowse(const char * type, MdnsServiceProtocol protocol, Inet::IPAddressType addressType, Inet::InterfaceId interface, MdnsBrowseCallback callback, void * context) { +#if CHIP_DEVICE_CONFIG_ENABLE_THREAD_SRP_CLIENT && CHIP_DEVICE_CONFIG_ENABLE_THREAD_DNS_CLIENT + if (type == nullptr || callback == nullptr) + return CHIP_ERROR_INVALID_ARGUMENT; + + char serviceType[chip::Mdns::kMdnsTypeAndProtocolMaxSize + 1]; + snprintf(serviceType, sizeof(serviceType), "%s.%s", type, GetProtocolString(protocol)); + + return ThreadStackMgr().DnsBrowse(serviceType, callback, context); +#else return CHIP_ERROR_NOT_IMPLEMENTED; +#endif // CHIP_DEVICE_CONFIG_ENABLE_THREAD_SRP_CLIENT && CHIP_DEVICE_CONFIG_ENABLE_THREAD_DNS_CLIENT } CHIP_ERROR ChipMdnsResolve(MdnsService * browseResult, Inet::InterfaceId interface, MdnsResolveCallback callback, void * context) { +#if CHIP_DEVICE_CONFIG_ENABLE_THREAD_SRP_CLIENT && CHIP_DEVICE_CONFIG_ENABLE_THREAD_DNS_CLIENT + if (browseResult == nullptr || callback == nullptr) + return CHIP_ERROR_INVALID_ARGUMENT; + + char serviceType[chip::Mdns::kMdnsTypeAndProtocolMaxSize + 1]; + snprintf(serviceType, sizeof(serviceType), "%s.%s", browseResult->mType, GetProtocolString(browseResult->mProtocol)); + + return ThreadStackMgr().DnsResolve(serviceType, browseResult->mName, callback, context); +#else return CHIP_ERROR_NOT_IMPLEMENTED; +#endif // CHIP_DEVICE_CONFIG_ENABLE_THREAD_SRP_CLIENT && CHIP_DEVICE_CONFIG_ENABLE_THREAD_DNS_CLIENT } } // namespace Mdns diff --git a/src/platform/nrfconnect/CHIPDevicePlatformConfig.h b/src/platform/nrfconnect/CHIPDevicePlatformConfig.h index 346638d38b98fd..b2096a58d07f0a 100644 --- a/src/platform/nrfconnect/CHIPDevicePlatformConfig.h +++ b/src/platform/nrfconnect/CHIPDevicePlatformConfig.h @@ -75,4 +75,8 @@ #ifdef CONFIG_CHIP_ENABLE_DNSSD_SRP #define CHIP_DEVICE_CONFIG_ENABLE_MDNS 1 #define CHIP_DEVICE_CONFIG_ENABLE_THREAD_SRP_CLIENT 1 +#ifdef CONFIG_CHIP_ENABLE_DNS_CLIENT +#define CHIP_DEVICE_CONFIG_ENABLE_THREAD_DNS_CLIENT 1 +#define CHIP_DEVICE_CONFIG_ENABLE_THREAD_COMMISSIONABLE_DISCOVERY 1 +#endif // CONFIG_CHIP_ENABLE_DNS_CLIENT #endif // CONFIG_CHIP_ENABLE_DNSSD_SRP