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 = 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 * ServiceMode: srv_txt_opt
165 * Nat64Mode: allow
166 * TransportProtocol: udp
167 * Done
168 * @endcode
169 * @code
170 * dns config fd00::2
171 * Done
172 * @endcode
173 * @code
174 * dns config
175 * Server: [fd00:0:0:0:0:0:0:2]:53
176 * ResponseTimeout: 6000 ms
177 * MaxTxAttempts: 3
178 * RecursionDesired: yes
179 * ServiceMode: srv_txt_opt
180 * Nat64Mode: allow
181 * TransportProtocol: udp
182 * Done
183 * @endcode
184 * @par api_copy
185 * #otDnsClientSetDefaultConfig
186 * @cparam dns config [@ca{dns-server-IP}] [@ca{dns-server-port}] <!--
187 * --> [@ca{response-timeout-ms}] [@ca{max-tx-attempts}] <!--
188 * --> [@ca{recursion-desired-boolean}] [@ca{service-mode}] <!--
189 * --> [@ca{protocol}]
190 * @par
191 * We can leave some of the fields as unspecified (or use value zero). The
192 * unspecified fields are replaced by the corresponding OT config option
193 * definitions `OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT` to form the default
194 * query config.
195 * `OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE` is required.
196 */
197 else
198 {
199 otDnsQueryConfig queryConfig;
200 otDnsQueryConfig *config = &queryConfig;
201
202 SuccessOrExit(error = GetDnsConfig(aArgs, config));
203 otDnsClientSetDefaultConfig(GetInstancePtr(), config);
204 }
205
206 exit:
207 return error;
208 }
209
210 /**
211 * @cli dns resolve
212 * @code
213 * dns resolve ipv6.google.com
214 * DNS response for ipv6.google.com - 2a00:1450:401b:801:0:0:0:200e TTL: 300
215 * @endcode
216 * @code
217 * dns resolve example.com 8.8.8.8
218 * Synthesized IPv6 DNS server address: fdde:ad00:beef:2:0:0:808:808
219 * DNS response for example.com. - fd4c:9574:3720:2:0:0:5db8:d822 TTL:20456
220 * Done
221 * @endcode
222 * @cparam dns resolve @ca{hostname} [@ca{dns-server-IP}] <!--
223 * --> [@ca{dns-server-port}] [@ca{response-timeout-ms}] <!--
224 * --> [@ca{max-tx-attempts}] [@ca{recursion-desired-boolean}]
225 * @par api_copy
226 * #otDnsClientResolveAddress
227 * @par
228 * Send DNS Query to obtain IPv6 address for given hostname.
229 * @par
230 * The parameters after hostname are optional. Any unspecified (or zero) value
231 * for these optional parameters is replaced by the value from the current default
232 * config (dns config).
233 * @par
234 * The DNS server IP can be an IPv4 address, which will be synthesized to an
235 * IPv6 address using the preferred NAT64 prefix from the network data.
236 * @par
237 * Note: The command will return InvalidState when the DNS server IP is an IPv4
238 * address but the preferred NAT64 prefix is unavailable.
239 * `OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE` is required.
240 */
Process(Arg aArgs[])241 template <> otError Dns::Process<Cmd("resolve")>(Arg aArgs[])
242 {
243 otError error = OT_ERROR_NONE;
244 otDnsQueryConfig queryConfig;
245 otDnsQueryConfig *config = &queryConfig;
246
247 VerifyOrExit(!aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
248 SuccessOrExit(error = GetDnsConfig(aArgs + 1, config));
249 SuccessOrExit(error = otDnsClientResolveAddress(GetInstancePtr(), aArgs[0].GetCString(), &HandleDnsAddressResponse,
250 this, config));
251 error = OT_ERROR_PENDING;
252
253 exit:
254 return error;
255 }
256
257 #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE
Process(Arg aArgs[])258 template <> otError Dns::Process<Cmd("resolve4")>(Arg aArgs[])
259 {
260 otError error = OT_ERROR_NONE;
261 otDnsQueryConfig queryConfig;
262 otDnsQueryConfig *config = &queryConfig;
263
264 VerifyOrExit(!aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
265 SuccessOrExit(error = GetDnsConfig(aArgs + 1, config));
266 SuccessOrExit(error = otDnsClientResolveIp4Address(GetInstancePtr(), aArgs[0].GetCString(),
267 &HandleDnsAddressResponse, this, config));
268 error = OT_ERROR_PENDING;
269
270 exit:
271 return error;
272 }
273 #endif
274
275 #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
276
277 /**
278 * @cli dns browse
279 * @code
280 * dns browse _service._udp.example.com
281 * DNS browse response for _service._udp.example.com.
282 * inst1
283 * Port:1234, Priority:1, Weight:2, TTL:7200
284 * Host:host.example.com.
285 * HostAddress:fd00:0:0:0:0:0:0:abcd TTL:7200
286 * TXT:[a=6531, b=6c12] TTL:7300
287 * instance2
288 * Port:1234, Priority:1, Weight:2, TTL:7200
289 * Host:host.example.com.
290 * HostAddress:fd00:0:0:0:0:0:0:abcd TTL:7200
291 * TXT:[a=1234] TTL:7300
292 * Done
293 * @endcode
294 * @code
295 * dns browse _airplay._tcp.default.service.arpa
296 * DNS browse response for _airplay._tcp.default.service.arpa.
297 * Mac mini
298 * Port:7000, Priority:0, Weight:0, TTL:10
299 * Host:Mac-mini.default.service.arpa.
300 * HostAddress:fd97:739d:386a:1:1c2e:d83c:fcbe:9cf4 TTL:10
301 * Done
302 * @endcode
303 * @cparam dns browse @ca{service-name} [@ca{dns-server-IP}] [@ca{dns-server-port}] <!--
304 * --> [@ca{response-timeout-ms}] [@ca{max-tx-attempts}] <!--
305 * --> [@ca{recursion-desired-boolean}]
306 * @sa otDnsClientBrowse
307 * @par
308 * Send a browse (service instance enumeration) DNS query to get the list of services for
309 * given service-name
310 * @par
311 * The parameters after `service-name` are optional. Any unspecified (or zero) value
312 * for these optional parameters is replaced by the value from the current default
313 * config (`dns config`).
314 * @par
315 * Note: The DNS server IP can be an IPv4 address, which will be synthesized to an IPv6
316 * address using the preferred NAT64 prefix from the network data. The command will return
317 * `InvalidState` when the DNS server IP is an IPv4 address but the preferred NAT64 prefix
318 * is unavailable. When testing DNS-SD discovery proxy, the zone is not `local` and
319 * instead should be `default.service.arpa`.
320 * `OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE` is required.
321 */
Process(Arg aArgs[])322 template <> otError Dns::Process<Cmd("browse")>(Arg aArgs[])
323 {
324 otError error = OT_ERROR_NONE;
325 otDnsQueryConfig queryConfig;
326 otDnsQueryConfig *config = &queryConfig;
327
328 VerifyOrExit(!aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
329 SuccessOrExit(error = GetDnsConfig(aArgs + 1, config));
330 SuccessOrExit(
331 error = otDnsClientBrowse(GetInstancePtr(), aArgs[0].GetCString(), &HandleDnsBrowseResponse, this, config));
332 error = OT_ERROR_PENDING;
333
334 exit:
335 return error;
336 }
337
338 /**
339 * @cli dns service
340 * @cparam dns service @ca{service-instance-label} @ca{service-name} <!--
341 * --> [@ca{DNS-server-IP}] [@ca{DNS-server-port}] <!--
342 * --> [@ca{response-timeout-ms}] [@ca{max-tx-attempts}] <!--
343 * --> [@ca{recursion-desired-boolean}]
344 * @par api_copy
345 * #otDnsClientResolveService
346 * @par
347 * Send a service instance resolution DNS query for a given service instance.
348 * Service instance label is provided first, followed by the service name
349 * (note that service instance label can contain dot '.' character).
350 * @par
351 * The parameters after `service-name` are optional. Any unspecified (or zero)
352 * value for these optional parameters is replaced by the value from the
353 * current default config (`dns config`).
354 * @par
355 * Note: The DNS server IP can be an IPv4 address, which will be synthesized
356 * to an IPv6 address using the preferred NAT64 prefix from the network data.
357 * The command will return `InvalidState` when the DNS server IP is an IPv4
358 * address but the preferred NAT64 prefix is unavailable.
359 * `OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE` is required.
360 */
Process(Arg aArgs[])361 template <> otError Dns::Process<Cmd("service")>(Arg aArgs[])
362 {
363 return ProcessService(aArgs, otDnsClientResolveService);
364 }
365
366 /**
367 * @cli dns servicehost
368 * @cparam dns servicehost @ca{service-instance-label} @ca{service-name} <!--
369 * --> [@ca{DNS-server-IP}] [@ca{DNS-server-port}] <!--
370 * --> [@ca{response-timeout-ms}] [@ca{max-tx-attempts}] <!--
371 * --> [@ca{recursion-desired-boolean}]
372 * @par api_copy
373 * #otDnsClientResolveServiceAndHostAddress
374 * @par
375 * Send a service instance resolution DNS query for a given service instance
376 * with potential follow-up host name resolution.
377 * Service instance label is provided first, followed by the service name
378 * (note that service instance label can contain dot '.' character).
379 * @par
380 * The parameters after `service-name` are optional. Any unspecified (or zero)
381 * value for these optional parameters is replaced by the value from the
382 * current default config (`dns config`).
383 * @par
384 * Note: The DNS server IP can be an IPv4 address, which will be synthesized
385 * to an IPv6 address using the preferred NAT64 prefix from the network data.
386 * The command will return `InvalidState` when the DNS server IP is an IPv4
387 * address but the preferred NAT64 prefix is unavailable.
388 * `OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE` is required.
389 */
Process(Arg aArgs[])390 template <> otError Dns::Process<Cmd("servicehost")>(Arg aArgs[])
391 {
392 return ProcessService(aArgs, otDnsClientResolveServiceAndHostAddress);
393 }
394
ProcessService(Arg aArgs[],ResolveServiceFn aResolveServiceFn)395 otError Dns::ProcessService(Arg aArgs[], ResolveServiceFn aResolveServiceFn)
396 {
397 otError error = OT_ERROR_NONE;
398 otDnsQueryConfig queryConfig;
399 otDnsQueryConfig *config = &queryConfig;
400
401 VerifyOrExit(!aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
402 SuccessOrExit(error = GetDnsConfig(aArgs + 2, config));
403 SuccessOrExit(error = aResolveServiceFn(GetInstancePtr(), aArgs[0].GetCString(), aArgs[1].GetCString(),
404 &HandleDnsServiceResponse, this, config));
405 error = OT_ERROR_PENDING;
406
407 exit:
408 return error;
409 }
410
411 #endif // OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
412
413 //----------------------------------------------------------------------------------------------------------------------
414
OutputResult(otError aError)415 void Dns::OutputResult(otError aError) { Interpreter::GetInterpreter().OutputResult(aError); }
416
GetDnsConfig(Arg aArgs[],otDnsQueryConfig * & aConfig)417 otError Dns::GetDnsConfig(Arg aArgs[], otDnsQueryConfig *&aConfig)
418 {
419 // This method gets the optional DNS config from `aArgs[]`.
420 // The format: `[server IP address] [server port] [timeout]
421 // [max tx attempt] [recursion desired] [service mode]
422 // [transport]`
423
424 otError error = OT_ERROR_NONE;
425 bool recursionDesired;
426 bool nat64Synth;
427
428 ClearAllBytes(*aConfig);
429
430 VerifyOrExit(!aArgs[0].IsEmpty(), aConfig = nullptr);
431
432 SuccessOrExit(error = ParseToIp6Address(GetInstancePtr(), aArgs[0], aConfig->mServerSockAddr.mAddress, nat64Synth));
433 if (nat64Synth)
434 {
435 OutputFormat("Synthesized IPv6 DNS server address: ");
436 OutputIp6AddressLine(aConfig->mServerSockAddr.mAddress);
437 }
438
439 VerifyOrExit(!aArgs[1].IsEmpty());
440 SuccessOrExit(error = aArgs[1].ParseAsUint16(aConfig->mServerSockAddr.mPort));
441
442 VerifyOrExit(!aArgs[2].IsEmpty());
443 SuccessOrExit(error = aArgs[2].ParseAsUint32(aConfig->mResponseTimeout));
444
445 VerifyOrExit(!aArgs[3].IsEmpty());
446 SuccessOrExit(error = aArgs[3].ParseAsUint8(aConfig->mMaxTxAttempts));
447
448 VerifyOrExit(!aArgs[4].IsEmpty());
449 SuccessOrExit(error = aArgs[4].ParseAsBool(recursionDesired));
450 aConfig->mRecursionFlag = recursionDesired ? OT_DNS_FLAG_RECURSION_DESIRED : OT_DNS_FLAG_NO_RECURSION;
451
452 VerifyOrExit(!aArgs[5].IsEmpty());
453 SuccessOrExit(error = ParseDnsServiceMode(aArgs[5], aConfig->mServiceMode));
454
455 VerifyOrExit(!aArgs[6].IsEmpty());
456
457 if (aArgs[6] == "tcp")
458 {
459 aConfig->mTransportProto = OT_DNS_TRANSPORT_TCP;
460 }
461 else if (aArgs[6] == "udp")
462 {
463 aConfig->mTransportProto = OT_DNS_TRANSPORT_UDP;
464 }
465 else
466 {
467 error = OT_ERROR_INVALID_ARGS;
468 }
469
470 exit:
471 return error;
472 }
473
474 const char *const Dns::kServiceModeStrings[] = {
475 "unspec", // OT_DNS_SERVICE_MODE_UNSPECIFIED (0)
476 "srv", // OT_DNS_SERVICE_MODE_SRV (1)
477 "txt", // OT_DNS_SERVICE_MODE_TXT (2)
478 "srv_txt", // OT_DNS_SERVICE_MODE_SRV_TXT (3)
479 "srv_txt_sep", // OT_DNS_SERVICE_MODE_SRV_TXT_SEPARATE (4)
480 "srv_txt_opt", // OT_DNS_SERVICE_MODE_SRV_TXT_OPTIMIZE (5)
481 };
482
483 static_assert(OT_DNS_SERVICE_MODE_UNSPECIFIED == 0, "OT_DNS_SERVICE_MODE_UNSPECIFIED value is incorrect");
484 static_assert(OT_DNS_SERVICE_MODE_SRV == 1, "OT_DNS_SERVICE_MODE_SRV value is incorrect");
485 static_assert(OT_DNS_SERVICE_MODE_TXT == 2, "OT_DNS_SERVICE_MODE_TXT value is incorrect");
486 static_assert(OT_DNS_SERVICE_MODE_SRV_TXT == 3, "OT_DNS_SERVICE_MODE_SRV_TXT value is incorrect");
487 static_assert(OT_DNS_SERVICE_MODE_SRV_TXT_SEPARATE == 4, "OT_DNS_SERVICE_MODE_SRV_TXT_SEPARATE value is incorrect");
488 static_assert(OT_DNS_SERVICE_MODE_SRV_TXT_OPTIMIZE == 5, "OT_DNS_SERVICE_MODE_SRV_TXT_OPTIMIZE value is incorrect");
489
DnsConfigServiceModeToString(otDnsServiceMode aMode) const490 const char *Dns::DnsConfigServiceModeToString(otDnsServiceMode aMode) const
491 {
492 return Stringify(aMode, kServiceModeStrings);
493 }
494
ParseDnsServiceMode(const Arg & aArg,otDnsServiceMode & aMode) const495 otError Dns::ParseDnsServiceMode(const Arg &aArg, otDnsServiceMode &aMode) const
496 {
497 otError error = OT_ERROR_NONE;
498
499 if (aArg == "def")
500 {
501 aMode = OT_DNS_SERVICE_MODE_UNSPECIFIED;
502 ExitNow();
503 }
504
505 for (size_t index = 0; index < OT_ARRAY_LENGTH(kServiceModeStrings); index++)
506 {
507 if (aArg == kServiceModeStrings[index])
508 {
509 aMode = static_cast<otDnsServiceMode>(index);
510 ExitNow();
511 }
512 }
513
514 error = OT_ERROR_INVALID_ARGS;
515
516 exit:
517 return error;
518 }
519
HandleDnsAddressResponse(otError aError,const otDnsAddressResponse * aResponse,void * aContext)520 void Dns::HandleDnsAddressResponse(otError aError, const otDnsAddressResponse *aResponse, void *aContext)
521 {
522 static_cast<Dns *>(aContext)->HandleDnsAddressResponse(aError, aResponse);
523 }
524
HandleDnsAddressResponse(otError aError,const otDnsAddressResponse * aResponse)525 void Dns::HandleDnsAddressResponse(otError aError, const otDnsAddressResponse *aResponse)
526 {
527 char hostName[OT_DNS_MAX_NAME_SIZE];
528 otIp6Address address;
529 uint32_t ttl;
530
531 IgnoreError(otDnsAddressResponseGetHostName(aResponse, hostName, sizeof(hostName)));
532
533 OutputFormat("DNS response for %s - ", hostName);
534
535 if (aError == OT_ERROR_NONE)
536 {
537 uint16_t index = 0;
538
539 while (otDnsAddressResponseGetAddress(aResponse, index, &address, &ttl) == OT_ERROR_NONE)
540 {
541 OutputIp6Address(address);
542 OutputFormat(" TTL:%lu ", ToUlong(ttl));
543 index++;
544 }
545 }
546
547 OutputNewLine();
548 OutputResult(aError);
549 }
550
551 #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
552
OutputDnsServiceInfo(uint8_t aIndentSize,const otDnsServiceInfo & aServiceInfo)553 void Dns::OutputDnsServiceInfo(uint8_t aIndentSize, const otDnsServiceInfo &aServiceInfo)
554 {
555 OutputLine(aIndentSize, "Port:%d, Priority:%d, Weight:%d, TTL:%lu", aServiceInfo.mPort, aServiceInfo.mPriority,
556 aServiceInfo.mWeight, ToUlong(aServiceInfo.mTtl));
557 OutputLine(aIndentSize, "Host:%s", aServiceInfo.mHostNameBuffer);
558 OutputFormat(aIndentSize, "HostAddress:");
559 OutputIp6Address(aServiceInfo.mHostAddress);
560 OutputLine(" TTL:%lu", ToUlong(aServiceInfo.mHostAddressTtl));
561 OutputFormat(aIndentSize, "TXT:");
562
563 if (!aServiceInfo.mTxtDataTruncated)
564 {
565 OutputDnsTxtData(aServiceInfo.mTxtData, aServiceInfo.mTxtDataSize);
566 }
567 else
568 {
569 OutputFormat("[");
570 OutputBytes(aServiceInfo.mTxtData, aServiceInfo.mTxtDataSize);
571 OutputFormat("...]");
572 }
573
574 OutputLine(" TTL:%lu", ToUlong(aServiceInfo.mTxtDataTtl));
575 }
576
HandleDnsBrowseResponse(otError aError,const otDnsBrowseResponse * aResponse,void * aContext)577 void Dns::HandleDnsBrowseResponse(otError aError, const otDnsBrowseResponse *aResponse, void *aContext)
578 {
579 static_cast<Dns *>(aContext)->HandleDnsBrowseResponse(aError, aResponse);
580 }
581
HandleDnsBrowseResponse(otError aError,const otDnsBrowseResponse * aResponse)582 void Dns::HandleDnsBrowseResponse(otError aError, const otDnsBrowseResponse *aResponse)
583 {
584 char name[OT_DNS_MAX_NAME_SIZE];
585 char label[OT_DNS_MAX_LABEL_SIZE];
586 uint8_t txtBuffer[kMaxTxtDataSize];
587 otDnsServiceInfo serviceInfo;
588
589 IgnoreError(otDnsBrowseResponseGetServiceName(aResponse, name, sizeof(name)));
590
591 OutputLine("DNS browse response for %s", name);
592
593 if (aError == OT_ERROR_NONE)
594 {
595 uint16_t index = 0;
596
597 while (otDnsBrowseResponseGetServiceInstance(aResponse, index, label, sizeof(label)) == OT_ERROR_NONE)
598 {
599 OutputLine("%s", label);
600 index++;
601
602 serviceInfo.mHostNameBuffer = name;
603 serviceInfo.mHostNameBufferSize = sizeof(name);
604 serviceInfo.mTxtData = txtBuffer;
605 serviceInfo.mTxtDataSize = sizeof(txtBuffer);
606
607 if (otDnsBrowseResponseGetServiceInfo(aResponse, label, &serviceInfo) == OT_ERROR_NONE)
608 {
609 OutputDnsServiceInfo(kIndentSize, serviceInfo);
610 }
611
612 OutputNewLine();
613 }
614 }
615
616 OutputResult(aError);
617 }
618
HandleDnsServiceResponse(otError aError,const otDnsServiceResponse * aResponse,void * aContext)619 void Dns::HandleDnsServiceResponse(otError aError, const otDnsServiceResponse *aResponse, void *aContext)
620 {
621 static_cast<Dns *>(aContext)->HandleDnsServiceResponse(aError, aResponse);
622 }
623
HandleDnsServiceResponse(otError aError,const otDnsServiceResponse * aResponse)624 void Dns::HandleDnsServiceResponse(otError aError, const otDnsServiceResponse *aResponse)
625 {
626 char name[OT_DNS_MAX_NAME_SIZE];
627 char label[OT_DNS_MAX_LABEL_SIZE];
628 uint8_t txtBuffer[kMaxTxtDataSize];
629 otDnsServiceInfo serviceInfo;
630
631 IgnoreError(otDnsServiceResponseGetServiceName(aResponse, label, sizeof(label), name, sizeof(name)));
632
633 OutputLine("DNS service resolution response for %s for service %s", label, name);
634
635 if (aError == OT_ERROR_NONE)
636 {
637 serviceInfo.mHostNameBuffer = name;
638 serviceInfo.mHostNameBufferSize = sizeof(name);
639 serviceInfo.mTxtData = txtBuffer;
640 serviceInfo.mTxtDataSize = sizeof(txtBuffer);
641
642 if (otDnsServiceResponseGetServiceInfo(aResponse, &serviceInfo) == OT_ERROR_NONE)
643 {
644 OutputDnsServiceInfo(/* aIndentSize */ 0, serviceInfo);
645 OutputNewLine();
646 }
647 }
648
649 OutputResult(aError);
650 }
651
652 #endif // OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
653 #endif // OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE
654
655 #if OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE
656
Process(Arg aArgs[])657 template <> otError Dns::Process<Cmd("server")>(Arg aArgs[])
658 {
659 otError error = OT_ERROR_NONE;
660
661 if (aArgs[0].IsEmpty())
662 {
663 error = OT_ERROR_INVALID_ARGS;
664 }
665 #if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE
666 /**
667 * @cli dns server upstream
668 * @code
669 * dns server upstream
670 * Enabled
671 * Done
672 * @endcode
673 * @par api_copy
674 * #otDnssdUpstreamQueryIsEnabled
675 */
676 else if (aArgs[0] == "upstream")
677 {
678 /**
679 * @cli dns server upstream {enable|disable}
680 * @code
681 * dns server upstream enable
682 * Done
683 * @endcode
684 * @cparam dns server upstream @ca{enable|disable}
685 * @par api_copy
686 * #otDnssdUpstreamQuerySetEnabled
687 */
688 error = ProcessEnableDisable(aArgs + 1, otDnssdUpstreamQueryIsEnabled, otDnssdUpstreamQuerySetEnabled);
689 }
690 #endif // OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE
691 else
692 {
693 ExitNow(error = OT_ERROR_INVALID_COMMAND);
694 }
695
696 exit:
697 return error;
698 }
699
700 #endif // OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE
701
Process(Arg aArgs[])702 otError Dns::Process(Arg aArgs[])
703 {
704 #define CmdEntry(aCommandString) \
705 { \
706 aCommandString, &Dns::Process<Cmd(aCommandString)> \
707 }
708
709 static constexpr Command kCommands[] = {
710
711 #if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE && OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
712 CmdEntry("browse"),
713 #endif
714 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
715 CmdEntry("compression"),
716 #endif
717 #if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE
718 CmdEntry("config"),
719 CmdEntry("resolve"),
720 #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE
721 CmdEntry("resolve4"),
722 #endif
723 #endif // OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE
724 #if OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE
725 CmdEntry("server"),
726 #endif
727 #if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE && OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
728 CmdEntry("service"),
729 CmdEntry("servicehost"),
730 #endif
731 };
732
733 #undef CmdEntry
734
735 static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted");
736
737 otError error = OT_ERROR_INVALID_COMMAND;
738 const Command *command;
739
740 if (aArgs[0].IsEmpty() || (aArgs[0] == "help"))
741 {
742 OutputCommandTable(kCommands);
743 ExitNow(error = aArgs[0].IsEmpty() ? error : OT_ERROR_NONE);
744 }
745
746 command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
747 VerifyOrExit(command != nullptr);
748
749 error = (this->*command->mHandler)(aArgs + 1);
750
751 exit:
752 return error;
753 }
754
755 } // namespace Cli
756 } // namespace ot
757
758 #endif // OPENTHREAD_CLI_DNS_ENABLE
759