1 /*
2 * Copyright (c) 2023, The OpenThread Authors.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. Neither the name of the copyright holder nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 /**
30 * @file
31 * This file implements CLI for DNS (client and server/resolver).
32 */
33
34 #include "cli_dns.hpp"
35
36 #include "cli/cli.hpp"
37
38 #if OPENTHREAD_CLI_DNS_ENABLE
39
40 namespace ot {
41 namespace Cli {
42
43 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
44
Process(Arg aArgs[])45 template <> otError Dns::Process<Cmd("compression")>(Arg aArgs[])
46 {
47 otError error = OT_ERROR_NONE;
48
49 /**
50 * @cli dns compression
51 * @code
52 * dns compression
53 * Enabled
54 * @endcode
55 * @cparam dns compression [@ca{enable|disable}]
56 * @par api_copy
57 * #otDnsIsNameCompressionEnabled
58 * @par
59 * By default DNS name compression is enabled. When disabled,
60 * DNS names are appended as full and never compressed. This
61 * is applicable to OpenThread's DNS and SRP client/server
62 * modules."
63 * `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` is required.
64 */
65 if (aArgs[0].IsEmpty())
66 {
67 OutputEnabledDisabledStatus(otDnsIsNameCompressionEnabled());
68 }
69 /**
70 * @cli dns compression (enable,disable)
71 * @code
72 * dns compression enable
73 * Enabled
74 * @endcode
75 * @code
76 * dns compression disable
77 * Done
78 * dns compression
79 * Disabled
80 * Done
81 * @endcode
82 * @cparam dns compression [@ca{enable|disable}]
83 * @par
84 * Set the "DNS name compression" mode.
85 * @par
86 * By default DNS name compression is enabled. When disabled,
87 * DNS names are appended as full and never compressed. This
88 * is applicable to OpenThread's DNS and SRP client/server
89 * modules."
90 * `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` is required.
91 * @sa otDnsSetNameCompressionEnabled
92 */
93 else
94 {
95 bool enable;
96
97 SuccessOrExit(error = Interpreter::ParseEnableOrDisable(aArgs[0], enable));
98 otDnsSetNameCompressionEnabled(enable);
99 }
100
101 exit:
102 return error;
103 }
104
105 #endif // OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
106
107 #if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE
108
Process(Arg aArgs[])109 template <> otError Dns::Process<Cmd("config")>(Arg aArgs[])
110 {
111 otError error = OT_ERROR_NONE;
112
113 /**
114 * @cli dns config
115 * @code
116 * dns config
117 * Server: [fd00:0:0:0:0:0:0:1]:1234
118 * ResponseTimeout: 5000 ms
119 * MaxTxAttempts: 2
120 * RecursionDesired: no
121 * ServiceMode: srv
122 * Nat64Mode: allow
123 * Done
124 * @endcode
125 * @par api_copy
126 * #otDnsClientGetDefaultConfig
127 * @par
128 * The config includes the server IPv6 address and port, response
129 * timeout in msec (wait time to rx response), maximum tx attempts
130 * before reporting failure, boolean flag to indicate whether the server
131 * can resolve the query recursively or not.
132 * `OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE` is required.
133 */
134 if (aArgs[0].IsEmpty())
135 {
136 const otDnsQueryConfig *defaultConfig = otDnsClientGetDefaultConfig(GetInstancePtr());
137
138 OutputFormat("Server: ");
139 OutputSockAddrLine(defaultConfig->mServerSockAddr);
140 OutputLine("ResponseTimeout: %lu ms", ToUlong(defaultConfig->mResponseTimeout));
141 OutputLine("MaxTxAttempts: %u", defaultConfig->mMaxTxAttempts);
142 OutputLine("RecursionDesired: %s",
143 (defaultConfig->mRecursionFlag == OT_DNS_FLAG_RECURSION_DESIRED) ? "yes" : "no");
144 OutputLine("ServiceMode: %s", DnsConfigServiceModeToString(defaultConfig->mServiceMode));
145 #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE
146 OutputLine("Nat64Mode: %s", (defaultConfig->mNat64Mode == OT_DNS_NAT64_ALLOW) ? "allow" : "disallow");
147 #endif
148 #if OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE
149 OutputLine("TransportProtocol: %s", (defaultConfig->mTransportProto == OT_DNS_TRANSPORT_UDP) ? "udp" : "tcp");
150 #endif
151 }
152 /**
153 * @cli dns config (set)
154 * @code
155 * dns config fd00::1 1234 5000 2 0
156 * Done
157 * @endcode
158 * @code
159 * dns config
160 * Server: [fd00:0:0:0:0:0:0:1]:1234
161 * ResponseTimeout: 5000 ms
162 * MaxTxAttempts: 2
163 * RecursionDesired: no
164 * Done
165 * @endcode
166 * @code
167 * dns config fd00::2
168 * Done
169 * @endcode
170 * @code
171 * dns config
172 * Server: [fd00:0:0:0:0:0:0:2]:53
173 * ResponseTimeout: 3000 ms
174 * MaxTxAttempts: 3
175 * RecursionDesired: yes
176 * Done
177 * @endcode
178 * @par api_copy
179 * #otDnsClientSetDefaultConfig
180 * @cparam dns config [@ca{dns-server-IP}] [@ca{dns-server-port}] <!--
181 * --> [@ca{response-timeout-ms}] [@ca{max-tx-attempts}] <!--
182 * --> [@ca{recursion-desired-boolean}] [@ca{service-mode}]
183 * @par
184 * We can leave some of the fields as unspecified (or use value zero). The
185 * unspecified fields are replaced by the corresponding OT config option
186 * definitions `OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT` to form the default
187 * query config.
188 * `OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE` is required.
189 */
190 else
191 {
192 otDnsQueryConfig queryConfig;
193 otDnsQueryConfig *config = &queryConfig;
194
195 SuccessOrExit(error = GetDnsConfig(aArgs, config));
196 otDnsClientSetDefaultConfig(GetInstancePtr(), config);
197 }
198
199 exit:
200 return error;
201 }
202
203 /**
204 * @cli dns resolve
205 * @code
206 * dns resolve ipv6.google.com
207 * DNS response for ipv6.google.com - 2a00:1450:401b:801:0:0:0:200e TTL: 300
208 * @endcode
209 * @code
210 * dns resolve example.com 8.8.8.8
211 * Synthesized IPv6 DNS server address: fdde:ad00:beef:2:0:0:808:808
212 * DNS response for example.com. - fd4c:9574:3720:2:0:0:5db8:d822 TTL:20456
213 * Done
214 * @endcode
215 * @cparam dns resolve @ca{hostname} [@ca{dns-server-IP}] <!--
216 * --> [@ca{dns-server-port}] [@ca{response-timeout-ms}] <!--
217 * --> [@ca{max-tx-attempts}] [@ca{recursion-desired-boolean}]
218 * @par api_copy
219 * #otDnsClientResolveAddress
220 * @par
221 * Send DNS Query to obtain IPv6 address for given hostname.
222 * @par
223 * The parameters after hostname are optional. Any unspecified (or zero) value
224 * for these optional parameters is replaced by the value from the current default
225 * config (dns config).
226 * @par
227 * The DNS server IP can be an IPv4 address, which will be synthesized to an
228 * IPv6 address using the preferred NAT64 prefix from the network data.
229 * @par
230 * Note: The command will return InvalidState when the DNS server IP is an IPv4
231 * address but the preferred NAT64 prefix is unavailable.
232 * `OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE` is required.
233 */
Process(Arg aArgs[])234 template <> otError Dns::Process<Cmd("resolve")>(Arg aArgs[])
235 {
236 otError error = OT_ERROR_NONE;
237 otDnsQueryConfig queryConfig;
238 otDnsQueryConfig *config = &queryConfig;
239
240 VerifyOrExit(!aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
241 SuccessOrExit(error = GetDnsConfig(aArgs + 1, config));
242 SuccessOrExit(error = otDnsClientResolveAddress(GetInstancePtr(), aArgs[0].GetCString(), &HandleDnsAddressResponse,
243 this, config));
244 error = OT_ERROR_PENDING;
245
246 exit:
247 return error;
248 }
249
250 #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE
Process(Arg aArgs[])251 template <> otError Dns::Process<Cmd("resolve4")>(Arg aArgs[])
252 {
253 otError error = OT_ERROR_NONE;
254 otDnsQueryConfig queryConfig;
255 otDnsQueryConfig *config = &queryConfig;
256
257 VerifyOrExit(!aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
258 SuccessOrExit(error = GetDnsConfig(aArgs + 1, config));
259 SuccessOrExit(error = otDnsClientResolveIp4Address(GetInstancePtr(), aArgs[0].GetCString(),
260 &HandleDnsAddressResponse, this, config));
261 error = OT_ERROR_PENDING;
262
263 exit:
264 return error;
265 }
266 #endif
267
268 #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
269
270 /**
271 * @cli dns browse
272 * @code
273 * dns browse _service._udp.example.com
274 * DNS browse response for _service._udp.example.com.
275 * inst1
276 * Port:1234, Priority:1, Weight:2, TTL:7200
277 * Host:host.example.com.
278 * HostAddress:fd00:0:0:0:0:0:0:abcd TTL:7200
279 * TXT:[a=6531, b=6c12] TTL:7300
280 * instance2
281 * Port:1234, Priority:1, Weight:2, TTL:7200
282 * Host:host.example.com.
283 * HostAddress:fd00:0:0:0:0:0:0:abcd TTL:7200
284 * TXT:[a=1234] TTL:7300
285 * Done
286 * @endcode
287 * @code
288 * dns browse _airplay._tcp.default.service.arpa
289 * DNS browse response for _airplay._tcp.default.service.arpa.
290 * Mac mini
291 * Port:7000, Priority:0, Weight:0, TTL:10
292 * Host:Mac-mini.default.service.arpa.
293 * HostAddress:fd97:739d:386a:1:1c2e:d83c:fcbe:9cf4 TTL:10
294 * Done
295 * @endcode
296 * @cparam dns browse @ca{service-name} [@ca{dns-server-IP}] [@ca{dns-server-port}] <!--
297 * --> [@ca{response-timeout-ms}] [@ca{max-tx-attempts}] <!--
298 * --> [@ca{recursion-desired-boolean}]
299 * @sa otDnsClientBrowse
300 * @par
301 * Send a browse (service instance enumeration) DNS query to get the list of services for
302 * given service-name
303 * @par
304 * The parameters after `service-name` are optional. Any unspecified (or zero) value
305 * for these optional parameters is replaced by the value from the current default
306 * config (`dns config`).
307 * @par
308 * Note: The DNS server IP can be an IPv4 address, which will be synthesized to an IPv6
309 * address using the preferred NAT64 prefix from the network data. The command will return
310 * `InvalidState` when the DNS server IP is an IPv4 address but the preferred NAT64 prefix
311 * is unavailable. When testing DNS-SD discovery proxy, the zone is not `local` and
312 * instead should be `default.service.arpa`.
313 * `OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE` is required.
314 */
Process(Arg aArgs[])315 template <> otError Dns::Process<Cmd("browse")>(Arg aArgs[])
316 {
317 otError error = OT_ERROR_NONE;
318 otDnsQueryConfig queryConfig;
319 otDnsQueryConfig *config = &queryConfig;
320
321 VerifyOrExit(!aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
322 SuccessOrExit(error = GetDnsConfig(aArgs + 1, config));
323 SuccessOrExit(
324 error = otDnsClientBrowse(GetInstancePtr(), aArgs[0].GetCString(), &HandleDnsBrowseResponse, this, config));
325 error = OT_ERROR_PENDING;
326
327 exit:
328 return error;
329 }
330
331 /**
332 * @cli dns service
333 * @cparam dns service @ca{service-instance-label} @ca{service-name} <!--
334 * --> [@ca{DNS-server-IP}] [@ca{DNS-server-port}] <!--
335 * --> [@ca{response-timeout-ms}] [@ca{max-tx-attempts}] <!--
336 * --> [@ca{recursion-desired-boolean}]
337 * @par api_copy
338 * #otDnsClientResolveService
339 * @par
340 * Send a service instance resolution DNS query for a given service instance.
341 * Service instance label is provided first, followed by the service name
342 * (note that service instance label can contain dot '.' character).
343 * @par
344 * The parameters after `service-name` are optional. Any unspecified (or zero)
345 * value for these optional parameters is replaced by the value from the
346 * current default config (`dns config`).
347 * @par
348 * Note: The DNS server IP can be an IPv4 address, which will be synthesized
349 * to an IPv6 address using the preferred NAT64 prefix from the network data.
350 * The command will return `InvalidState` when the DNS server IP is an IPv4
351 * address but the preferred NAT64 prefix is unavailable.
352 * `OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE` is required.
353 */
Process(Arg aArgs[])354 template <> otError Dns::Process<Cmd("service")>(Arg aArgs[])
355 {
356 return ProcessService(aArgs, otDnsClientResolveService);
357 }
358
359 /**
360 * @cli dns servicehost
361 * @cparam dns servicehost @ca{service-instance-label} @ca{service-name} <!--
362 * --> [@ca{DNS-server-IP}] [@ca{DNS-server-port}] <!--
363 * --> [@ca{response-timeout-ms}] [@ca{max-tx-attempts}] <!--
364 * --> [@ca{recursion-desired-boolean}]
365 * @par api_copy
366 * #otDnsClientResolveServiceAndHostAddress
367 * @par
368 * Send a service instance resolution DNS query for a given service instance
369 * with potential follow-up host name resolution.
370 * Service instance label is provided first, followed by the service name
371 * (note that service instance label can contain dot '.' character).
372 * @par
373 * The parameters after `service-name` are optional. Any unspecified (or zero)
374 * value for these optional parameters is replaced by the value from the
375 * current default config (`dns config`).
376 * @par
377 * Note: The DNS server IP can be an IPv4 address, which will be synthesized
378 * to an IPv6 address using the preferred NAT64 prefix from the network data.
379 * The command will return `InvalidState` when the DNS server IP is an IPv4
380 * address but the preferred NAT64 prefix is unavailable.
381 * `OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE` is required.
382 */
Process(Arg aArgs[])383 template <> otError Dns::Process<Cmd("servicehost")>(Arg aArgs[])
384 {
385 return ProcessService(aArgs, otDnsClientResolveServiceAndHostAddress);
386 }
387
ProcessService(Arg aArgs[],ResolveServiceFn aResolveServiceFn)388 otError Dns::ProcessService(Arg aArgs[], ResolveServiceFn aResolveServiceFn)
389 {
390 otError error = OT_ERROR_NONE;
391 otDnsQueryConfig queryConfig;
392 otDnsQueryConfig *config = &queryConfig;
393
394 VerifyOrExit(!aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
395 SuccessOrExit(error = GetDnsConfig(aArgs + 2, config));
396 SuccessOrExit(error = aResolveServiceFn(GetInstancePtr(), aArgs[0].GetCString(), aArgs[1].GetCString(),
397 &HandleDnsServiceResponse, this, config));
398 error = OT_ERROR_PENDING;
399
400 exit:
401 return error;
402 }
403
404 #endif // OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
405
406 //----------------------------------------------------------------------------------------------------------------------
407
OutputResult(otError aError)408 void Dns::OutputResult(otError aError) { Interpreter::GetInterpreter().OutputResult(aError); }
409
GetDnsConfig(Arg aArgs[],otDnsQueryConfig * & aConfig)410 otError Dns::GetDnsConfig(Arg aArgs[], otDnsQueryConfig *&aConfig)
411 {
412 // This method gets the optional DNS config from `aArgs[]`.
413 // The format: `[server IP address] [server port] [timeout]
414 // [max tx attempt] [recursion desired] [service mode]
415 // [transport]`
416
417 otError error = OT_ERROR_NONE;
418 bool recursionDesired;
419 bool nat64SynthesizedAddress;
420
421 memset(aConfig, 0, sizeof(otDnsQueryConfig));
422
423 VerifyOrExit(!aArgs[0].IsEmpty(), aConfig = nullptr);
424
425 SuccessOrExit(error = Interpreter::ParseToIp6Address(GetInstancePtr(), aArgs[0], aConfig->mServerSockAddr.mAddress,
426 nat64SynthesizedAddress));
427 if (nat64SynthesizedAddress)
428 {
429 OutputFormat("Synthesized IPv6 DNS server address: ");
430 OutputIp6AddressLine(aConfig->mServerSockAddr.mAddress);
431 }
432
433 VerifyOrExit(!aArgs[1].IsEmpty());
434 SuccessOrExit(error = aArgs[1].ParseAsUint16(aConfig->mServerSockAddr.mPort));
435
436 VerifyOrExit(!aArgs[2].IsEmpty());
437 SuccessOrExit(error = aArgs[2].ParseAsUint32(aConfig->mResponseTimeout));
438
439 VerifyOrExit(!aArgs[3].IsEmpty());
440 SuccessOrExit(error = aArgs[3].ParseAsUint8(aConfig->mMaxTxAttempts));
441
442 VerifyOrExit(!aArgs[4].IsEmpty());
443 SuccessOrExit(error = aArgs[4].ParseAsBool(recursionDesired));
444 aConfig->mRecursionFlag = recursionDesired ? OT_DNS_FLAG_RECURSION_DESIRED : OT_DNS_FLAG_NO_RECURSION;
445
446 VerifyOrExit(!aArgs[5].IsEmpty());
447 SuccessOrExit(error = ParseDnsServiceMode(aArgs[5], aConfig->mServiceMode));
448
449 VerifyOrExit(!aArgs[6].IsEmpty());
450
451 if (aArgs[6] == "tcp")
452 {
453 aConfig->mTransportProto = OT_DNS_TRANSPORT_TCP;
454 }
455 else if (aArgs[6] == "udp")
456 {
457 aConfig->mTransportProto = OT_DNS_TRANSPORT_UDP;
458 }
459 else
460 {
461 error = OT_ERROR_INVALID_ARGS;
462 }
463
464 exit:
465 return error;
466 }
467
468 const char *const Dns::kServiceModeStrings[] = {
469 "unspec", // OT_DNS_SERVICE_MODE_UNSPECIFIED (0)
470 "srv", // OT_DNS_SERVICE_MODE_SRV (1)
471 "txt", // OT_DNS_SERVICE_MODE_TXT (2)
472 "srv_txt", // OT_DNS_SERVICE_MODE_SRV_TXT (3)
473 "srv_txt_sep", // OT_DNS_SERVICE_MODE_SRV_TXT_SEPARATE (4)
474 "srv_txt_opt", // OT_DNS_SERVICE_MODE_SRV_TXT_OPTIMIZE (5)
475 };
476
477 static_assert(OT_DNS_SERVICE_MODE_UNSPECIFIED == 0, "OT_DNS_SERVICE_MODE_UNSPECIFIED value is incorrect");
478 static_assert(OT_DNS_SERVICE_MODE_SRV == 1, "OT_DNS_SERVICE_MODE_SRV value is incorrect");
479 static_assert(OT_DNS_SERVICE_MODE_TXT == 2, "OT_DNS_SERVICE_MODE_TXT value is incorrect");
480 static_assert(OT_DNS_SERVICE_MODE_SRV_TXT == 3, "OT_DNS_SERVICE_MODE_SRV_TXT value is incorrect");
481 static_assert(OT_DNS_SERVICE_MODE_SRV_TXT_SEPARATE == 4, "OT_DNS_SERVICE_MODE_SRV_TXT_SEPARATE value is incorrect");
482 static_assert(OT_DNS_SERVICE_MODE_SRV_TXT_OPTIMIZE == 5, "OT_DNS_SERVICE_MODE_SRV_TXT_OPTIMIZE value is incorrect");
483
DnsConfigServiceModeToString(otDnsServiceMode aMode) const484 const char *Dns::DnsConfigServiceModeToString(otDnsServiceMode aMode) const
485 {
486 return Stringify(aMode, kServiceModeStrings);
487 }
488
ParseDnsServiceMode(const Arg & aArg,otDnsServiceMode & aMode) const489 otError Dns::ParseDnsServiceMode(const Arg &aArg, otDnsServiceMode &aMode) const
490 {
491 otError error = OT_ERROR_NONE;
492
493 if (aArg == "def")
494 {
495 aMode = OT_DNS_SERVICE_MODE_UNSPECIFIED;
496 ExitNow();
497 }
498
499 for (size_t index = 0; index < OT_ARRAY_LENGTH(kServiceModeStrings); index++)
500 {
501 if (aArg == kServiceModeStrings[index])
502 {
503 aMode = static_cast<otDnsServiceMode>(index);
504 ExitNow();
505 }
506 }
507
508 error = OT_ERROR_INVALID_ARGS;
509
510 exit:
511 return error;
512 }
513
HandleDnsAddressResponse(otError aError,const otDnsAddressResponse * aResponse,void * aContext)514 void Dns::HandleDnsAddressResponse(otError aError, const otDnsAddressResponse *aResponse, void *aContext)
515 {
516 static_cast<Dns *>(aContext)->HandleDnsAddressResponse(aError, aResponse);
517 }
518
HandleDnsAddressResponse(otError aError,const otDnsAddressResponse * aResponse)519 void Dns::HandleDnsAddressResponse(otError aError, const otDnsAddressResponse *aResponse)
520 {
521 char hostName[OT_DNS_MAX_NAME_SIZE];
522 otIp6Address address;
523 uint32_t ttl;
524
525 IgnoreError(otDnsAddressResponseGetHostName(aResponse, hostName, sizeof(hostName)));
526
527 OutputFormat("DNS response for %s - ", hostName);
528
529 if (aError == OT_ERROR_NONE)
530 {
531 uint16_t index = 0;
532
533 while (otDnsAddressResponseGetAddress(aResponse, index, &address, &ttl) == OT_ERROR_NONE)
534 {
535 OutputIp6Address(address);
536 OutputFormat(" TTL:%lu ", ToUlong(ttl));
537 index++;
538 }
539 }
540
541 OutputNewLine();
542 OutputResult(aError);
543 }
544
545 #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
546
OutputDnsServiceInfo(uint8_t aIndentSize,const otDnsServiceInfo & aServiceInfo)547 void Dns::OutputDnsServiceInfo(uint8_t aIndentSize, const otDnsServiceInfo &aServiceInfo)
548 {
549 OutputLine(aIndentSize, "Port:%d, Priority:%d, Weight:%d, TTL:%lu", aServiceInfo.mPort, aServiceInfo.mPriority,
550 aServiceInfo.mWeight, ToUlong(aServiceInfo.mTtl));
551 OutputLine(aIndentSize, "Host:%s", aServiceInfo.mHostNameBuffer);
552 OutputFormat(aIndentSize, "HostAddress:");
553 OutputIp6Address(aServiceInfo.mHostAddress);
554 OutputLine(" TTL:%lu", ToUlong(aServiceInfo.mHostAddressTtl));
555 OutputFormat(aIndentSize, "TXT:");
556
557 if (!aServiceInfo.mTxtDataTruncated)
558 {
559 OutputDnsTxtData(aServiceInfo.mTxtData, aServiceInfo.mTxtDataSize);
560 }
561 else
562 {
563 OutputFormat("[");
564 OutputBytes(aServiceInfo.mTxtData, aServiceInfo.mTxtDataSize);
565 OutputFormat("...]");
566 }
567
568 OutputLine(" TTL:%lu", ToUlong(aServiceInfo.mTxtDataTtl));
569 }
570
HandleDnsBrowseResponse(otError aError,const otDnsBrowseResponse * aResponse,void * aContext)571 void Dns::HandleDnsBrowseResponse(otError aError, const otDnsBrowseResponse *aResponse, void *aContext)
572 {
573 static_cast<Dns *>(aContext)->HandleDnsBrowseResponse(aError, aResponse);
574 }
575
HandleDnsBrowseResponse(otError aError,const otDnsBrowseResponse * aResponse)576 void Dns::HandleDnsBrowseResponse(otError aError, const otDnsBrowseResponse *aResponse)
577 {
578 char name[OT_DNS_MAX_NAME_SIZE];
579 char label[OT_DNS_MAX_LABEL_SIZE];
580 uint8_t txtBuffer[kMaxTxtDataSize];
581 otDnsServiceInfo serviceInfo;
582
583 IgnoreError(otDnsBrowseResponseGetServiceName(aResponse, name, sizeof(name)));
584
585 OutputLine("DNS browse response for %s", name);
586
587 if (aError == OT_ERROR_NONE)
588 {
589 uint16_t index = 0;
590
591 while (otDnsBrowseResponseGetServiceInstance(aResponse, index, label, sizeof(label)) == OT_ERROR_NONE)
592 {
593 OutputLine("%s", label);
594 index++;
595
596 serviceInfo.mHostNameBuffer = name;
597 serviceInfo.mHostNameBufferSize = sizeof(name);
598 serviceInfo.mTxtData = txtBuffer;
599 serviceInfo.mTxtDataSize = sizeof(txtBuffer);
600
601 if (otDnsBrowseResponseGetServiceInfo(aResponse, label, &serviceInfo) == OT_ERROR_NONE)
602 {
603 OutputDnsServiceInfo(kIndentSize, serviceInfo);
604 }
605
606 OutputNewLine();
607 }
608 }
609
610 OutputResult(aError);
611 }
612
HandleDnsServiceResponse(otError aError,const otDnsServiceResponse * aResponse,void * aContext)613 void Dns::HandleDnsServiceResponse(otError aError, const otDnsServiceResponse *aResponse, void *aContext)
614 {
615 static_cast<Dns *>(aContext)->HandleDnsServiceResponse(aError, aResponse);
616 }
617
HandleDnsServiceResponse(otError aError,const otDnsServiceResponse * aResponse)618 void Dns::HandleDnsServiceResponse(otError aError, const otDnsServiceResponse *aResponse)
619 {
620 char name[OT_DNS_MAX_NAME_SIZE];
621 char label[OT_DNS_MAX_LABEL_SIZE];
622 uint8_t txtBuffer[kMaxTxtDataSize];
623 otDnsServiceInfo serviceInfo;
624
625 IgnoreError(otDnsServiceResponseGetServiceName(aResponse, label, sizeof(label), name, sizeof(name)));
626
627 OutputLine("DNS service resolution response for %s for service %s", label, name);
628
629 if (aError == OT_ERROR_NONE)
630 {
631 serviceInfo.mHostNameBuffer = name;
632 serviceInfo.mHostNameBufferSize = sizeof(name);
633 serviceInfo.mTxtData = txtBuffer;
634 serviceInfo.mTxtDataSize = sizeof(txtBuffer);
635
636 if (otDnsServiceResponseGetServiceInfo(aResponse, &serviceInfo) == OT_ERROR_NONE)
637 {
638 OutputDnsServiceInfo(/* aIndentSize */ 0, serviceInfo);
639 OutputNewLine();
640 }
641 }
642
643 OutputResult(aError);
644 }
645
646 #endif // OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
647 #endif // OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE
648
649 #if OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE
650
Process(Arg aArgs[])651 template <> otError Dns::Process<Cmd("server")>(Arg aArgs[])
652 {
653 otError error = OT_ERROR_NONE;
654
655 if (aArgs[0].IsEmpty())
656 {
657 error = OT_ERROR_INVALID_ARGS;
658 }
659 #if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE
660 /**
661 * @cli dns server upstream
662 * @code
663 * dns server upstream
664 * Enabled
665 * Done
666 * @endcode
667 * @par api_copy
668 * #otDnssdUpstreamQueryIsEnabled
669 */
670 else if (aArgs[0] == "upstream")
671 {
672 /**
673 * @cli dns server upstream {enable|disable}
674 * @code
675 * dns server upstream enable
676 * Done
677 * @endcode
678 * @cparam dns server upstream @ca{enable|disable}
679 * @par api_copy
680 * #otDnssdUpstreamQuerySetEnabled
681 */
682 error = Interpreter::GetInterpreter().ProcessEnableDisable(aArgs + 1, otDnssdUpstreamQueryIsEnabled,
683 otDnssdUpstreamQuerySetEnabled);
684 }
685 #endif // OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE
686 else
687 {
688 ExitNow(error = OT_ERROR_INVALID_COMMAND);
689 }
690
691 exit:
692 return error;
693 }
694
695 #endif // OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE
696
Process(Arg aArgs[])697 otError Dns::Process(Arg aArgs[])
698 {
699 #define CmdEntry(aCommandString) \
700 { \
701 aCommandString, &Dns::Process<Cmd(aCommandString)> \
702 }
703
704 static constexpr Command kCommands[] = {
705
706 #if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE && OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
707 CmdEntry("browse"),
708 #endif
709 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
710 CmdEntry("compression"),
711 #endif
712 #if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE
713 CmdEntry("config"),
714 CmdEntry("resolve"),
715 #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE
716 CmdEntry("resolve4"),
717 #endif
718 #endif // OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE
719 #if OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE
720 CmdEntry("server"),
721 #endif
722 #if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE && OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
723 CmdEntry("service"),
724 CmdEntry("servicehost"),
725 #endif
726 };
727
728 #undef CmdEntry
729
730 static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted");
731
732 otError error = OT_ERROR_INVALID_COMMAND;
733 const Command *command;
734
735 if (aArgs[0].IsEmpty() || (aArgs[0] == "help"))
736 {
737 OutputCommandTable(kCommands);
738 ExitNow(error = aArgs[0].IsEmpty() ? error : OT_ERROR_NONE);
739 }
740
741 command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
742 VerifyOrExit(command != nullptr);
743
744 error = (this->*command->mHandler)(aArgs + 1);
745
746 exit:
747 return error;
748 }
749
750 } // namespace Cli
751 } // namespace ot
752
753 #endif // OPENTHREAD_CLI_DNS_ENABLE
754