blob: d74f91db152230d71c64a2a003c54745249374d3 [file] [log] [blame] [edit]
/*
* 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;
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
if (aArgs[0].IsEmpty())
{
bool isRunning;
// If no if-index is provided, we use the Border Router's
// infrastructure if-index (if any).
SuccessOrExit(error = otBorderRoutingGetInfraIfInfo(GetInstancePtr(), &infraIfIndex, &isRunning));
VerifyOrExit(isRunning, error = OT_ERROR_INVALID_STATE);
}
else
#endif
{
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;
}
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
template <> otError Mdns::Process<Cmd("auto")>(Arg aArgs[])
{
return ProcessEnableDisable(aArgs, otMdnsGetAutoEnableMode, otMdnsSetAutoEnableMode);
}
#endif
template <> otError Mdns::Process<Cmd("unicastquestion")>(Arg aArgs[])
{
return ProcessEnableDisable(aArgs, otMdnsIsQuestionUnicastAllowed, otMdnsSetQuestionUnicastAllowed);
}
template <> otError Mdns::Process<Cmd("localhostname")>(Arg aArgs[])
{
return ProcessGetSet(aArgs, otMdnsGetLocalHostName, otMdnsSetLocalHostName);
}
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);
}
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<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;
}
#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE
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;
}
template <> otError Mdns::Process<Cmd("localhostaddrs")>(Arg aArgs[])
{
otError error = OT_ERROR_NONE;
otMdnsIterator *iterator = nullptr;
otMdnsLocalHostAddress addr;
VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
iterator = otMdnsAllocateIterator(GetInstancePtr());
VerifyOrExit(iterator != nullptr, error = OT_ERROR_NO_BUFS);
while (true)
{
error = otMdnsGetNextLocalHostAddress(GetInstancePtr(), iterator, &addr);
if (error == OT_ERROR_NOT_FOUND)
{
error = OT_ERROR_NONE;
ExitNow();
}
SuccessOrExit(error);
if (addr.mIsIp6)
{
OutputIp6AddressLine(addr.mAddress.mIp6);
}
else
{
char ip4AddressString[OT_IP4_ADDRESS_STRING_SIZE];
otIp4AddressToString(&addr.mAddress.mIp4, ip4AddressString, sizeof(ip4AddressString));
OutputLine("%s", ip4AddressString);
}
}
exit:
if (iterator != nullptr)
{
otMdnsFreeIterator(GetInstancePtr(), iterator);
}
return error;
}
#endif // OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE
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);
}
template <> otError Mdns::Process<Cmd("recordquerier")>(Arg aArgs[])
{
// mdns recordquerier start|stop <record-type> <first-label> [<next-labels>]
otError error;
otMdnsRecordQuerier querier;
bool isStart;
ClearAllBytes(querier);
SuccessOrExit(error = ParseStartOrStop(aArgs[0], isStart));
SuccessOrExit(error = aArgs[1].ParseAsUint16(querier.mRecordType));
VerifyOrExit(!aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
querier.mFirstLabel = aArgs[2].GetCString();
if (!aArgs[3].IsEmpty())
{
querier.mNextLabels = aArgs[3].GetCString();
VerifyOrExit(aArgs[4].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
}
querier.mInfraIfIndex = mInfraIfIndex;
querier.mCallback = HandleRecordResult;
if (isStart)
{
error = otMdnsStartRecordQuerier(GetInstancePtr(), &querier);
}
else
{
error = otMdnsStopRecordQuerier(GetInstancePtr(), &querier);
}
exit:
return error;
}
void Mdns::HandleRecordResult(otInstance *aInstance, const otMdnsRecordResult *aResult)
{
OT_UNUSED_VARIABLE(aInstance);
Interpreter::GetInterpreter().mMdns.HandleRecordResult(*aResult);
}
void Mdns::HandleRecordResult(const otMdnsRecordResult &aResult)
{
OutputLine("mDNS result for record %u and name %s %s", aResult.mRecordType, aResult.mFirstLabel,
aResult.mNextLabels == nullptr ? "" : aResult.mNextLabels);
OutputFormat(kIndentSize, "data: ");
OutputBytesLine(aResult.mRecordData, aResult.mRecordDataLength);
OutputLine(kIndentSize, "ttl: %lu", ToUlong(aResult.mTtl));
OutputLine(kIndentSize, "if-index: %lu", ToUlong(aResult.mInfraIfIndex));
}
#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE
template <> otError Mdns::Process<Cmd("browsers")>(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<Cmd("srvresolvers")>(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<Cmd("txtresolvers")>(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<Cmd("ip6resolvers")>(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<Cmd("ip4resolvers")>(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;
}
template <> otError Mdns::Process<Cmd("recordqueriers")>(Arg aArgs[])
{
// mdns recordqueriers
otError error;
otMdnsIterator *iterator = nullptr;
otMdnsCacheInfo info;
otMdnsRecordQuerier querier;
VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
iterator = otMdnsAllocateIterator(GetInstancePtr());
VerifyOrExit(iterator != nullptr, error = OT_ERROR_NO_BUFS);
while (true)
{
error = otMdnsGetNextRecordQuerier(GetInstancePtr(), iterator, &querier, &info);
if (error == OT_ERROR_NOT_FOUND)
{
error = OT_ERROR_NONE;
ExitNow();
}
SuccessOrExit(error);
OutputLine("Record querier for type %u and name %s %s", querier.mRecordType, querier.mFirstLabel,
querier.mNextLabels == nullptr ? "" : querier.mNextLabels);
OutputCacheInfo(info);
}
exit:
if (iterator != nullptr)
{
otMdnsFreeIterator(GetInstancePtr(), iterator);
}
return error;
}
#endif // OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE
#if OPENTHREAD_CONFIG_MULTICAST_DNS_VERBOSE_LOGGING_ENABLE
template <> otError Mdns::Process<Cmd("verboselogging")>(Arg aArgs[])
{
return ProcessEnableDisable(aArgs, otMdnsIsVerboseLoggingEnabled, otMdnsSetVerboseLoggingEnabled);
}
#endif
otError Mdns::Process(Arg aArgs[])
{
#define CmdEntry(aCommandString) {aCommandString, &Mdns::Process<Cmd(aCommandString)>}
static constexpr Command kCommands[] = {
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
CmdEntry("auto"),
#endif
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
#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE
CmdEntry("localhostaddrs"),
#endif
CmdEntry("localhostname"),
CmdEntry("recordquerier"),
#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE
CmdEntry("recordqueriers"),
#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"),
#if OPENTHREAD_CONFIG_MULTICAST_DNS_VERBOSE_LOGGING_ENABLE
CmdEntry("verboselogging"),
#endif
};
#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