From de54437b35449fd9f2eb2d96511b903105ef11f7 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Wed, 1 May 2024 11:19:22 -0700 Subject: [PATCH] [mdns] add APIs to iterate over browsers and resolvers This commit adds APIs to iterate over mDNS browsers and resolvers, along with related CLI commands. These are intended for testing. It also introduces a build config `ENTRY_ITERATION_API_ENABLE` to control whether the mDNS module provides mechanisms and public APIs for entry iteration. --- include/openthread/instance.h | 2 +- include/openthread/mdns.h | 144 ++++++++++ src/cli/cli_mdns.cpp | 253 +++++++++++++++++- src/cli/cli_mdns.hpp | 1 + src/core/api/mdns_api.cpp | 68 +++++ src/core/config/mdns.h | 11 + src/core/net/mdns.cpp | 211 +++++++++++++++ src/core/net/mdns.hpp | 127 +++++++++ ...openthread-core-toranj-config-simulation.h | 2 + 9 files changed, 814 insertions(+), 5 deletions(-) diff --git a/include/openthread/instance.h b/include/openthread/instance.h index df87b06cb69..690277cf9ee 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (415) +#define OPENTHREAD_API_VERSION (416) /** * @addtogroup api-instance diff --git a/include/openthread/mdns.h b/include/openthread/mdns.h index a88c70f8357..56ffff09c88 100644 --- a/include/openthread/mdns.h +++ b/include/openthread/mdns.h @@ -411,6 +411,8 @@ otError otMdnsUnregisterKey(otInstance *aInstance, const otMdnsKey *aKey); /** * Allocates a new iterator. * + * Requires `OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE`. + * * An allocated iterator must be freed by the caller using `otMdnsFreeIterator()`. * * @param[in] aInstance The OpenThread instance. @@ -423,6 +425,8 @@ otMdnsIterator *otMdnsAllocateIterator(otInstance *aInstance); /** * Frees a previously allocated iterator. * + * Requires `OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE`. + * * @param[in] aInstance The OpenThread instance. * @param[in] aIterator The iterator to free. * @@ -432,6 +436,8 @@ void otMdnsFreeIterator(otInstance *aInstance, otMdnsIterator *aIterator); /** * Iterates over registered host entries. * + * Requires `OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE`. + * * On success, @p aHost is populated with information about the next host. Pointers within the `otMdnsHost` structure * (like `mName`) remain valid until the next call to any OpenThread stack's public or platform API/callback. * @@ -453,6 +459,8 @@ otError otMdnsGetNextHost(otInstance *aInstance, /** * Iterates over registered service entries. * + * Requires `OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE`. + * * On success, @p aService is populated with information about the next service . Pointers within the `otMdnsService` * structure (like `mServiceType`, `mSubTypeLabels`) remain valid until the next call to any OpenThread stack's public * or platform API/callback. @@ -475,6 +483,8 @@ otError otMdnsGetNextService(otInstance *aInstance, /** * Iterates over registered key entries. * + * Requires `OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE`. + * * On success, @p aKey is populated with information about the next key. Pointers within the `otMdnsKey` structure * (like `mName`) remain valid until the next call to any OpenThread stack's public or platform API/callback. * @@ -837,6 +847,140 @@ otError otMdnsStartIp4AddressResolver(otInstance *aInstance, const otMdnsAddress */ otError otMdnsStopIp4AddressResolver(otInstance *aInstance, const otMdnsAddressResolver *aResolver); +/** + * Represents additional information about a browser/resolver and its cached results. + * + */ +typedef struct otMdnsCacheInfo +{ + bool mIsActive; ///< Whether this is an active browser/resolver vs an opportunistic cached one. + bool mHasCachedResults; ///< Whether there is any cached results. +} otMdnsCacheInfo; + +/** + * Iterates over browsers. + * + * Requires `OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE`. + * + * On success, @p aBrowser is populated with information about the next browser. The `mCallback` field is always + * set to `NULL` as there may be multiple active browsers with different callbacks. Other pointers within the + * `otMdnsBrowser` structure remain valid until the next call to any OpenThread stack's public or platform API/callback. + * + * @param[in] aInstance The OpenThread instance. + * @param[in] aIterator Pointer to the iterator. + * @param[out] aBrowser Pointer to an `otMdnsBrowser` to return the information about the next browser. + * @param[out] aInfo Pointer to an `otMdnsCacheInfo` to return additional information. + * + * @retval OT_ERROR_NONE @p aBrowser, @p aInfo, & @p aIterator are updated successfully. + * @retval OT_ERROR_NOT_FOUND Reached the end of the list. + * @retval OT_ERROR_INVALID_ARG @p aIterator is not valid. + * + */ +otError otMdnsGetNextBrowser(otInstance *aInstance, + otMdnsIterator *aIterator, + otMdnsBrowser *aBrowser, + otMdnsCacheInfo *aInfo); + +/** + * Iterates over SRV resolvers. + * + * Requires `OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE`. + * + * On success, @p aResolver is populated with information about the next resolver. The `mCallback` field is always + * set to `NULL` as there may be multiple active resolvers with different callbacks. Other pointers within the + * `otMdnsSrvResolver` structure remain valid until the next call to any OpenThread stack's public or platform + * API/callback. + * + * @param[in] aInstance The OpenThread instance. + * @param[in] aIterator Pointer to the iterator. + * @param[out] aResolver Pointer to an `otMdnsSrvResolver` to return the information about the next resolver. + * @param[out] aInfo Pointer to an `otMdnsCacheInfo` to return additional information. + * + * @retval OT_ERROR_NONE @p aResolver, @p aInfo, & @p aIterator are updated successfully. + * @retval OT_ERROR_NOT_FOUND Reached the end of the list. + * @retval OT_ERROR_INVALID_ARG @p aIterator is not valid. + * + */ +otError otMdnsGetNextSrvResolver(otInstance *aInstance, + otMdnsIterator *aIterator, + otMdnsSrvResolver *aResolver, + otMdnsCacheInfo *aInfo); + +/** + * Iterates over TXT resolvers. + * + * Requires `OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE`. + * + * On success, @p aResolver is populated with information about the next resolver. The `mCallback` field is always + * set to `NULL` as there may be multiple active resolvers with different callbacks. Other pointers within the + * `otMdnsTxtResolver` structure remain valid until the next call to any OpenThread stack's public or platform + * API/callback. + * + * @param[in] aInstance The OpenThread instance. + * @param[in] aIterator Pointer to the iterator. + * @param[out] aResolver Pointer to an `otMdnsTxtResolver` to return the information about the next resolver. + * @param[out] aInfo Pointer to an `otMdnsCacheInfo` to return additional information. + * + * @retval OT_ERROR_NONE @p aResolver, @p aInfo, & @p aIterator are updated successfully. + * @retval OT_ERROR_NOT_FOUND Reached the end of the list. + * @retval OT_ERROR_INVALID_ARG @p aIterator is not valid. + * + */ +otError otMdnsGetNextTxtResolver(otInstance *aInstance, + otMdnsIterator *aIterator, + otMdnsTxtResolver *aResolver, + otMdnsCacheInfo *aInfo); + +/** + * Iterates over IPv6 address resolvers. + * + * Requires `OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE`. + * + * On success, @p aResolver is populated with information about the next resolver. The `mCallback` field is always + * set to `NULL` as there may be multiple active resolvers with different callbacks. Other pointers within the + * `otMdnsAddressResolver` structure remain valid until the next call to any OpenThread stack's public or platform + * API/callback. + * + * @param[in] aInstance The OpenThread instance. + * @param[in] aIterator Pointer to the iterator. + * @param[out] aResolver Pointer to an `otMdnsAddressResolver` to return the information about the next resolver. + * @param[out] aInfo Pointer to an `otMdnsCacheInfo` to return additional information. + * + * @retval OT_ERROR_NONE @p aResolver, @p aInfo, & @p aIterator are updated successfully. + * @retval OT_ERROR_NOT_FOUND Reached the end of the list. + * @retval OT_ERROR_INVALID_ARG @p aIterator is not valid. + * + */ +otError otMdnsGetNextIp6AddressResolver(otInstance *aInstance, + otMdnsIterator *aIterator, + otMdnsAddressResolver *aResolver, + otMdnsCacheInfo *aInfo); + +/** + * Iterates over IPv4 address resolvers. + * + * Requires `OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE`. + * + * On success, @p aResolver is populated with information about the next resolver. The `mCallback` field is always + * set to `NULL` as there may be multiple active resolvers with different callbacks. Other pointers within the + * `otMdnsAddressResolver` structure remain valid until the next call to any OpenThread stack's public or platform + * API/callback. + * + * @param[in] aInstance The OpenThread instance. + * @param[in] aIterator Pointer to the iterator. + * @param[out] aResolver Pointer to an `otMdnsAddressResolver` to return the information about the next resolver. + * @param[out] aInfo Pointer to an `otMdnsCacheInfo` to return additional information. + * + * @retval OT_ERROR_NONE @p aResolver, @p aInfo, & @p aIterator are updated successfully. + * @retval OT_ERROR_NOT_FOUND Reached the end of the list. + * @retval OT_ERROR_INVALID_ARG @p aIterator is not valid. + * + */ +otError otMdnsGetNextIp4AddressResolver(otInstance *aInstance, + otMdnsIterator *aIterator, + otMdnsAddressResolver *aResolver, + otMdnsCacheInfo *aInfo); + /** * @} * diff --git a/src/cli/cli_mdns.cpp b/src/cli/cli_mdns.cpp index 977bb7fa178..8a4e08ee491 100644 --- a/src/cli/cli_mdns.cpp +++ b/src/cli/cli_mdns.cpp @@ -171,6 +171,12 @@ void Mdns::OutputState(otMdnsEntryState aState) OutputLine(kIndentSize, "state: %s", stateString); } +void Mdns::OutputCacheInfo(const otMdnsCacheInfo &aInfo) +{ + OutputLine(kIndentSize, "active: %s", aInfo.mIsActive ? "yes" : "no"); + OutputLine(kIndentSize, "cached-results: %s", aInfo.mHasCachedResults ? "yes" : "no"); +} + template <> otError Mdns::Process(Arg aArgs[]) { // mdns [async] [host|service|key] @@ -481,6 +487,8 @@ template <> otError Mdns::Process(Arg aArgs[]) return error; } +#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE + template <> otError Mdns::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; @@ -592,6 +600,8 @@ template <> otError Mdns::Process(Arg aArgs[]) return error; } +#endif // OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE + otError Mdns::ParseStartOrStop(const Arg &aArg, bool &aIsStart) { otError error = OT_ERROR_NONE; @@ -869,6 +879,212 @@ void Mdns::HandleIp4AddressResult(otInstance *aInstance, const otMdnsAddressResu Interpreter::GetInterpreter().mMdns.HandleAddressResult(*aResult, kIp4Address); } +#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE + +template <> otError Mdns::Process(Arg aArgs[]) +{ + // mdns browsers + + otError error; + otMdnsIterator *iterator = nullptr; + otMdnsCacheInfo info; + otMdnsBrowser browser; + + VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS); + + iterator = otMdnsAllocateIterator(GetInstancePtr()); + VerifyOrExit(iterator != nullptr, error = OT_ERROR_NO_BUFS); + + while (true) + { + error = otMdnsGetNextBrowser(GetInstancePtr(), iterator, &browser, &info); + + if (error == OT_ERROR_NOT_FOUND) + { + error = OT_ERROR_NONE; + ExitNow(); + } + + SuccessOrExit(error); + + OutputFormat("Browser %s", browser.mServiceType); + + if (browser.mSubTypeLabel != nullptr) + { + OutputFormat(" for sub-type %s", browser.mSubTypeLabel); + } + + OutputNewLine(); + OutputCacheInfo(info); + } + +exit: + if (iterator != nullptr) + { + otMdnsFreeIterator(GetInstancePtr(), iterator); + } + + return error; +} + +template <> otError Mdns::Process(Arg aArgs[]) +{ + // mdns srvresolvers + + otError error; + otMdnsIterator *iterator = nullptr; + otMdnsCacheInfo info; + otMdnsSrvResolver resolver; + + VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS); + + iterator = otMdnsAllocateIterator(GetInstancePtr()); + VerifyOrExit(iterator != nullptr, error = OT_ERROR_NO_BUFS); + + while (true) + { + error = otMdnsGetNextSrvResolver(GetInstancePtr(), iterator, &resolver, &info); + + if (error == OT_ERROR_NOT_FOUND) + { + error = OT_ERROR_NONE; + ExitNow(); + } + + SuccessOrExit(error); + + OutputLine("SRV resolver %s for %s", resolver.mServiceInstance, resolver.mServiceType); + OutputCacheInfo(info); + } + +exit: + if (iterator != nullptr) + { + otMdnsFreeIterator(GetInstancePtr(), iterator); + } + + return error; +} + +template <> otError Mdns::Process(Arg aArgs[]) +{ + // mdns txtresolvers + + otError error; + otMdnsIterator *iterator = nullptr; + otMdnsCacheInfo info; + otMdnsTxtResolver resolver; + + VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS); + + iterator = otMdnsAllocateIterator(GetInstancePtr()); + VerifyOrExit(iterator != nullptr, error = OT_ERROR_NO_BUFS); + + while (true) + { + error = otMdnsGetNextTxtResolver(GetInstancePtr(), iterator, &resolver, &info); + + if (error == OT_ERROR_NOT_FOUND) + { + error = OT_ERROR_NONE; + ExitNow(); + } + + SuccessOrExit(error); + + OutputLine("TXT resolver %s for %s", resolver.mServiceInstance, resolver.mServiceType); + OutputCacheInfo(info); + } + +exit: + if (iterator != nullptr) + { + otMdnsFreeIterator(GetInstancePtr(), iterator); + } + + return error; +} + +template <> otError Mdns::Process(Arg aArgs[]) +{ + // mdns ip6resolvers + + otError error; + otMdnsIterator *iterator = nullptr; + otMdnsCacheInfo info; + otMdnsAddressResolver resolver; + + VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS); + + iterator = otMdnsAllocateIterator(GetInstancePtr()); + VerifyOrExit(iterator != nullptr, error = OT_ERROR_NO_BUFS); + + while (true) + { + error = otMdnsGetNextIp6AddressResolver(GetInstancePtr(), iterator, &resolver, &info); + + if (error == OT_ERROR_NOT_FOUND) + { + error = OT_ERROR_NONE; + ExitNow(); + } + + SuccessOrExit(error); + + OutputLine("IPv6 address resolver %s", resolver.mHostName); + OutputCacheInfo(info); + } + +exit: + if (iterator != nullptr) + { + otMdnsFreeIterator(GetInstancePtr(), iterator); + } + + return error; +} + +template <> otError Mdns::Process(Arg aArgs[]) +{ + // mdns ip4resolvers + + otError error; + otMdnsIterator *iterator = nullptr; + otMdnsCacheInfo info; + otMdnsAddressResolver resolver; + + VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS); + + iterator = otMdnsAllocateIterator(GetInstancePtr()); + VerifyOrExit(iterator != nullptr, error = OT_ERROR_NO_BUFS); + + while (true) + { + error = otMdnsGetNextIp4AddressResolver(GetInstancePtr(), iterator, &resolver, &info); + + if (error == OT_ERROR_NOT_FOUND) + { + error = OT_ERROR_NONE; + ExitNow(); + } + + SuccessOrExit(error); + + OutputLine("IPv4 address resolver %s", resolver.mHostName); + OutputCacheInfo(info); + } + +exit: + if (iterator != nullptr) + { + otMdnsFreeIterator(GetInstancePtr(), iterator); + } + + return error; +} + +#endif // OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE + otError Mdns::Process(Arg aArgs[]) { #define CmdEntry(aCommandString) \ @@ -877,10 +1093,39 @@ otError Mdns::Process(Arg aArgs[]) } static constexpr Command kCommands[] = { - CmdEntry("browser"), CmdEntry("disable"), CmdEntry("enable"), CmdEntry("hosts"), - CmdEntry("ip4resolver"), CmdEntry("ip6resolver"), CmdEntry("keys"), CmdEntry("register"), - CmdEntry("services"), CmdEntry("srvresolver"), CmdEntry("state"), CmdEntry("txtresolver"), - CmdEntry("unicastquestion"), CmdEntry("unregister"), + CmdEntry("browser"), +#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE + CmdEntry("browsers"), +#endif + CmdEntry("disable"), + CmdEntry("enable"), +#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE + CmdEntry("hosts"), +#endif + CmdEntry("ip4resolver"), +#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE + CmdEntry("ip4resolvers"), +#endif + CmdEntry("ip6resolver"), +#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE + CmdEntry("ip6resolvers"), + CmdEntry("keys"), +#endif + CmdEntry("register"), +#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE + CmdEntry("services"), +#endif + CmdEntry("srvresolver"), +#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE + CmdEntry("srvresolvers"), +#endif + CmdEntry("state"), + CmdEntry("txtresolver"), +#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE + CmdEntry("txtresolvers"), +#endif + CmdEntry("unicastquestion"), + CmdEntry("unregister"), }; #undef CmdEntry diff --git a/src/cli/cli_mdns.hpp b/src/cli/cli_mdns.hpp index 242b6d9a4b4..df6b4fe4c94 100644 --- a/src/cli/cli_mdns.hpp +++ b/src/cli/cli_mdns.hpp @@ -111,6 +111,7 @@ class Mdns : private Utils void OutputService(const otMdnsService &aService); void OutputKey(const otMdnsKey &aKey); void OutputState(otMdnsEntryState aState); + void OutputCacheInfo(const otMdnsCacheInfo &aInfo); otError ProcessRegisterHost(Arg aArgs[]); otError ProcessRegisterService(Arg aArgs[]); otError ProcessRegisterKey(Arg aArgs[]); diff --git a/src/core/api/mdns_api.cpp b/src/core/api/mdns_api.cpp index 8563303e61c..14c779d9c1e 100644 --- a/src/core/api/mdns_api.cpp +++ b/src/core/api/mdns_api.cpp @@ -115,6 +115,8 @@ otError otMdnsUnregisterKey(otInstance *aInstance, const otMdnsKey *aKey) return AsCoreType(aInstance).Get().UnregisterKey(*aKey); } +#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE + otMdnsIterator *otMdnsAllocateIterator(otInstance *aInstance) { return AsCoreType(aInstance).Get().AllocateIterator(); @@ -157,6 +159,8 @@ otError otMdnsGetNextKey(otInstance *aInstance, otMdnsIterator *aIterator, otMdn return AsCoreType(aInstance).Get().GetNextKey(*aIterator, *aKey, *aState); } +#endif // OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE + otError otMdnsStartBrowser(otInstance *aInstance, const otMdnsBrowser *aBroswer) { AssertPointerIsNotNull(aBroswer); @@ -227,4 +231,68 @@ otError otMdnsStopIp4AddressResolver(otInstance *aInstance, const otMdnsAddressR return AsCoreType(aInstance).Get().StopIp4AddressResolver(*aResolver); } +#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE + +otError otMdnsGetNextBrowser(otInstance *aInstance, + otMdnsIterator *aIterator, + otMdnsBrowser *aBrowser, + otMdnsCacheInfo *aInfo) +{ + AssertPointerIsNotNull(aIterator); + AssertPointerIsNotNull(aBrowser); + AssertPointerIsNotNull(aInfo); + + return AsCoreType(aInstance).Get().GetNextBrowser(*aIterator, *aBrowser, *aInfo); +} + +otError otMdnsGetNextSrvResolver(otInstance *aInstance, + otMdnsIterator *aIterator, + otMdnsSrvResolver *aResolver, + otMdnsCacheInfo *aInfo) +{ + AssertPointerIsNotNull(aIterator); + AssertPointerIsNotNull(aResolver); + AssertPointerIsNotNull(aInfo); + + return AsCoreType(aInstance).Get().GetNextSrvResolver(*aIterator, *aResolver, *aInfo); +} + +otError otMdnsGetNextTxtResolver(otInstance *aInstance, + otMdnsIterator *aIterator, + otMdnsTxtResolver *aResolver, + otMdnsCacheInfo *aInfo) +{ + AssertPointerIsNotNull(aIterator); + AssertPointerIsNotNull(aResolver); + AssertPointerIsNotNull(aInfo); + + return AsCoreType(aInstance).Get().GetNextTxtResolver(*aIterator, *aResolver, *aInfo); +} + +otError otMdnsGetNextIp6AddressResolver(otInstance *aInstance, + otMdnsIterator *aIterator, + otMdnsAddressResolver *aResolver, + otMdnsCacheInfo *aInfo) +{ + AssertPointerIsNotNull(aIterator); + AssertPointerIsNotNull(aResolver); + AssertPointerIsNotNull(aInfo); + + return AsCoreType(aInstance).Get().GetNextIp6AddressResolver(*aIterator, *aResolver, *aInfo); +} + +otError otMdnsGetNextIp4AddressResolver(otInstance *aInstance, + otMdnsIterator *aIterator, + otMdnsAddressResolver *aResolver, + otMdnsCacheInfo *aInfo) +{ + AssertPointerIsNotNull(aIterator); + AssertPointerIsNotNull(aResolver); + AssertPointerIsNotNull(aInfo); + + return AsCoreType(aInstance).Get().GetNextIp4AddressResolver(*aIterator, *aResolver, *aInfo); +} + +#endif // OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE + #endif // OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE && OPENTHREAD_CONFIG_MULTICAST_DNS_PUBLIC_API_ENABLE diff --git a/src/core/config/mdns.h b/src/core/config/mdns.h index addd14bf05a..a1bde09ed4b 100644 --- a/src/core/config/mdns.h +++ b/src/core/config/mdns.h @@ -68,6 +68,17 @@ #define OPENTHREAD_CONFIG_MULTICAST_DNS_PUBLIC_API_ENABLE 0 #endif +/** + * @def OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE + * + * Define to 1 for mDNS module to provide mechanisms and public APIs to iterate over registered host, service, and + * key entries, as well as browsers and resolvers. + * + */ +#ifndef OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE +#define OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE OPENTHREAD_CONFIG_MULTICAST_DNS_PUBLIC_API_ENABLE +#endif + /** * @def OPENTHREAD_CONFIG_MULTICAST_DNS_AUTO_ENABLE_ON_INFRA_IF * diff --git a/src/core/net/mdns.cpp b/src/core/net/mdns.cpp index df0a61fdd1a..06ce0445890 100644 --- a/src/core/net/mdns.cpp +++ b/src/core/net/mdns.cpp @@ -201,6 +201,8 @@ Error Core::UnregisterKey(const Key &aKey) return IsKeyForService(aKey) ? Unregister(aKey) : Unregister(aKey); } +#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE + Core::Iterator *Core::AllocateIterator(void) { return EntryIterator::Allocate(GetInstance()); } void Core::FreeIterator(Iterator &aIterator) { static_cast(aIterator).Free(); } @@ -220,6 +222,33 @@ Error Core::GetNextKey(Iterator &aIterator, Key &aKey, EntryState &aState) const return static_cast(aIterator).GetNextKey(aKey, aState); } +Error Core::GetNextBrowser(Iterator &aIterator, Browser &aBrowser, CacheInfo &aInfo) const +{ + return static_cast(aIterator).GetNextBrowser(aBrowser, aInfo); +} + +Error Core::GetNextSrvResolver(Iterator &aIterator, SrvResolver &aResolver, CacheInfo &aInfo) const +{ + return static_cast(aIterator).GetNextSrvResolver(aResolver, aInfo); +} + +Error Core::GetNextTxtResolver(Iterator &aIterator, TxtResolver &aResolver, CacheInfo &aInfo) const +{ + return static_cast(aIterator).GetNextTxtResolver(aResolver, aInfo); +} + +Error Core::GetNextIp6AddressResolver(Iterator &aIterator, AddressResolver &aResolver, CacheInfo &aInfo) const +{ + return static_cast(aIterator).GetNextIp6AddressResolver(aResolver, aInfo); +} + +Error Core::GetNextIp4AddressResolver(Iterator &aIterator, AddressResolver &aResolver, CacheInfo &aInfo) const +{ + return static_cast(aIterator).GetNextIp4AddressResolver(aResolver, aInfo); +} + +#endif // OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE + void Core::InvokeConflictCallback(const char *aName, const char *aServiceType) { if (mConflictCallback != nullptr) @@ -1643,6 +1672,8 @@ void Core::HostEntry::AppendNameTo(TxMessage &aTxMessage, Section aSection) return; } +#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE + Error Core::HostEntry::CopyInfoTo(Host &aHost, EntryState &aState) const { Error error = kErrorNone; @@ -1673,6 +1704,8 @@ Error Core::HostEntry::CopyInfoTo(Key &aKey, EntryState &aState) const return error; } +#endif // OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE + //---------------------------------------------------------------------------------------------------------------------- // Core::ServiceEntry @@ -2528,6 +2561,8 @@ void Core::ServiceEntry::AppendHostNameTo(TxMessage &aTxMessage, Section aSectio return; } +#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE + Error Core::ServiceEntry::CopyInfoTo(Service &aService, EntryState &aState, EntryIterator &aIterator) const { Error error = kErrorNone; @@ -2572,6 +2607,8 @@ Error Core::ServiceEntry::CopyInfoTo(Key &aKey, EntryState &aState) const return error; } +#endif // OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE + //---------------------------------------------------------------------------------------------------------------------- // Core::ServiceEntry::SubType @@ -5244,6 +5281,20 @@ void Core::BrowseCache::ReportResultsTo(ResultCallback &aCallback) const } } +#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE + +void Core::BrowseCache::CopyInfoTo(Browser &aBrowser, CacheInfo &aInfo) const +{ + aBrowser.mServiceType = mServiceType.AsCString(); + aBrowser.mSubTypeLabel = mSubTypeLabel.AsCString(); + aBrowser.mInfraIfIndex = Get().mInfraIfIndex; + aBrowser.mCallback = nullptr; + aInfo.mIsActive = IsActive(); + aInfo.mHasCachedResults = !mPtrEntries.IsEmpty(); +} + +#endif + //--------------------------------------------------------------------------------------------------------------------- // Core::BrowseCache::PtrEntry @@ -5532,6 +5583,20 @@ void Core::SrvCache::ConvertTo(SrvResult &aResult) const aResult.mInfraIfIndex = Get().mInfraIfIndex; } +#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE + +void Core::SrvCache::CopyInfoTo(SrvResolver &aResolver, CacheInfo &aInfo) const +{ + aResolver.mServiceInstance = mServiceInstance.AsCString(); + aResolver.mServiceType = mServiceType.AsCString(); + aResolver.mInfraIfIndex = Get().mInfraIfIndex; + aResolver.mCallback = nullptr; + aInfo.mIsActive = IsActive(); + aInfo.mHasCachedResults = mRecord.IsPresent(); +} + +#endif + //--------------------------------------------------------------------------------------------------------------------- // Core::TxtCache @@ -5703,6 +5768,20 @@ void Core::TxtCache::ConvertTo(TxtResult &aResult) const aResult.mInfraIfIndex = Get().mInfraIfIndex; } +#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE + +void Core::TxtCache::CopyInfoTo(TxtResolver &aResolver, CacheInfo &aInfo) const +{ + aResolver.mServiceInstance = mServiceInstance.AsCString(); + aResolver.mServiceType = mServiceType.AsCString(); + aResolver.mInfraIfIndex = Get().mInfraIfIndex; + aResolver.mCallback = nullptr; + aInfo.mIsActive = IsActive(); + aInfo.mHasCachedResults = mRecord.IsPresent(); +} + +#endif + //--------------------------------------------------------------------------------------------------------------------- // Core::AddrCache @@ -6029,6 +6108,19 @@ void Core::AddrCache::CommitNewResponseEntries(void) ScheduleTimer(); } +#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE + +void Core::AddrCache::CopyInfoTo(AddressResolver &aResolver, CacheInfo &aInfo) const +{ + aResolver.mHostName = mName.AsCString(); + aResolver.mInfraIfIndex = Get().mInfraIfIndex; + aResolver.mCallback = nullptr; + aInfo.mIsActive = IsActive(); + aInfo.mHasCachedResults = !mCommittedEntries.IsEmpty(); +} + +#endif + //--------------------------------------------------------------------------------------------------------------------- // Core::AddrCache::AddrEntry @@ -6119,6 +6211,8 @@ void Core::Ip4AddrCache::PrepareAQuestion(TxMessage &aQuery) { PrepareQueryQuest //--------------------------------------------------------------------------------------------------------------------- // Core::Iterator +#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE + Core::EntryIterator::EntryIterator(Instance &aInstance) : InstanceLocator(aInstance) , mType(kUnspecified) @@ -6213,6 +6307,123 @@ Error Core::EntryIterator::GetNextKey(Key &aKey, EntryState &aState) return error; } +Error Core::EntryIterator::GetNextBrowser(Browser &aBrowser, CacheInfo &aInfo) +{ + Error error = kErrorNone; + + if (mType == kUnspecified) + { + mBrowseCache = Get().mBrowseCacheList.GetHead(); + mType = kBrowser; + } + else + { + VerifyOrExit(mType == kBrowser, error = kErrorInvalidArgs); + } + + VerifyOrExit(mBrowseCache != nullptr, error = kErrorNotFound); + + mBrowseCache->CopyInfoTo(aBrowser, aInfo); + mBrowseCache = mBrowseCache->GetNext(); + +exit: + return error; +} + +Error Core::EntryIterator::GetNextSrvResolver(SrvResolver &aResolver, CacheInfo &aInfo) +{ + Error error = kErrorNone; + + if (mType == kUnspecified) + { + mSrvCache = Get().mSrvCacheList.GetHead(); + mType = kSrvResolver; + } + else + { + VerifyOrExit(mType == kSrvResolver, error = kErrorInvalidArgs); + } + + VerifyOrExit(mSrvCache != nullptr, error = kErrorNotFound); + + mSrvCache->CopyInfoTo(aResolver, aInfo); + mSrvCache = mSrvCache->GetNext(); + +exit: + return error; +} + +Error Core::EntryIterator::GetNextTxtResolver(TxtResolver &aResolver, CacheInfo &aInfo) +{ + Error error = kErrorNone; + + if (mType == kUnspecified) + { + mTxtCache = Get().mTxtCacheList.GetHead(); + mType = kTxtResolver; + } + else + { + VerifyOrExit(mType == kTxtResolver, error = kErrorInvalidArgs); + } + + VerifyOrExit(mTxtCache != nullptr, error = kErrorNotFound); + + mTxtCache->CopyInfoTo(aResolver, aInfo); + mTxtCache = mTxtCache->GetNext(); + +exit: + return error; +} + +Error Core::EntryIterator::GetNextIp6AddressResolver(AddressResolver &aResolver, CacheInfo &aInfo) +{ + Error error = kErrorNone; + + if (mType == kUnspecified) + { + mIp6AddrCache = Get().mIp6AddrCacheList.GetHead(); + mType = kIp6AddrResolver; + } + else + { + VerifyOrExit(mType == kIp6AddrResolver, error = kErrorInvalidArgs); + } + + VerifyOrExit(mIp6AddrCache != nullptr, error = kErrorNotFound); + + mIp6AddrCache->CopyInfoTo(aResolver, aInfo); + mIp6AddrCache = mIp6AddrCache->GetNext(); + +exit: + return error; +} + +Error Core::EntryIterator::GetNextIp4AddressResolver(AddressResolver &aResolver, CacheInfo &aInfo) +{ + Error error = kErrorNone; + + if (mType == kUnspecified) + { + mIp4AddrCache = Get().mIp4AddrCacheList.GetHead(); + mType = kIp4AddrResolver; + } + else + { + VerifyOrExit(mType == kIp4AddrResolver, error = kErrorInvalidArgs); + } + + VerifyOrExit(mIp4AddrCache != nullptr, error = kErrorNotFound); + + mIp4AddrCache->CopyInfoTo(aResolver, aInfo); + mIp4AddrCache = mIp4AddrCache->GetNext(); + +exit: + return error; +} + +#endif // OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE + } // namespace Multicast } // namespace Dns } // namespace ot diff --git a/src/core/net/mdns.hpp b/src/core/net/mdns.hpp index 6001e8f587b..a420bd47305 100644 --- a/src/core/net/mdns.hpp +++ b/src/core/net/mdns.hpp @@ -122,6 +122,7 @@ class Core : public InstanceLocator, private NonCopyable typedef otMdnsAddressResult AddressResult; ///< Address result. typedef otMdnsAddressAndTtl AddressAndTtl; ///< Address and TTL. typedef otMdnsIterator Iterator; ///< An entry iterator. + typedef otMdnsCacheInfo CacheInfo; ///< Cache information. /** * Represents a socket address info. @@ -577,6 +578,8 @@ class Core : public InstanceLocator, private NonCopyable */ void SetMaxMessageSize(uint16_t aMaxSize) { mMaxMessageSize = aMaxSize; } +#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE + /** * Allocates a new iterator. * @@ -643,6 +646,95 @@ class Core : public InstanceLocator, private NonCopyable */ Error GetNextKey(Iterator &aIterator, Key &aKey, EntryState &aState) const; + /** + * Iterates over browsers. + * + * On success, @p aBrowser is populated with information about the next browser. Pointers within the `Browser` + * structure remain valid until the next call to any OpenThread stack's public or platform API/callback. + * + * @param[in] aIterator The iterator to use. + * @param[out] aBrowser A `Browser` to return the information about the next browser. + * @param[out] aInfo A `CacheInfo` to return additional information. + * + * @retval kErrorNone @p aBrowser, @p aInfo, & @p aIterator are updated successfully. + * @retval kErrorNotFound Reached the end of the list. + * @retval kErrorInvalidArg @p aIterator is not valid. + * + */ + Error GetNextBrowser(Iterator &aIterator, Browser &aBrowser, CacheInfo &aInfo) const; + + /** + * Iterates over SRV resolvers. + * + * On success, @p aResolver is populated with information about the next resolver. Pointers within the `SrvResolver` + * structure remain valid until the next call to any OpenThread stack's public or platform API/callback. + * + * @param[in] aIterator The iterator to use. + * @param[out] aResolver An `SrvResolver` to return the information about the next resolver. + * @param[out] aInfo A `CacheInfo` to return additional information. + * + * @retval kErrorNone @p aResolver, @p aInfo, & @p aIterator are updated successfully. + * @retval kErrorNotFound Reached the end of the list. + * @retval kErrorInvalidArg @p aIterator is not valid. + * + */ + Error GetNextSrvResolver(Iterator &aIterator, SrvResolver &aResolver, CacheInfo &aInfo) const; + + /** + * Iterates over TXT resolvers. + * + * On success, @p aResolver is populated with information about the next resolver. Pointers within the `TxtResolver` + * structure remain valid until the next call to any OpenThread stack's public or platform API/callback. + * + * @param[in] aIterator The iterator to use. + * @param[out] aResolver A `TxtResolver` to return the information about the next resolver. + * @param[out] aInfo A `CacheInfo` to return additional information. + * + * @retval kErrorNone @p aResolver, @p aInfo, & @p aIterator are updated successfully. + * @retval kErrorNotFound Reached the end of the list. + * @retval kErrorInvalidArg @p aIterator is not valid. + * + */ + Error GetNextTxtResolver(Iterator &aIterator, TxtResolver &aResolver, CacheInfo &aInfo) const; + + /** + * Iterates over IPv6 address resolvers. + * + * On success, @p aResolver is populated with information about the next resolver. Pointers within the + * `AddressResolver` structure remain valid until the next call to any OpenThread stack's public or platform + * API/callback. + * + * @param[in] aIterator The iterator to use. + * @param[out] aResolver An `AddressResolver to return the information about the next resolver. + * @param[out] aInfo A `CacheInfo` to return additional information. + * + * @retval kErrorNone @p aResolver, @p aInfo, & @p aIterator are updated successfully. + * @retval kErrorNotFound Reached the end of the list. + * @retval kErrorInvalidArg @p aIterator is not valid. + * + */ + Error GetNextIp6AddressResolver(Iterator &aIterator, AddressResolver &aResolver, CacheInfo &aInfo) const; + + /** + * Iterates over IPv4 address resolvers. + * + * On success, @p aResolver is populated with information about the next resolver. Pointers within the + * `AddressResolver` structure remain valid until the next call to any OpenThread stack's public or platform + * API/callback. + * + * @param[in] aIterator The iterator to use. + * @param[out] aResolver An `AddressResolver to return the information about the next resolver. + * @param[out] aInfo A `CacheInfo` to return additional information. + * + * @retval kErrorNone @p aResolver, @p aInfo, & @p aIterator are updated successfully. + * @retval kErrorNotFound Reached the end of the list. + * @retval kErrorInvalidArg @p aIterator is not valid. + * + */ + Error GetNextIp4AddressResolver(Iterator &aIterator, AddressResolver &aResolver, CacheInfo &aInfo) const; + +#endif // OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE + private: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -975,8 +1067,10 @@ class Core : public InstanceLocator, private NonCopyable void ClearAppendState(void); void PrepareResponse(TxMessage &aResponse, TimeMilli aNow); void HandleConflict(void); +#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE Error CopyInfoTo(Host &aHost, EntryState &aState) const; Error CopyInfoTo(Key &aKey, EntryState &aState) const; +#endif private: Error Init(Instance &aInstance, const char *aName); @@ -1032,8 +1126,10 @@ class Core : public InstanceLocator, private NonCopyable void ClearAppendState(void); void PrepareResponse(TxMessage &aResponse, TimeMilli aNow); void HandleConflict(void); +#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE Error CopyInfoTo(Service &aService, EntryState &aState, EntryIterator &aIterator) const; Error CopyInfoTo(Key &aKey, EntryState &aState) const; +#endif private: class SubType : public LinkedListEntry, public Heap::Allocatable, private ot::NonCopyable @@ -1563,6 +1659,9 @@ class Core : public InstanceLocator, private NonCopyable Error Add(const Browser &aBrowser); void Remove(const Browser &aBrowser); void ProcessResponseRecord(const Message &aMessage, uint16_t aRecordOffset); +#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE + void CopyInfoTo(Browser &aBrowser, CacheInfo &aInfo) const; +#endif private: struct PtrEntry : public LinkedListEntry, public Heap::Allocatable @@ -1663,6 +1762,9 @@ class Core : public InstanceLocator, private NonCopyable Error Add(const SrvResolver &aResolver); void Remove(const SrvResolver &aResolver); void ProcessResponseRecord(const Message &aMessage, uint16_t aRecordOffset); +#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE + void CopyInfoTo(SrvResolver &aResolver, CacheInfo &aInfo) const; +#endif private: Error Init(Instance &aInstance, const char *aServiceInstance, const char *aServiceType); @@ -1698,6 +1800,9 @@ class Core : public InstanceLocator, private NonCopyable Error Add(const TxtResolver &aResolver); void Remove(const TxtResolver &aResolver); void ProcessResponseRecord(const Message &aMessage, uint16_t aRecordOffset); +#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE + void CopyInfoTo(TxtResolver &aResolver, CacheInfo &aInfo) const; +#endif private: Error Init(Instance &aInstance, const char *aServiceInstance, const char *aServiceType); @@ -1730,6 +1835,9 @@ class Core : public InstanceLocator, private NonCopyable Error Add(const AddressResolver &aResolver); void Remove(const AddressResolver &aResolver); void CommitNewResponseEntries(void); +#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE + void CopyInfoTo(AddressResolver &aResolver, CacheInfo &aInfo) const; +#endif protected: struct AddrEntry : public LinkedListEntry, public Heap::Allocatable @@ -1802,6 +1910,8 @@ class Core : public InstanceLocator, private NonCopyable // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE + class EntryIterator : public Iterator, public InstanceLocator, public Heap::Allocatable { friend class Heap::Allocatable; @@ -1811,6 +1921,11 @@ class Core : public InstanceLocator, private NonCopyable Error GetNextHost(Host &aHost, EntryState &aState); Error GetNextService(Service &aService, EntryState &aState); Error GetNextKey(Key &aKey, EntryState &aState); + Error GetNextBrowser(Browser &aBrowser, CacheInfo &aInfo); + Error GetNextSrvResolver(SrvResolver &aResolver, CacheInfo &aInfo); + Error GetNextTxtResolver(TxtResolver &aResolver, CacheInfo &aInfo); + Error GetNextIp6AddressResolver(AddressResolver &aResolver, CacheInfo &aInfo); + Error GetNextIp4AddressResolver(AddressResolver &aResolver, CacheInfo &aInfo); private: static constexpr uint16_t kArrayCapacityIncrement = 32; @@ -1822,6 +1937,11 @@ class Core : public InstanceLocator, private NonCopyable kService, kHostKey, kServiceKey, + kBrowser, + kSrvResolver, + kTxtResolver, + kIp6AddrResolver, + kIp4AddrResolver, }; explicit EntryIterator(Instance &aInstance); @@ -1832,11 +1952,18 @@ class Core : public InstanceLocator, private NonCopyable { const HostEntry *mHostEntry; const ServiceEntry *mServiceEntry; + const BrowseCache *mBrowseCache; + const SrvCache *mSrvCache; + const TxtCache *mTxtCache; + const Ip6AddrCache *mIp6AddrCache; + const Ip4AddrCache *mIp4AddrCache; }; Heap::Array mSubTypeArray; }; +#endif // OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - template OwningList &GetEntryList(void); diff --git a/tests/toranj/openthread-core-toranj-config-simulation.h b/tests/toranj/openthread-core-toranj-config-simulation.h index 6bf449e5f08..e0f030e57fc 100644 --- a/tests/toranj/openthread-core-toranj-config-simulation.h +++ b/tests/toranj/openthread-core-toranj-config-simulation.h @@ -56,6 +56,8 @@ #define OPENTHREAD_CONFIG_MULTICAST_DNS_PUBLIC_API_ENABLE 1 +#define OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE 1 + #define OPENTHREAD_CONFIG_MULTICAST_DNS_AUTO_ENABLE_ON_INFRA_IF 0 #define OPENTHREAD_SIMULATION_MDNS_SOCKET_IMPLEMENT_POSIX 1