[cli] add support for DNS-SD command (#6116)

This commit adds new commands in CLI to support DNS-SD browse and
service resolution.
This commit is contained in:
Abtin Keshavarzian
2021-02-12 11:27:11 -08:00
committed by Jonathan Hui
parent 7084422a0e
commit 4538a5fb7c
5 changed files with 193 additions and 35 deletions
+24
View File
@@ -758,6 +758,30 @@ Send DNS Query to obtain IPv6 address for given hostname. The latter two paramet
> DNS response for ipv6.google.com - 2a00:1450:401b:801:0:0:0:200e TTL: 300
```
### dns browse \<service-name\> \[DNS server IP\] \[DNS server port\]
Send a browse (service instance enumeration) DNS query to get the list of services for given service-name.
```bash
> dns browse _service._udp.example.com
DNS browse response for _service._udp.example.com.
inst1
Port:1234, Priority:1, Weight:2, TTL:7200
Host:host.example.com.
HostAddress:fd00:0:0:0:0:0:0:abcd TTL:7200
TXT-Data:(len:11) [096b65793d76616c756500] TTL:7300
instance2
Port:1234, Priority:1, Weight:2, TTL:7200
Host:host.example.com.
HostAddress:fd00:0:0:0:0:0:0:abcd TTL:7200
TXT-Data:(len:11) [096b65793d76616c756500] TTL:7300
Done
```
### dns service \<service-instance-label\> \<service-name\> \[DNS server IP\] \[DNS server port\]
Send a service instance resolution DNS query for a given service instance. Service instance label is provided first, followed by the service name (note that service instance label can contain dot '.' character).
### domainname
Get the Thread Domain Name for Thread 1.2 device.
+149 -24
View File
@@ -1335,37 +1335,70 @@ exit:
}
#if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE
otError Interpreter::GetDnsServerAddress(uint8_t aArgsLength,
char * aArgs[],
otSockAddr &aAddress,
uint8_t aStartArgsIndex)
{
// This method gets the optional server address from given `aArgs`
// after the `aStartArgsIndex`. The format `[server IPv6 address]
// [server port]`.
otError error = OT_ERROR_NONE;
VerifyOrExit(aArgsLength >= aStartArgsIndex, error = OT_ERROR_INVALID_ARGS);
if (aArgsLength > aStartArgsIndex)
{
SuccessOrExit(error = ParseAsIp6Address(aArgs[aStartArgsIndex], aAddress.mAddress));
}
else
{
// Use IPv6 address of default DNS server.
SuccessOrExit(error = otIp6AddressFromString(OT_DNS_DEFAULT_SERVER_IP, &aAddress.mAddress));
}
if (aArgsLength > aStartArgsIndex + 1)
{
SuccessOrExit(error = ParseAsUint16(aArgs[aStartArgsIndex + 1], aAddress.mPort));
}
else
{
aAddress.mPort = OT_DNS_DEFAULT_SERVER_PORT;
}
exit:
return error;
}
otError Interpreter::ProcessDns(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
otSockAddr serverSockAddr;
serverSockAddr.mPort = OT_DNS_DEFAULT_SERVER_PORT;
VerifyOrExit(aArgsLength > 0, error = OT_ERROR_INVALID_ARGS);
if (strcmp(aArgs[0], "resolve") == 0)
{
VerifyOrExit(aArgsLength > 1, error = OT_ERROR_INVALID_ARGS);
if (aArgsLength > 2)
{
SuccessOrExit(error = ParseAsIp6Address(aArgs[2], serverSockAddr.mAddress));
}
else
{
// Use IPv6 address of default DNS server.
SuccessOrExit(error = otIp6AddressFromString(OT_DNS_DEFAULT_SERVER_IP, &serverSockAddr.mAddress));
}
if (aArgsLength > 3)
{
SuccessOrExit(error = ParseAsUint16(aArgs[3], serverSockAddr.mPort));
}
SuccessOrExit(error = GetDnsServerAddress(aArgsLength, aArgs, serverSockAddr, 2));
SuccessOrExit(error = otDnsClientResolveAddress(mInstance, &serverSockAddr, aArgs[1], /* aNoRecursion */ false,
&Interpreter::HandleDnsResponse, this));
&Interpreter::HandleDnsAddressResponse, this));
}
#if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
else if (strcmp(aArgs[0], "browse") == 0)
{
SuccessOrExit(error = GetDnsServerAddress(aArgsLength, aArgs, serverSockAddr, 2));
SuccessOrExit(error = otDnsClientBrowse(mInstance, &serverSockAddr, aArgs[1],
&Interpreter::HandleDnsBrowseResponse, this));
}
else if (strcmp(aArgs[0], "service") == 0)
{
SuccessOrExit(error = GetDnsServerAddress(aArgsLength, aArgs, serverSockAddr, 3));
SuccessOrExit(error = otDnsClientResolveService(mInstance, &serverSockAddr, aArgs[1], aArgs[2],
&Interpreter::HandleDnsServiceResponse, this));
}
#endif // OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
else
{
ExitNow(error = OT_ERROR_INVALID_COMMAND);
@@ -1377,12 +1410,12 @@ exit:
return error;
}
void Interpreter::HandleDnsResponse(otError aError, const otDnsAddressResponse *aResponse, void *aContext)
void Interpreter::HandleDnsAddressResponse(otError aError, const otDnsAddressResponse *aResponse, void *aContext)
{
static_cast<Interpreter *>(aContext)->HandleDnsResponse(aError, aResponse);
static_cast<Interpreter *>(aContext)->HandleDnsAddressResponse(aError, aResponse);
}
void Interpreter::HandleDnsResponse(otError aError, const otDnsAddressResponse *aResponse)
void Interpreter::HandleDnsAddressResponse(otError aError, const otDnsAddressResponse *aResponse)
{
char hostName[OT_DNS_MAX_NAME_SIZE];
otIp6Address address;
@@ -1399,7 +1432,7 @@ void Interpreter::HandleDnsResponse(otError aError, const otDnsAddressResponse *
while (otDnsAddressResponseGetAddress(aResponse, index, &address, &ttl) == OT_ERROR_NONE)
{
OutputIp6Address(address);
OutputFormat(" TTL: %u ", ttl);
OutputFormat(" TTL:%u ", ttl);
index++;
}
@@ -1408,6 +1441,98 @@ void Interpreter::HandleDnsResponse(otError aError, const otDnsAddressResponse *
OutputResult(aError);
}
#if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
void Interpreter::OutputDnsServiceInfo(uint8_t aIndentSize, const otDnsServiceInfo &aServiceInfo)
{
OutputLine(aIndentSize, "Port:%d, Priority:%d, Weight:%d, TTL:%u", aServiceInfo.mPort, aServiceInfo.mPriority,
aServiceInfo.mWeight, aServiceInfo.mTtl);
OutputLine(aIndentSize, "Host:%s", aServiceInfo.mHostNameBuffer);
OutputFormat(aIndentSize, "HostAddress:");
OutputIp6Address(aServiceInfo.mHostAddress);
OutputLine(" TTL:%u", aServiceInfo.mHostAddressTtl);
OutputFormat(aIndentSize, "TXT-Data:(len:%d) [", aServiceInfo.mTxtDataSize);
OutputBytes(aServiceInfo.mTxtData, aServiceInfo.mTxtDataSize);
OutputFormat("] TTL:%u", aServiceInfo.mTxtDataTtl);
}
void Interpreter::HandleDnsBrowseResponse(otError aError, const otDnsBrowseResponse *aResponse, void *aContext)
{
static_cast<Interpreter *>(aContext)->HandleDnsBrowseResponse(aError, aResponse);
}
void Interpreter::HandleDnsBrowseResponse(otError aError, const otDnsBrowseResponse *aResponse)
{
char name[OT_DNS_MAX_NAME_SIZE];
char label[OT_DNS_MAX_LABEL_SIZE];
uint8_t txtBuffer[255];
otDnsServiceInfo serviceInfo;
IgnoreError(otDnsBrowseResponseGetServiceName(aResponse, name, sizeof(name)));
OutputLine("DNS browse response for %s", name);
if (aError == OT_ERROR_NONE)
{
uint16_t index = 0;
while (otDnsBrowseResponseGetServiceInstance(aResponse, index, label, sizeof(label)) == OT_ERROR_NONE)
{
OutputLine("%s", label);
index++;
serviceInfo.mHostNameBuffer = name;
serviceInfo.mHostNameBufferSize = sizeof(name);
serviceInfo.mTxtData = txtBuffer;
serviceInfo.mTxtDataSize = sizeof(txtBuffer);
if (otDnsBrowseResponseGetServiceInfo(aResponse, label, &serviceInfo) == OT_ERROR_NONE)
{
OutputDnsServiceInfo(kIndentSize, serviceInfo);
}
OutputLine("");
}
}
OutputResult(aError);
}
void Interpreter::HandleDnsServiceResponse(otError aError, const otDnsServiceResponse *aResponse, void *aContext)
{
static_cast<Interpreter *>(aContext)->HandleDnsServiceResponse(aError, aResponse);
}
void Interpreter::HandleDnsServiceResponse(otError aError, const otDnsServiceResponse *aResponse)
{
char name[OT_DNS_MAX_NAME_SIZE];
char label[OT_DNS_MAX_LABEL_SIZE];
uint8_t txtBuffer[255];
otDnsServiceInfo serviceInfo;
IgnoreError(otDnsServiceResponseGetServiceName(aResponse, label, sizeof(label), name, sizeof(name)));
OutputLine("DNS service resolution response for %s for service %s", label, name);
if (aError == OT_ERROR_NONE)
{
serviceInfo.mHostNameBuffer = name;
serviceInfo.mHostNameBufferSize = sizeof(name);
serviceInfo.mTxtData = txtBuffer;
serviceInfo.mTxtDataSize = sizeof(txtBuffer);
if (otDnsServiceResponseGetServiceInfo(aResponse, &serviceInfo) == OT_ERROR_NONE)
{
OutputDnsServiceInfo(/* aIndetSize */ 0, serviceInfo);
OutputLine("");
}
}
OutputResult(aError);
}
#endif // OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
#endif // OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE
#if OPENTHREAD_FTD
+10 -2
View File
@@ -554,8 +554,16 @@ private:
#endif
#if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE
static void HandleDnsResponse(otError aError, const otDnsAddressResponse *aResponse, void *aContext);
void HandleDnsResponse(otError aError, const otDnsAddressResponse *aResponse);
otError GetDnsServerAddress(uint8_t aArgsLength, char *aArgs[], otSockAddr &aAddress, uint8_t aStartArgsIndex);
static void HandleDnsAddressResponse(otError aError, const otDnsAddressResponse *aResponse, void *aContext);
void HandleDnsAddressResponse(otError aError, const otDnsAddressResponse *aResponse);
#if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
void OutputDnsServiceInfo(uint8_t aIndentSize, const otDnsServiceInfo &aServiceInfo);
static void HandleDnsBrowseResponse(otError aError, const otDnsBrowseResponse *aResponse, void *aContext);
void HandleDnsBrowseResponse(otError aError, const otDnsBrowseResponse *aResponse);
static void HandleDnsServiceResponse(otError aError, const otDnsServiceResponse *aResponse, void *aContext);
void HandleDnsServiceResponse(otError aError, const otDnsServiceResponse *aResponse);
#endif
#endif
#if OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE
+6 -5
View File
@@ -2411,12 +2411,13 @@ class NodeImpl:
self.simulator.go(10)
output = self._expect_command_output(cmd)
dns_resp = output[0]
# example output: DNS response for host1.default.service.arpa. - fd00:db8:0:0:ae43:4938:4c42:e6af TTL: 7190
ip, ttl = dns_resp.split(' - ')[1].split(' TTL: ')
ip = ip.strip()
ttl = int(ttl)
# example output: "DNS response for host1.default.service.arpa. - fd00:db8:0:0:fd3d:d471:1e8c:b60 TTL:7190 "
# " fd00:db8:0:0:0:ff:fe00:9000 TTL:7190"
addrs = dns_resp.strip().split(' - ')[1].split(' ')
ip = [item.strip() for item in addrs[::2]]
ttl = [int(item.split('TTL:')[1]) for item in addrs[1::2]]
return (ip, ttl)
return list(zip(ip, ttl))
class Node(NodeImpl, OtCli):
+4 -4
View File
@@ -88,11 +88,11 @@ class TestDnssd(thread_cert.TestCase):
self._config_srp_client_services(CLIENT2, 'ins2', 'host2', 22222, 2, 2, client2_addrs)
# Test AAAA query using DNS client
ip, ttl = self.nodes[CLIENT1].dns_resolve(f"host1.{DOMAIN}", self.nodes[SERVER].get_mleid(), 53)
self.assertIn(ipaddress.IPv6Address(ip), map(ipaddress.IPv6Address, client1_addrs))
response = self.nodes[CLIENT1].dns_resolve(f"host1.{DOMAIN}", self.nodes[SERVER].get_mleid(), 53)
self.assertIn(ipaddress.IPv6Address(response[0][0]), map(ipaddress.IPv6Address, client1_addrs))
ip, ttl = self.nodes[CLIENT1].dns_resolve(f"host2.{DOMAIN}", self.nodes[SERVER].get_mleid(), 53)
self.assertIn(ipaddress.IPv6Address(ip), map(ipaddress.IPv6Address, client2_addrs))
response = self.nodes[CLIENT1].dns_resolve(f"host2.{DOMAIN}", self.nodes[SERVER].get_mleid(), 53)
self.assertIn(ipaddress.IPv6Address(response[0][0]), map(ipaddress.IPv6Address, client2_addrs))
# TODO: test other query types using DNS-SD client