[mdns] add APIs to iterate over registered host/service/key entries (#9954)

This commit is contained in:
Abtin Keshavarzian
2024-04-22 10:54:47 -07:00
committed by GitHub
parent 9af7203ff3
commit 9681690fab
7 changed files with 647 additions and 36 deletions
+1 -1
View File
@@ -53,7 +53,7 @@ extern "C" {
* @note This number versions both OpenThread platform and user APIs.
*
*/
#define OPENTHREAD_API_VERSION (406)
#define OPENTHREAD_API_VERSION (407)
/**
* @addtogroup api-instance
+112 -10
View File
@@ -133,6 +133,24 @@ typedef otPlatDnssdService otMdnsService;
*/
typedef otPlatDnssdKey otMdnsKey;
/**
* Represents an mDNS entry iterator.
*
*/
typedef struct otMdnsIterator otMdnsIterator;
/**
* Represents a host/service/key entry state.
*
*/
typedef enum otMdnsEntryState
{
OT_MDNS_ENTRY_STATE_PROBING, ///< Probing to claim the name.
OT_MDNS_ENTRY_STATE_REGISTERED, ///< Entry is successfully registered.
OT_MDNS_ENTRY_STATE_CONFLICT, ///< Name conflict was detected.
OT_MDNS_ENTRY_STATE_REMOVING, ///< Entry is being removed (sending "goodbye" announcements).
} otMdnsEntryState;
/**
* Enables or disables the mDNS module.
*
@@ -335,10 +353,11 @@ otError otMdnsUnregisterService(otInstance *aInstance, const otMdnsService *aSer
*
* The fields in @p aKey follow these rules:
*
* - If the key is associated with a host entry, `mName` specifies the host name and `mServcieType` MUST be NULL.
* - If the key is associated with a service entry, `mName` specifies the service instance label (always treated as
* a single label) and `mServiceType` specifies the service type (e.g., "_tst._udp"). In this case the DNS name for
* key record is `<mName>.<mServiceTye>`.
* - If the key is associated with a host entry, the `mName` field specifies the host name and the `mServiceType` MUST
* be NULL.
* - If the key is associated with a service entry, the `mName` filed specifies the service instance label (always
* treated as a single label) and the `mServiceType` filed specifies the service type (e.g., "_tst._udp"). In this
* case the DNS name for key record is `<mName>.<mServiceTye>`.
* - The `mKeyData` field contains the key record's data with `mKeyDataLength` as its length in byes.
* - The `mTtl` specifies the TTL if non-zero. If zero, the mDNS module will use the default TTL of 120 seconds.
* - Other fields in @p aKey structure are ignored in an `otMdnsRegisterKey()` call.
@@ -368,10 +387,11 @@ otError otMdnsRegisterKey(otInstance *aInstance,
*
* The fields in @p aKey follow these rules:
*
* - If the key is associated with a host entry, `mName` specifies the host name and `mServcieType` MUST be NULL.
* - If the key is associated with a service entry, `mName` specifies the service instance label (always treated as
* a single label) and `mServiceType` specifies the service type (e.g., "_tst._udp"). In this case the DNS name for
* key record is `<mName>.<mServiceTye>`.
* - If the key is associated with a host entry, the `mName` field specifies the host name and the `mServiceType` MUST
* be NULL.
* - If the key is associated with a service entry, the `mName` filed specifies the service instance label (always
* treated as a single label) and the `mServiceType` filed specifies the service type (e.g., "_tst._udp"). In this
* case the DNS name for key record is `<mName>.<mServiceTye>`.
* - Other fields in @p aKey structure are ignored in an `otMdnsUnregisterKey()` call.
*
* If there is no previously registered key with the same name, no action is performed.
@@ -388,6 +408,88 @@ otError otMdnsRegisterKey(otInstance *aInstance,
*/
otError otMdnsUnregisterKey(otInstance *aInstance, const otMdnsKey *aKey);
/**
* Allocates a new iterator.
*
* An allocated iterator must be freed by the caller using `otMdnsFreeIterator()`.
*
* @param[in] aInstance The OpenThread instance.
*
* @returns A pointer to the allocated iterator, or `NULL` if it fails to allocate.
*
*/
otMdnsIterator *otMdnsAllocateIterator(otInstance *aInstance);
/**
* Frees a previously allocated iterator.
*
* @param[in] aInstance The OpenThread instance.
* @param[in] aIterator The iterator to free.
*
*/
void otMdnsFreeIterator(otInstance *aInstance, otMdnsIterator *aIterator);
/**
* Iterates over registered host entries.
*
* 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.
*
* @param[in] aInstance The OpenThread instance.
* @param[in] aIterator Pointer to the iterator.
* @param[out] aHost Pointer to an `otMdnsHost` to return the information about the next host entry.
* @param[out] aState Pointer to an `otMdnsEntryState` to return the entry state.
*
* @retval OT_ERROR_NONE @p aHost, @p aState, & @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 otMdnsGetNextHost(otInstance *aInstance,
otMdnsIterator *aIterator,
otMdnsHost *aHost,
otMdnsEntryState *aState);
/**
* Iterates over registered service entries.
*
* 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.
*
* @param[in] aInstance The OpenThread instance.
* @param[in] aIterator Pointer to the iterator to use.
* @param[out] aService Pointer to an `otMdnsService` to return the information about the next service entry.
* @param[out] aState Pointer to an `otMdnsEntryState` to return the entry state.
*
* @retval OT_ERROR_NONE @p aService, @p aState, & @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 otMdnsGetNextService(otInstance *aInstance,
otMdnsIterator *aIterator,
otMdnsService *aService,
otMdnsEntryState *aState);
/**
* Iterates over registered key entries.
*
* 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.
*
* @param[in] aInstance The OpenThread instance.
* @param[in] aIterator Pointer to the iterator to use.
* @param[out] aKey Pointer to an `otMdnsKey` to return the information about the next key entry.
* @param[out] aState Pointer to an `otMdnsEntryState` to return the entry state.
*
* @retval OT_ERROR_NONE @p aKey, @p aState, & @p aIterator are updated successfully.
* @retval OT_ERROR_NOT_FOUND Reached the end of the list.
* @retval OT_ERROR_INVALID_ARG Iterator is not valid.
*
*/
otError otMdnsGetNextKey(otInstance *aInstance, otMdnsIterator *aIterator, otMdnsKey *aKey, otMdnsEntryState *aState);
typedef struct otMdnsBrowseResult otMdnsBrowseResult;
typedef struct otMdnsSrvResult otMdnsSrvResult;
typedef struct otMdnsTxtResult otMdnsTxtResult;
@@ -661,7 +763,7 @@ otError otMdnsStopTxtResolver(otInstance *aInstance, const otMdnsTxtResolver *aR
*
* Initiates a continuous IPv6 address resolver for the specified host name in @p aResolver.
*
* Discovered addresses are reported through the `mCallback` function in @ p aResolver. The callback is invoked
* Discovered addresses are reported through the `mCallback` function in @p aResolver. The callback is invoked
* whenever addresses are added or removed, providing an updated list. If all addresses are removed, the callback is
* invoked with an empty list (`mAddresses` will be NULL, and `mAddressesLength` will be zero).
*
@@ -700,7 +802,7 @@ otError otMdnsStopIp6AddressResolver(otInstance *aInstance, const otMdnsAddressR
*
* Initiates a continuous IPv4 address resolver for the specified host name in @p aResolver.
*
* Discovered addresses are reported through the `mCallback` function in @ p aResolver. The IPv4 addresses are
* Discovered addresses are reported through the `mCallback` function in @p aResolver. The IPv4 addresses are
* represented using the IPv4-mapped IPv6 address format in `mAddresses` array. The callback is invoked whenever
* addresses are added or removed, providing an updated list. If all addresses are removed, the callback is invoked
* with an empty list (`mAddresses` will be NULL, and `mAddressesLength` will be zero).
+138 -3
View File
@@ -148,6 +148,29 @@ void Mdns::OutputKey(const otMdnsKey &aKey)
OutputLine(kIndentSize, "ttl: %lu", ToUlong(aKey.mTtl));
}
void Mdns::OutputState(otMdnsEntryState aState)
{
const char *stateString = "";
switch (aState)
{
case OT_MDNS_ENTRY_STATE_PROBING:
stateString = "probing";
break;
case OT_MDNS_ENTRY_STATE_REGISTERED:
stateString = "registered";
break;
case OT_MDNS_ENTRY_STATE_CONFLICT:
stateString = "conflict";
break;
case OT_MDNS_ENTRY_STATE_REMOVING:
stateString = "removing";
break;
}
OutputLine(kIndentSize, "state: %s", stateString);
}
template <> otError Mdns::Process<Cmd("register")>(Arg aArgs[])
{
// mdns [async] [host|service|key] <entry specific args>
@@ -458,6 +481,117 @@ exit:
return error;
}
template <> otError Mdns::Process<Cmd("hosts")>(Arg aArgs[])
{
otError error = OT_ERROR_NONE;
otMdnsIterator *iterator = nullptr;
otMdnsHost host;
otMdnsEntryState state;
VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
iterator = otMdnsAllocateIterator(GetInstancePtr());
VerifyOrExit(iterator != nullptr, error = OT_ERROR_NO_BUFS);
while (true)
{
error = otMdnsGetNextHost(GetInstancePtr(), iterator, &host, &state);
if (error == OT_ERROR_NOT_FOUND)
{
error = OT_ERROR_NONE;
ExitNow();
}
SuccessOrExit(error);
OutputHost(host);
OutputState(state);
}
exit:
if (iterator != nullptr)
{
otMdnsFreeIterator(GetInstancePtr(), iterator);
}
return error;
}
template <> otError Mdns::Process<Cmd("services")>(Arg aArgs[])
{
otError error = OT_ERROR_NONE;
otMdnsIterator *iterator = nullptr;
otMdnsService service;
otMdnsEntryState state;
VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
iterator = otMdnsAllocateIterator(GetInstancePtr());
VerifyOrExit(iterator != nullptr, error = OT_ERROR_NO_BUFS);
while (true)
{
error = otMdnsGetNextService(GetInstancePtr(), iterator, &service, &state);
if (error == OT_ERROR_NOT_FOUND)
{
error = OT_ERROR_NONE;
ExitNow();
}
SuccessOrExit(error);
OutputService(service);
OutputState(state);
}
exit:
if (iterator != nullptr)
{
otMdnsFreeIterator(GetInstancePtr(), iterator);
}
return error;
}
template <> otError Mdns::Process<Cmd("keys")>(Arg aArgs[])
{
otError error = OT_ERROR_NONE;
otMdnsIterator *iterator = nullptr;
otMdnsKey key;
otMdnsEntryState state;
VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
iterator = otMdnsAllocateIterator(GetInstancePtr());
VerifyOrExit(iterator != nullptr, error = OT_ERROR_NO_BUFS);
while (true)
{
error = otMdnsGetNextKey(GetInstancePtr(), iterator, &key, &state);
if (error == OT_ERROR_NOT_FOUND)
{
error = OT_ERROR_NONE;
ExitNow();
}
SuccessOrExit(error);
OutputKey(key);
OutputState(state);
}
exit:
if (iterator != nullptr)
{
otMdnsFreeIterator(GetInstancePtr(), iterator);
}
return error;
}
otError Mdns::ParseStartOrStop(const Arg &aArg, bool &aIsStart)
{
otError error = OT_ERROR_NONE;
@@ -743,9 +877,10 @@ otError Mdns::Process(Arg aArgs[])
}
static constexpr Command kCommands[] = {
CmdEntry("browser"), CmdEntry("disable"), CmdEntry("enable"), CmdEntry("ip4resolver"),
CmdEntry("ip6resolver"), CmdEntry("register"), CmdEntry("srvresolver"), CmdEntry("state"),
CmdEntry("txtresolver"), CmdEntry("unicastquestion"), CmdEntry("unregister"),
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"),
};
#undef CmdEntry
+1
View File
@@ -110,6 +110,7 @@ private:
void OutputHost(const otMdnsHost &aHost);
void OutputService(const otMdnsService &aService);
void OutputKey(const otMdnsKey &aKey);
void OutputState(otMdnsEntryState aState);
otError ProcessRegisterHost(Arg aArgs[]);
otError ProcessRegisterService(Arg aArgs[]);
otError ProcessRegisterKey(Arg aArgs[]);
+42
View File
@@ -115,6 +115,48 @@ otError otMdnsUnregisterKey(otInstance *aInstance, const otMdnsKey *aKey)
return AsCoreType(aInstance).Get<Dns::Multicast::Core>().UnregisterKey(*aKey);
}
otMdnsIterator *otMdnsAllocateIterator(otInstance *aInstance)
{
return AsCoreType(aInstance).Get<Dns::Multicast::Core>().AllocateIterator();
}
void otMdnsFreeIterator(otInstance *aInstance, otMdnsIterator *aIterator)
{
AssertPointerIsNotNull(aIterator);
AsCoreType(aInstance).Get<Dns::Multicast::Core>().FreeIterator(*aIterator);
}
otError otMdnsGetNextHost(otInstance *aInstance, otMdnsIterator *aIterator, otMdnsHost *aHost, otMdnsEntryState *aState)
{
AssertPointerIsNotNull(aIterator);
AssertPointerIsNotNull(aHost);
AssertPointerIsNotNull(aState);
return AsCoreType(aInstance).Get<Dns::Multicast::Core>().GetNextHost(*aIterator, *aHost, *aState);
}
otError otMdnsGetNextService(otInstance *aInstance,
otMdnsIterator *aIterator,
otMdnsService *aService,
otMdnsEntryState *aState)
{
AssertPointerIsNotNull(aIterator);
AssertPointerIsNotNull(aService);
AssertPointerIsNotNull(aState);
return AsCoreType(aInstance).Get<Dns::Multicast::Core>().GetNextService(*aIterator, *aService, *aState);
}
otError otMdnsGetNextKey(otInstance *aInstance, otMdnsIterator *aIterator, otMdnsKey *aKey, otMdnsEntryState *aState)
{
AssertPointerIsNotNull(aIterator);
AssertPointerIsNotNull(aKey);
AssertPointerIsNotNull(aState);
return AsCoreType(aInstance).Get<Dns::Multicast::Core>().GetNextKey(*aIterator, *aKey, *aState);
}
otError otMdnsStartBrowser(otInstance *aInstance, const otMdnsBrowser *aBroswer)
{
AssertPointerIsNotNull(aBroswer);
+214 -6
View File
@@ -201,6 +201,25 @@ Error Core::UnregisterKey(const Key &aKey)
return IsKeyForService(aKey) ? Unregister<ServiceEntry>(aKey) : Unregister<HostEntry>(aKey);
}
Core::Iterator *Core::AllocateIterator(void) { return EntryIterator::Allocate(GetInstance()); }
void Core::FreeIterator(Iterator &aIterator) { static_cast<EntryIterator &>(aIterator).Free(); }
Error Core::GetNextHost(Iterator &aIterator, Host &aHost, EntryState &aState) const
{
return static_cast<EntryIterator &>(aIterator).GetNextHost(aHost, aState);
}
Error Core::GetNextService(Iterator &aIterator, Service &aService, EntryState &aState) const
{
return static_cast<EntryIterator &>(aIterator).GetNextService(aService, aState);
}
Error Core::GetNextKey(Iterator &aIterator, Key &aKey, EntryState &aState) const
{
return static_cast<EntryIterator &>(aIterator).GetNextKey(aKey, aState);
}
void Core::InvokeConflictCallback(const char *aName, const char *aServiceType)
{
if (mConflictCallback != nullptr)
@@ -1256,6 +1275,23 @@ void Core::Entry::AppendNsecRecordTo(TxMessage &aTxMessage,
mAppendedNsec = true;
}
Error Core::Entry::CopyKeyInfoTo(Key &aKey, EntryState &aState) const
{
Error error = kErrorNone;
VerifyOrExit(mKeyRecord.IsPresent(), error = kErrorNotFound);
aKey.mKeyData = mKeyData.GetBytes();
aKey.mKeyDataLength = mKeyData.GetLength();
aKey.mClass = ResourceRecord::kClassInternet;
aKey.mTtl = mKeyRecord.GetTtl();
aKey.mInfraIfIndex = Get<Core>().mInfraIfIndex;
aState = static_cast<EntryState>(GetState());
exit:
return error;
}
//----------------------------------------------------------------------------------------------------------------------
// Core::HostEntry
@@ -1584,6 +1620,36 @@ exit:
return;
}
Error Core::HostEntry::CopyInfoTo(Host &aHost, EntryState &aState) const
{
Error error = kErrorNone;
VerifyOrExit(mAddrRecord.IsPresent(), error = kErrorNotFound);
aHost.mHostName = mName.AsCString();
aHost.mAddresses = mAddresses.AsCArray();
aHost.mAddressesLength = mAddresses.GetLength();
aHost.mTtl = mAddrRecord.GetTtl();
aHost.mInfraIfIndex = Get<Core>().mInfraIfIndex;
aState = static_cast<EntryState>(GetState());
exit:
return error;
}
Error Core::HostEntry::CopyInfoTo(Key &aKey, EntryState &aState) const
{
Error error;
SuccessOrExit(error = CopyKeyInfoTo(aKey, aState));
aKey.mName = mName.AsCString();
aKey.mServiceType = nullptr;
exit:
return error;
}
//----------------------------------------------------------------------------------------------------------------------
// Core::ServiceEntry
@@ -2198,9 +2264,10 @@ void Core::ServiceEntry::UpdateServiceTypes(void)
// This method updates the `mServiceTypes` list adding or
// removing this `ServiceEntry` info.
//
// It is called whenever `ServcieEntry` state gets changed or an
// PTR record is added or removed. The service is valid when
// entry is registered and we have a PTR with non-zero TTL.
// It is called whenever the `ServiceEntry` state gets changed
// or a PTR record is added or removed. The service is valid
// when entry is registered and we have a PTR with non-zero
// TTL.
bool shouldAdd = (GetState() == kRegistered) && mPtrRecord.CanAnswer();
ServiceType *serviceType;
@@ -2235,7 +2302,7 @@ void Core::ServiceEntry::UpdateServiceTypes(void)
// the `mServiceTypes` list. It is safe to
// remove here as this method will never be
// called while we are iterating over the
// `mServcieTypes` list.
// `mServiceTypes` list.
Get<Core>().mServiceTypes.RemoveMatching(*serviceType);
}
@@ -2430,6 +2497,50 @@ exit:
return;
}
Error Core::ServiceEntry::CopyInfoTo(Service &aService, EntryState &aState, EntryIterator &aIterator) const
{
Error error = kErrorNone;
VerifyOrExit(mPtrRecord.IsPresent(), error = kErrorNotFound);
aIterator.mSubTypeArray.Free();
for (const SubType &subType : mSubTypes)
{
SuccessOrAssert(aIterator.mSubTypeArray.PushBack(subType.mLabel.AsCString()));
}
aService.mHostName = mHostName.AsCString();
aService.mServiceInstance = mServiceInstance.AsCString();
aService.mServiceType = mServiceType.AsCString();
aService.mSubTypeLabels = aIterator.mSubTypeArray.AsCArray();
aService.mSubTypeLabelsLength = aIterator.mSubTypeArray.GetLength();
aService.mTxtData = mTxtData.GetBytes();
aService.mTxtDataLength = mTxtData.GetLength();
aService.mPort = mPort;
aService.mPriority = mPriority;
aService.mWeight = mWeight;
aService.mTtl = mPtrRecord.GetTtl();
aService.mInfraIfIndex = Get<Core>().mInfraIfIndex;
aState = static_cast<EntryState>(GetState());
exit:
return error;
}
Error Core::ServiceEntry::CopyInfoTo(Key &aKey, EntryState &aState) const
{
Error error;
SuccessOrExit(error = CopyKeyInfoTo(aKey, aState));
aKey.mName = mServiceInstance.AsCString();
aKey.mServiceType = mServiceType.AsCString();
exit:
return error;
}
//----------------------------------------------------------------------------------------------------------------------
// Core::ServiceEntry::SubType
@@ -2486,9 +2597,9 @@ exit:
return error;
}
bool Core::ServiceType::Matches(const Name &aServcieTypeName) const
bool Core::ServiceType::Matches(const Name &aServiceTypeName) const
{
return aServcieTypeName.Matches(/* aFirstLabel */ nullptr, mServiceType.AsCString(), kLocalDomain);
return aServiceTypeName.Matches(/* aFirstLabel */ nullptr, mServiceType.AsCString(), kLocalDomain);
}
bool Core::ServiceType::Matches(const Heap::String &aServiceType) const
@@ -5984,6 +6095,103 @@ exit:
void Core::Ip4AddrCache::PrepareAQuestion(TxMessage &aQuery) { PrepareQueryQuestion(aQuery, ResourceRecord::kTypeA); }
//---------------------------------------------------------------------------------------------------------------------
// Core::Iterator
Core::EntryIterator::EntryIterator(Instance &aInstance)
: InstanceLocator(aInstance)
, mType(kUnspecified)
{
}
Error Core::EntryIterator::GetNextHost(Host &aHost, EntryState &aState)
{
Error error = kErrorNotFound;
if (mType == kUnspecified)
{
mHostEntry = Get<Core>().mHostEntries.GetHead();
mType = kHost;
}
else
{
VerifyOrExit(mType == kHost, error = kErrorInvalidArgs);
}
while (error == kErrorNotFound)
{
VerifyOrExit(mHostEntry != nullptr);
error = mHostEntry->CopyInfoTo(aHost, aState);
mHostEntry = mHostEntry->GetNext();
}
exit:
return error;
}
Error Core::EntryIterator::GetNextService(Service &aService, EntryState &aState)
{
Error error = kErrorNotFound;
if (mType == kUnspecified)
{
mServiceEntry = Get<Core>().mServiceEntries.GetHead();
mType = kService;
}
else
{
VerifyOrExit(mType == kService, error = kErrorInvalidArgs);
}
while (error == kErrorNotFound)
{
VerifyOrExit(mServiceEntry != nullptr);
error = mServiceEntry->CopyInfoTo(aService, aState, *this);
mServiceEntry = mServiceEntry->GetNext();
}
exit:
return error;
}
Error Core::EntryIterator::GetNextKey(Key &aKey, EntryState &aState)
{
Error error = kErrorNotFound;
if (mType == kUnspecified)
{
mHostEntry = Get<Core>().mHostEntries.GetHead();
mType = kHostKey;
}
else
{
VerifyOrExit((mType == kServiceKey) || (mType == kHostKey), error = kErrorInvalidArgs);
}
while ((error == kErrorNotFound) && (mType == kHostKey))
{
if (mHostEntry == nullptr)
{
mServiceEntry = Get<Core>().mServiceEntries.GetHead();
mType = kServiceKey;
break;
}
error = mHostEntry->CopyInfoTo(aKey, aState);
mHostEntry = mHostEntry->GetNext();
}
while ((error == kErrorNotFound) && (mType == kServiceKey))
{
VerifyOrExit(mServiceEntry != nullptr);
error = mServiceEntry->CopyInfoTo(aKey, aState);
mServiceEntry = mServiceEntry->GetNext();
}
exit:
return error;
}
} // namespace Multicast
} // namespace Dns
} // namespace ot
+139 -16
View File
@@ -46,6 +46,7 @@
#include "common/heap_data.hpp"
#include "common/heap_string.hpp"
#include "common/linked_list.hpp"
#include "common/locator.hpp"
#include "common/owned_ptr.hpp"
#include "common/owning_list.hpp"
#include "common/retain_ptr.hpp"
@@ -63,6 +64,14 @@
*
*/
/**
* Represents an opaque (and empty) type for an mDNS iterator.
*
*/
struct otMdnsIterator
{
};
namespace ot {
namespace Dns {
namespace Multicast {
@@ -95,6 +104,7 @@ public:
typedef otMdnsRequestId RequestId; ///< A request Identifier.
typedef otMdnsRegisterCallback RegisterCallback; ///< Registration callback.
typedef otMdnsConflictCallback ConflictCallback; ///< Conflict callback.
typedef otMdnsEntryState EntryState; ///< Host/Service/Key entry state.
typedef otMdnsHost Host; ///< Host information.
typedef otMdnsService Service; ///< Service information.
typedef otMdnsKey Key; ///< Key information.
@@ -111,6 +121,7 @@ public:
typedef otMdnsAddressCallback AddressCallback; ///< Address callback
typedef otMdnsAddressResult AddressResult; ///< Address result.
typedef otMdnsAddressAndTtl AddressAndTtl; ///< Address and TTL.
typedef otMdnsIterator Iterator; ///< An entry iterator.
/**
* Represents a socket address info.
@@ -323,10 +334,11 @@ public:
*
* The fields in @p aKey follow these rules:
*
* - If the key is associated with a host entry, `mName` specifies the host name & `mServcieType` MUST be `nullptr`.
* - If the key is associated with a service entry, `mName` specifies the service instance label (always treated as
* a single label) and `mServiceType` specifies the service type (e.g. "_tst._udp"). In this case the DNS name
* for key record is `<mName>.<mServiceTye>`.
* - If the key is associated with a host entry, the `mName` field specifies the host name and the `mServiceType`
* MUST be `nullptr`.
* - If the key is associated with a service entry, the `mName` filed specifies the service instance label (always
* treated as a single label) and the `mServiceType` filed specifies the service type (e.g. "_tst._udp"). In this
* case the DNS name for key record is `<mName>.<mServiceTye>`.
* - The `mKeyData` field contains the key record's data with `mKeyDataLength` as its length in byes.
* - The `mTtl` specifies the TTL if non-zero. If zero, the mDNS module will use default TTL for the key entry.
*
@@ -351,10 +363,11 @@ public:
*
* The fields in @p aKey follow these rules:
*
* - If the key is associated with a host entry, `mName` specifies the host name & `mServcieType` MUST be `nullptr`.
* - If the key is associated with a service entry, `mName` specifies the service instance label (always treated as
* a single label) and `mServiceType` specifies the service type (e.g. "_tst._udp"). In this case the DNS name
* for key record is `<mName>.<mServiceTye>`.
* - If the key is associated with a host entry, the `mName` field specifies the host name and the `mServiceType`
* MUST be `nullptr`.
* - If the key is associated with a service entry, the `mName` filed specifies the service instance label (always
* treated as a single label) and the `mServiceType` field specifies the service type (e.g. "_tst._udp"). In this
* case the DNS name for key record is `<mName>.<mServiceTye>`.
* - The rest of the fields in @p aKey structure are ignored in a`otMdnsUnregisterKey()` call.
*
* If there is no previously registered key with the same name, no action is performed.
@@ -484,7 +497,7 @@ public:
*
* Initiates a continuous IPv6 address resolver for the specified host name in @p aResolver.
*
* Discovered addresses are reported through the `mCallback` function in @ p aResolver. The callback is invoked
* Discovered addresses are reported through the `mCallback` function in @p aResolver. The callback is invoked
* whenever addresses are added or removed, providing an updated list. If all addresses are removed, the callback
* is invoked with an empty list (`mAddresses` will be NULL, and `mAddressesLength` will be zero).
*
@@ -521,7 +534,7 @@ public:
*
* Initiates a continuous IPv4 address resolver for the specified host name in @p aResolver.
*
* Discovered addresses are reported through the `mCallback` function in @ p aResolver. The IPv4 addresses are
* Discovered addresses are reported through the `mCallback` function in @p aResolver. The IPv4 addresses are
* represented using the IPv4-mapped IPv6 address format in `mAddresses` array. The callback is invoked whenever
* addresses are added or removed, providing an updated list. If all addresses are removed, the callback is invoked
* with an empty list (`mAddresses` will be NULL, and `mAddressesLength` will be zero).
@@ -564,6 +577,72 @@ public:
*/
void SetMaxMessageSize(uint16_t aMaxSize) { mMaxMessageSize = aMaxSize; }
/**
* Allocates a new iterator.
*
* @returns A pointer to the newly allocated iterator or `nullptr` if it fails to allocate.
*
*/
Iterator *AllocateIterator(void);
/**
* Frees a previously allocated iterator.
*
* @param[in] aIterator The iterator to free.
*
*/
void FreeIterator(Iterator &aIterator);
/**
* Iterates over registered host entries.
*
* On success, @p aHost is populated with information about the next host. Pointers within the `Host` structure
* (like `mName`) 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] aHost A `Host` to return the information about the next host entry.
* @param[out] aState An `EntryState` to return the entry state.
*
* @retval kErrorNone @p aHost, @p aState, & @p aIterator are updated successfully.
* @retval kErrorNotFound Reached the end of the list.
* @retval kErrorInvalidArg @p aIterator is not valid.
*
*/
Error GetNextHost(Iterator &aIterator, Host &aHost, EntryState &aState) const;
/**
* Iterates over registered service entries.
*
* On success, @p aService is populated with information about the next service. Pointers within the `Service`
* structure (like `mServiceType`) remain valid until the next call to any OpenThread stack's public or platform
* API/callback.
*
* @param[out] aService A `Service` to return the information about the next service entry.
* @param[out] aState An `EntryState` to return the entry state.
*
* @retval kErrorNone @p aService, @p aState, & @p aIterator are updated successfully.
* @retval kErrorNotFound Reached the end of the list.
* @retval kErrorInvalidArg @p aIterator is not valid.
*
*/
Error GetNextService(Iterator &aIterator, Service &aService, EntryState &aState) const;
/**
* Iterates over registered key entries.
*
* On success, @p aKey is populated with information about the next key. Pointers within the `Key` structure
* (like `mName`) remain valid until the next call to any OpenThread stack's public or platform API/callback.
*
* @param[out] aKey A `Key` to return the information about the next key entry.
* @param[out] aState An `EntryState` to return the entry state.
*
* @retval kErrorNone @p aKey, @p aState, & @p aIterator are updated successfully.
* @retval kErrorNotFound Reached the end of the list.
* @retval kErrorInvalidArg @p aIterator is not valid.
*
*/
Error GetNextKey(Iterator &aIterator, Key &aKey, EntryState &aState) const;
private:
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -626,6 +705,7 @@ private:
class RxMessage;
class ServiceEntry;
class ServiceType;
class EntryIterator;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -792,17 +872,19 @@ private:
public:
enum State : uint8_t
{
kProbing,
kRegistered,
kConflict,
kRemoving,
kProbing = OT_MDNS_ENTRY_STATE_PROBING,
kRegistered = OT_MDNS_ENTRY_STATE_REGISTERED,
kConflict = OT_MDNS_ENTRY_STATE_CONFLICT,
kRemoving = OT_MDNS_ENTRY_STATE_REMOVING,
};
State GetState(void) const { return mState; }
bool HasKeyRecord(void) const { return mKeyRecord.IsPresent(); }
void Register(const Key &aKey, const Callback &aCallback);
void Unregister(const Key &aKey);
void InvokeCallbacks(void);
void ClearAppendState(void);
Error CopyKeyInfoTo(Key &aKey, EntryState &aState) const;
protected:
static constexpr uint32_t kMinIntervalProbeResponse = 250; // msec
@@ -891,6 +973,8 @@ private:
void ClearAppendState(void);
void PrepareResponse(TxMessage &aResponse, TimeMilli aNow);
void HandleConflict(void);
Error CopyInfoTo(Host &aHost, EntryState &aState) const;
Error CopyInfoTo(Key &aKey, EntryState &aState) const;
private:
Error Init(Instance &aInstance, const char *aName);
@@ -946,6 +1030,8 @@ private:
void ClearAppendState(void);
void PrepareResponse(TxMessage &aResponse, TimeMilli aNow);
void HandleConflict(void);
Error CopyInfoTo(Service &aService, EntryState &aState, EntryIterator &aIterator) const;
Error CopyInfoTo(Key &aKey, EntryState &aState) const;
private:
class SubType : public LinkedListEntry<SubType>, public Heap::Allocatable<SubType>, private ot::NonCopyable
@@ -1021,7 +1107,7 @@ private:
public:
Error Init(Instance &aInstance, const char *aServiceType);
bool Matches(const Name &aServcieTypeName) const;
bool Matches(const Name &aServiceTypeName) const;
bool Matches(const Heap::String &aServiceType) const;
bool Matches(const ServiceType &aServiceType) const { return (this == &aServiceType); }
void IncrementNumEntries(void) { mNumEntries++; }
@@ -1714,6 +1800,43 @@ private:
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
class EntryIterator : public Iterator, public InstanceLocator, public Heap::Allocatable<EntryIterator>
{
friend class Heap::Allocatable<EntryIterator>;
friend class ServiceEntry;
public:
Error GetNextHost(Host &aHost, EntryState &aState);
Error GetNextService(Service &aService, EntryState &aState);
Error GetNextKey(Key &aKey, EntryState &aState);
private:
static constexpr uint16_t kArrayCapacityIncrement = 32;
enum Type : uint8_t
{
kUnspecified,
kHost,
kService,
kHostKey,
kServiceKey,
};
explicit EntryIterator(Instance &aInstance);
Type mType;
union
{
const HostEntry *mHostEntry;
const ServiceEntry *mServiceEntry;
};
Heap::Array<const char *, kArrayCapacityIncrement> mSubTypeArray;
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
template <typename EntryType> OwningList<EntryType> &GetEntryList(void);
template <typename EntryType, typename ItemInfo>
Error Register(const ItemInfo &aItemInfo, RequestId aRequestId, RegisterCallback aCallback);
@@ -1782,7 +1905,7 @@ private:
CacheTask mCacheTask;
};
// Specializations of `Core::GetEntryList()` for `HostEntry` and `ServcieEntry`:
// Specializations of `Core::GetEntryList()` for `HostEntry` and `ServiceEntry`:
template <> inline OwningList<Core::HostEntry> &Core::GetEntryList<Core::HostEntry>(void) { return mHostEntries; }