blob: 977bb7fa1786743d1a5a1acad128b216212601b3 [file] [log] [blame]
/*
* Copyright (c) 2024, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* This file implements CLI for mDNS.
*/
#include <string.h>
#include "cli_mdns.hpp"
#include <openthread/nat64.h>
#include "cli/cli.hpp"
#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE && OPENTHREAD_CONFIG_MULTICAST_DNS_PUBLIC_API_ENABLE
namespace ot {
namespace Cli {
template <> otError Mdns::Process<Cmd("enable")>(Arg aArgs[])
{
otError error;
uint32_t infraIfIndex;
SuccessOrExit(error = aArgs[0].ParseAsUint32(infraIfIndex));
VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = otMdnsSetEnabled(GetInstancePtr(), true, infraIfIndex));
mInfraIfIndex = infraIfIndex;
exit:
return error;
}
template <> otError Mdns::Process<Cmd("disable")>(Arg aArgs[])
{
otError error = OT_ERROR_NONE;
VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
error = otMdnsSetEnabled(GetInstancePtr(), false, /* aInfraIfIndex */ 0);
exit:
return error;
}
template <> otError Mdns::Process<Cmd("state")>(Arg aArgs[])
{
otError error = OT_ERROR_NONE;
VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
OutputEnabledDisabledStatus(otMdnsIsEnabled(GetInstancePtr()));
exit:
return error;
}
template <> otError Mdns::Process<Cmd("unicastquestion")>(Arg aArgs[])
{
return ProcessEnableDisable(aArgs, otMdnsIsQuestionUnicastAllowed, otMdnsSetQuestionUnicastAllowed);
}
void Mdns::OutputHost(const otMdnsHost &aHost)
{
OutputLine("Host %s", aHost.mHostName);
OutputLine(kIndentSize, "%u address:", aHost.mAddressesLength);
for (uint16_t index = 0; index < aHost.mAddressesLength; index++)
{
OutputFormat(kIndentSize, " ");
OutputIp6AddressLine(aHost.mAddresses[index]);
}
OutputLine(kIndentSize, "ttl: %lu", ToUlong(aHost.mTtl));
}
void Mdns::OutputService(const otMdnsService &aService)
{
OutputLine("Service %s for %s", aService.mServiceInstance, aService.mServiceType);
OutputLine(kIndentSize, "host: %s", aService.mHostName);
if (aService.mSubTypeLabelsLength > 0)
{
OutputLine(kIndentSize, "%u sub-type:", aService.mSubTypeLabelsLength);
for (uint16_t index = 0; index < aService.mSubTypeLabelsLength; index++)
{
OutputLine(kIndentSize * 2, "%s", aService.mSubTypeLabels[index]);
}
}
OutputLine(kIndentSize, "port: %u", aService.mPort);
OutputLine(kIndentSize, "priority: %u", aService.mPriority);
OutputLine(kIndentSize, "weight: %u", aService.mWeight);
OutputLine(kIndentSize, "ttl: %lu", ToUlong(aService.mTtl));
if ((aService.mTxtData == nullptr) || (aService.mTxtDataLength == 0))
{
OutputLine(kIndentSize, "txt-data: (empty)");
}
else
{
OutputFormat(kIndentSize, "txt-data: ");
OutputBytesLine(aService.mTxtData, aService.mTxtDataLength);
}
}
void Mdns::OutputKey(const otMdnsKey &aKey)
{
if (aKey.mServiceType != nullptr)
{
OutputLine("Key %s for %s (service)", aKey.mName, aKey.mServiceType);
}
else
{
OutputLine("Key %s (host)", aKey.mName);
}
OutputFormat(kIndentSize, "key-data: ");
OutputBytesLine(aKey.mKeyData, aKey.mKeyDataLength);
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>
otError error = OT_ERROR_NONE;
bool isAsync = false;
if (aArgs[0] == "async")
{
isAsync = true;
aArgs++;
}
if (aArgs[0] == "host")
{
SuccessOrExit(error = ProcessRegisterHost(aArgs + 1));
}
else if (aArgs[0] == "service")
{
SuccessOrExit(error = ProcessRegisterService(aArgs + 1));
}
else if (aArgs[0] == "key")
{
SuccessOrExit(error = ProcessRegisterKey(aArgs + 1));
}
else
{
ExitNow(error = OT_ERROR_INVALID_ARGS);
}
if (isAsync)
{
OutputLine("mDNS request id: %lu", ToUlong(mRequestId));
}
else
{
error = OT_ERROR_PENDING;
mWaitingForCallback = true;
}
exit:
return error;
}
otError Mdns::ProcessRegisterHost(Arg aArgs[])
{
// register host <name> [<zero or more addresses>] [<ttl>]
otError error = OT_ERROR_NONE;
otMdnsHost host;
otIp6Address addresses[kMaxAddresses];
memset(&host, 0, sizeof(host));
VerifyOrExit(!aArgs->IsEmpty(), error = OT_ERROR_INVALID_ARGS);
host.mHostName = aArgs->GetCString();
aArgs++;
host.mAddresses = addresses;
for (; !aArgs->IsEmpty(); aArgs++)
{
otIp6Address address;
uint32_t ttl;
if (aArgs->ParseAsIp6Address(address) == OT_ERROR_NONE)
{
VerifyOrExit(host.mAddressesLength < kMaxAddresses, error = OT_ERROR_NO_BUFS);
addresses[host.mAddressesLength] = address;
host.mAddressesLength++;
}
else if (aArgs->ParseAsUint32(ttl) == OT_ERROR_NONE)
{
host.mTtl = ttl;
VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
}
else
{
ExitNow(error = OT_ERROR_INVALID_ARGS);
}
}
OutputHost(host);
mRequestId++;
error = otMdnsRegisterHost(GetInstancePtr(), &host, mRequestId, HandleRegisterationDone);
exit:
return error;
}
otError Mdns::ProcessRegisterService(Arg aArgs[])
{
otError error;
otMdnsService service;
Buffers buffers;
SuccessOrExit(error = ParseServiceArgs(aArgs, service, buffers));
OutputService(service);
mRequestId++;
error = otMdnsRegisterService(GetInstancePtr(), &service, mRequestId, HandleRegisterationDone);
exit:
return error;
}
otError Mdns::ParseServiceArgs(Arg aArgs[], otMdnsService &aService, Buffers &aBuffers)
{
// mdns register service <instance-label> <service-type,sub_types> <host-name> <port> [<prio>] [<weight>] [<ttl>]
// [<txtdata>]
otError error = OT_ERROR_INVALID_ARGS;
char *label;
uint16_t len;
memset(&aService, 0, sizeof(aService));
VerifyOrExit(!aArgs->IsEmpty());
aService.mServiceInstance = aArgs->GetCString();
aArgs++;
// Copy service type into `aBuffer.mString`, then search for
// `,` in the string to parse the list of sub-types (if any).
VerifyOrExit(!aArgs->IsEmpty());
len = aArgs->GetLength();
VerifyOrExit(len + 1 < kStringSize, error = OT_ERROR_NO_BUFS);
memcpy(aBuffers.mString, aArgs->GetCString(), len + 1);
aService.mServiceType = aBuffers.mString;
aService.mSubTypeLabels = aBuffers.mSubTypeLabels;
label = strchr(aBuffers.mString, ',');
if (label != nullptr)
{
while (true)
{
*label++ = '\0';
VerifyOrExit(aService.mSubTypeLabelsLength < kMaxSubTypes, error = OT_ERROR_NO_BUFS);
aBuffers.mSubTypeLabels[aService.mSubTypeLabelsLength] = label;
aService.mSubTypeLabelsLength++;
label = strchr(label, ',');
if (label == nullptr)
{
break;
}
}
}
aArgs++;
VerifyOrExit(!aArgs->IsEmpty());
aService.mHostName = aArgs->GetCString();
aArgs++;
SuccessOrExit(aArgs->ParseAsUint16(aService.mPort));
// The rest of `Args` are optional.
error = OT_ERROR_NONE;
aArgs++;
VerifyOrExit(!aArgs->IsEmpty());
SuccessOrExit(error = aArgs->ParseAsUint16(aService.mPriority));
aArgs++;
VerifyOrExit(!aArgs->IsEmpty());
SuccessOrExit(error = aArgs->ParseAsUint16(aService.mWeight));
aArgs++;
VerifyOrExit(!aArgs->IsEmpty());
SuccessOrExit(error = aArgs->ParseAsUint32(aService.mTtl));
aArgs++;
VerifyOrExit(!aArgs->IsEmpty());
len = kMaxTxtDataSize;
SuccessOrExit(error = aArgs->ParseAsHexString(len, aBuffers.mTxtData));
aService.mTxtData = aBuffers.mTxtData;
aService.mTxtDataLength = len;
aArgs++;
VerifyOrExit(aArgs->IsEmpty(), error = OT_ERROR_INVALID_ARGS);
exit:
return error;
}
otError Mdns::ProcessRegisterKey(Arg aArgs[])
{
otError error = OT_ERROR_INVALID_ARGS;
otMdnsKey key;
uint16_t len;
uint8_t data[kMaxKeyDataSize];
memset(&key, 0, sizeof(key));
VerifyOrExit(!aArgs->IsEmpty());
key.mName = aArgs->GetCString();
aArgs++;
VerifyOrExit(!aArgs->IsEmpty());
if (aArgs->GetCString()[0] == '_')
{
key.mServiceType = aArgs->GetCString();
aArgs++;
VerifyOrExit(!aArgs->IsEmpty());
}
len = kMaxKeyDataSize;
SuccessOrExit(error = aArgs->ParseAsHexString(len, data));
key.mKeyData = data;
key.mKeyDataLength = len;
// ttl is optional
aArgs++;
if (!aArgs->IsEmpty())
{
SuccessOrExit(error = aArgs->ParseAsUint32(key.mTtl));
aArgs++;
VerifyOrExit(aArgs->IsEmpty(), error = kErrorInvalidArgs);
}
OutputKey(key);
mRequestId++;
error = otMdnsRegisterKey(GetInstancePtr(), &key, mRequestId, HandleRegisterationDone);
exit:
return error;
}
void Mdns::HandleRegisterationDone(otInstance *aInstance, otMdnsRequestId aRequestId, otError aError)
{
OT_UNUSED_VARIABLE(aInstance);
Interpreter::GetInterpreter().mMdns.HandleRegisterationDone(aRequestId, aError);
}
void Mdns::HandleRegisterationDone(otMdnsRequestId aRequestId, otError aError)
{
if (mWaitingForCallback && (aRequestId == mRequestId))
{
mWaitingForCallback = false;
Interpreter::GetInterpreter().OutputResult(aError);
}
else
{
OutputLine("mDNS registration for request id %lu outcome: %s", ToUlong(aRequestId),
otThreadErrorToString(aError));
}
}
template <> otError Mdns::Process<Cmd("unregister")>(Arg aArgs[])
{
otError error = OT_ERROR_INVALID_ARGS;
if (aArgs[0] == "host")
{
otMdnsHost host;
memset(&host, 0, sizeof(host));
VerifyOrExit(!aArgs[1].IsEmpty());
host.mHostName = aArgs[1].GetCString();
VerifyOrExit(aArgs[2].IsEmpty());
error = otMdnsUnregisterHost(GetInstancePtr(), &host);
}
else if (aArgs[0] == "service")
{
otMdnsService service;
memset(&service, 0, sizeof(service));
VerifyOrExit(!aArgs[1].IsEmpty());
service.mServiceInstance = aArgs[1].GetCString();
VerifyOrExit(!aArgs[2].IsEmpty());
service.mServiceType = aArgs[2].GetCString();
VerifyOrExit(aArgs[3].IsEmpty());
error = otMdnsUnregisterService(GetInstancePtr(), &service);
}
else if (aArgs[0] == "key")
{
otMdnsKey key;
memset(&key, 0, sizeof(key));
VerifyOrExit(!aArgs[1].IsEmpty());
key.mName = aArgs[1].GetCString();
if (!aArgs[2].IsEmpty())
{
key.mServiceType = aArgs[2].GetCString();
VerifyOrExit(aArgs[3].IsEmpty());
}
error = otMdnsUnregisterKey(GetInstancePtr(), &key);
}
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;
if (aArg == "start")
{
aIsStart = true;
}
else if (aArg == "stop")
{
aIsStart = false;
}
else
{
error = OT_ERROR_INVALID_ARGS;
}
return error;
}
template <> otError Mdns::Process<Cmd("browser")>(Arg aArgs[])
{
// mdns browser start|stop <service-type> [<sub-type>]
otError error;
otMdnsBrowser browser;
bool isStart;
ClearAllBytes(browser);
SuccessOrExit(error = ParseStartOrStop(aArgs[0], isStart));
VerifyOrExit(!aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
browser.mServiceType = aArgs[1].GetCString();
if (!aArgs[2].IsEmpty())
{
browser.mSubTypeLabel = aArgs[2].GetCString();
VerifyOrExit(aArgs[3].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
}
browser.mInfraIfIndex = mInfraIfIndex;
browser.mCallback = HandleBrowseResult;
if (isStart)
{
error = otMdnsStartBrowser(GetInstancePtr(), &browser);
}
else
{
error = otMdnsStopBrowser(GetInstancePtr(), &browser);
}
exit:
return error;
}
void Mdns::HandleBrowseResult(otInstance *aInstance, const otMdnsBrowseResult *aResult)
{
OT_UNUSED_VARIABLE(aInstance);
Interpreter::GetInterpreter().mMdns.HandleBrowseResult(*aResult);
}
void Mdns::HandleBrowseResult(const otMdnsBrowseResult &aResult)
{
OutputFormat("mDNS browse result for %s", aResult.mServiceType);
if (aResult.mSubTypeLabel)
{
OutputLine(" sub-type %s", aResult.mSubTypeLabel);
}
else
{
OutputNewLine();
}
OutputLine(kIndentSize, "instance: %s", aResult.mServiceInstance);
OutputLine(kIndentSize, "ttl: %lu", ToUlong(aResult.mTtl));
OutputLine(kIndentSize, "if-index: %lu", ToUlong(aResult.mInfraIfIndex));
}
template <> otError Mdns::Process<Cmd("srvresolver")>(Arg aArgs[])
{
// mdns srvresolver start|stop <service-instance> <service-type>
otError error;
otMdnsSrvResolver resolver;
bool isStart;
ClearAllBytes(resolver);
SuccessOrExit(error = ParseStartOrStop(aArgs[0], isStart));
VerifyOrExit(!aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
resolver.mServiceInstance = aArgs[1].GetCString();
resolver.mServiceType = aArgs[2].GetCString();
resolver.mInfraIfIndex = mInfraIfIndex;
resolver.mCallback = HandleSrvResult;
if (isStart)
{
error = otMdnsStartSrvResolver(GetInstancePtr(), &resolver);
}
else
{
error = otMdnsStopSrvResolver(GetInstancePtr(), &resolver);
}
exit:
return error;
}
void Mdns::HandleSrvResult(otInstance *aInstance, const otMdnsSrvResult *aResult)
{
OT_UNUSED_VARIABLE(aInstance);
Interpreter::GetInterpreter().mMdns.HandleSrvResult(*aResult);
}
void Mdns::HandleSrvResult(const otMdnsSrvResult &aResult)
{
OutputLine("mDNS SRV result for %s for %s", aResult.mServiceInstance, aResult.mServiceType);
if (aResult.mTtl != 0)
{
OutputLine(kIndentSize, "host: %s", aResult.mHostName);
OutputLine(kIndentSize, "port: %u", aResult.mPort);
OutputLine(kIndentSize, "priority: %u", aResult.mPriority);
OutputLine(kIndentSize, "weight: %u", aResult.mWeight);
}
OutputLine(kIndentSize, "ttl: %lu", ToUlong(aResult.mTtl));
OutputLine(kIndentSize, "if-index: %lu", ToUlong(aResult.mInfraIfIndex));
}
template <> otError Mdns::Process<Cmd("txtresolver")>(Arg aArgs[])
{
// mdns txtresolver start|stop <service-instance> <service-type>
otError error;
otMdnsTxtResolver resolver;
bool isStart;
ClearAllBytes(resolver);
SuccessOrExit(error = ParseStartOrStop(aArgs[0], isStart));
VerifyOrExit(!aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
resolver.mServiceInstance = aArgs[1].GetCString();
resolver.mServiceType = aArgs[2].GetCString();
resolver.mInfraIfIndex = mInfraIfIndex;
resolver.mCallback = HandleTxtResult;
if (isStart)
{
error = otMdnsStartTxtResolver(GetInstancePtr(), &resolver);
}
else
{
error = otMdnsStopTxtResolver(GetInstancePtr(), &resolver);
}
exit:
return error;
}
void Mdns::HandleTxtResult(otInstance *aInstance, const otMdnsTxtResult *aResult)
{
OT_UNUSED_VARIABLE(aInstance);
Interpreter::GetInterpreter().mMdns.HandleTxtResult(*aResult);
}
void Mdns::HandleTxtResult(const otMdnsTxtResult &aResult)
{
OutputLine("mDNS TXT result for %s for %s", aResult.mServiceInstance, aResult.mServiceType);
if (aResult.mTtl != 0)
{
OutputFormat(kIndentSize, "txt-data: ");
OutputBytesLine(aResult.mTxtData, aResult.mTxtDataLength);
}
OutputLine(kIndentSize, "ttl: %lu", ToUlong(aResult.mTtl));
OutputLine(kIndentSize, "if-index: %lu", ToUlong(aResult.mInfraIfIndex));
}
template <> otError Mdns::Process<Cmd("ip6resolver")>(Arg aArgs[])
{
// mdns ip6resolver start|stop <host-name>
otError error;
otMdnsAddressResolver resolver;
bool isStart;
ClearAllBytes(resolver);
SuccessOrExit(error = ParseStartOrStop(aArgs[0], isStart));
VerifyOrExit(!aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
resolver.mHostName = aArgs[1].GetCString();
resolver.mInfraIfIndex = mInfraIfIndex;
resolver.mCallback = HandleIp6AddressResult;
if (isStart)
{
error = otMdnsStartIp6AddressResolver(GetInstancePtr(), &resolver);
}
else
{
error = otMdnsStopIp6AddressResolver(GetInstancePtr(), &resolver);
}
exit:
return error;
}
void Mdns::HandleIp6AddressResult(otInstance *aInstance, const otMdnsAddressResult *aResult)
{
OT_UNUSED_VARIABLE(aInstance);
Interpreter::GetInterpreter().mMdns.HandleAddressResult(*aResult, kIp6Address);
}
void Mdns::HandleAddressResult(const otMdnsAddressResult &aResult, IpAddressType aType)
{
OutputLine("mDNS %s address result for %s", aType == kIp6Address ? "IPv6" : "IPv4", aResult.mHostName);
OutputLine(kIndentSize, "%u address:", aResult.mAddressesLength);
for (uint16_t index = 0; index < aResult.mAddressesLength; index++)
{
OutputFormat(kIndentSize, " ");
OutputIp6Address(aResult.mAddresses[index].mAddress);
OutputLine(" ttl:%lu", ToUlong(aResult.mAddresses[index].mTtl));
}
OutputLine(kIndentSize, "if-index: %lu", ToUlong(aResult.mInfraIfIndex));
}
template <> otError Mdns::Process<Cmd("ip4resolver")>(Arg aArgs[])
{
// mdns ip4resolver start|stop <host-name>
otError error;
otMdnsAddressResolver resolver;
bool isStart;
ClearAllBytes(resolver);
SuccessOrExit(error = ParseStartOrStop(aArgs[0], isStart));
VerifyOrExit(!aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
resolver.mHostName = aArgs[1].GetCString();
resolver.mInfraIfIndex = mInfraIfIndex;
resolver.mCallback = HandleIp4AddressResult;
if (isStart)
{
error = otMdnsStartIp4AddressResolver(GetInstancePtr(), &resolver);
}
else
{
error = otMdnsStopIp4AddressResolver(GetInstancePtr(), &resolver);
}
exit:
return error;
}
void Mdns::HandleIp4AddressResult(otInstance *aInstance, const otMdnsAddressResult *aResult)
{
OT_UNUSED_VARIABLE(aInstance);
Interpreter::GetInterpreter().mMdns.HandleAddressResult(*aResult, kIp4Address);
}
otError Mdns::Process(Arg aArgs[])
{
#define CmdEntry(aCommandString) \
{ \
aCommandString, &Mdns::Process<Cmd(aCommandString)> \
}
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"),
};
#undef CmdEntry
static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted");
otError error = OT_ERROR_INVALID_COMMAND;
const Command *command;
if (aArgs[0].IsEmpty() || (aArgs[0] == "help"))
{
OutputCommandTable(kCommands);
ExitNow(error = aArgs[0].IsEmpty() ? error : OT_ERROR_NONE);
}
command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
VerifyOrExit(command != nullptr);
error = (this->*command->mHandler)(aArgs + 1);
exit:
return error;
}
} // namespace Cli
} // namespace ot
#endif // OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE && OPENTHREAD_CONFIG_MULTICAST_DNS_PUBLIC_API_ENABLE