From d4ec3712d1b5bd34e9a649d11e8ae53c6602beb2 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Tue, 4 Jun 2024 13:04:15 -0700 Subject: [PATCH] [mdns] add APIs to iterate over browsers and resolvers (#10140) 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 76f4fe191..cf33632b0 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 (417) +#define OPENTHREAD_API_VERSION (418) /** * @addtogroup api-instance diff --git a/include/openthread/mdns.h b/include/openthread/mdns.h index a88c70f83..56ffff09c 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 977bb7fa1..8a4e08ee4 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 @@ exit: 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 @@ exit: 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 242b6d9a4..df6b4fe4c 100644 --- a/src/cli/cli_mdns.hpp +++ b/src/cli/cli_mdns.hpp @@ -111,6 +111,7 @@ private: 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 8563303e6..14c779d9c 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 addd14bf0..a1bde09ed 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 84c0cd199..8fe50f77e 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) @@ -1645,6 +1674,8 @@ exit: return; } +#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE + Error Core::HostEntry::CopyInfoTo(Host &aHost, EntryState &aState) const { Error error = kErrorNone; @@ -1675,6 +1706,8 @@ exit: return error; } +#endif // OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE + //---------------------------------------------------------------------------------------------------------------------- // Core::ServiceEntry @@ -2530,6 +2563,8 @@ exit: return; } +#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE + Error Core::ServiceEntry::CopyInfoTo(Service &aService, EntryState &aState, EntryIterator &aIterator) const { Error error = kErrorNone; @@ -2574,6 +2609,8 @@ exit: return error; } +#endif // OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE + //---------------------------------------------------------------------------------------------------------------------- // Core::ServiceEntry::SubType @@ -5215,6 +5252,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 @@ -5503,6 +5554,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 @@ -5674,6 +5739,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 @@ -6000,6 +6079,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 @@ -6090,6 +6182,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) @@ -6184,6 +6278,123 @@ exit: 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 33f848870..1065bf160 100644 --- a/src/core/net/mdns.hpp +++ b/src/core/net/mdns.hpp @@ -122,6 +122,7 @@ public: 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 @@ public: */ void SetMaxMessageSize(uint16_t aMaxSize) { mMaxMessageSize = aMaxSize; } +#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE + /** * Allocates a new iterator. * @@ -643,6 +646,95 @@ public: */ 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: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -976,8 +1068,10 @@ private: 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); @@ -1033,8 +1127,10 @@ private: 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 @@ -1554,6 +1650,9 @@ private: 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 @@ -1654,6 +1753,9 @@ private: 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); @@ -1689,6 +1791,9 @@ private: 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); @@ -1721,6 +1826,9 @@ private: 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 @@ -1793,6 +1901,8 @@ private: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE + class EntryIterator : public Iterator, public InstanceLocator, public Heap::Allocatable { friend class Heap::Allocatable; @@ -1802,6 +1912,11 @@ private: 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; @@ -1813,6 +1928,11 @@ private: kService, kHostKey, kServiceKey, + kBrowser, + kSrvResolver, + kTxtResolver, + kIp6AddrResolver, + kIp4AddrResolver, }; explicit EntryIterator(Instance &aInstance); @@ -1823,11 +1943,18 @@ private: { 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 6bf449e5f..e0f030e57 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