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 * 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 nat64Synth;
420
421 ClearAllBytes(*aConfig);
422
423 VerifyOrExit(!aArgs[0].IsEmpty(), aConfig = nullptr);
424
425 SuccessOrExit(error = ParseToIp6Address(GetInstancePtr(), aArgs[0], aConfig->mServerSockAddr.mAddress, nat64Synth));
426 if (nat64Synth)
427 {
428 OutputFormat("Synthesized IPv6 DNS server address: ");
429 OutputIp6AddressLine(aConfig->mServerSockAddr.mAddress);
430 }
431
432 VerifyOrExit(!aArgs[1].IsEmpty());
433 SuccessOrExit(error = aArgs[1].ParseAsUint16(aConfig->mServerSockAddr.mPort));
434
435 VerifyOrExit(!aArgs[2].IsEmpty());
436 SuccessOrExit(error = aArgs[2].ParseAsUint32(aConfig->mResponseTimeout));
437
438 VerifyOrExit(!aArgs[3].IsEmpty());
439 SuccessOrExit(error = aArgs[3].ParseAsUint8(aConfig->mMaxTxAttempts));
440
441 VerifyOrExit(!aArgs[4].IsEmpty());
442 SuccessOrExit(error = aArgs[4].ParseAsBool(recursionDesired));
443 aConfig->mRecursionFlag = recursionDesired ? OT_DNS_FLAG_RECURSION_DESIRED : OT_DNS_FLAG_NO_RECURSION;
444
445 VerifyOrExit(!aArgs[5].IsEmpty());
446 SuccessOrExit(error = ParseDnsServiceMode(aArgs[5], aConfig->mServiceMode));
447
448 VerifyOrExit(!aArgs[6].IsEmpty());
449
450 if (aArgs[6] == "tcp")
451 {
452 aConfig->mTransportProto = OT_DNS_TRANSPORT_TCP;
453 }
454 else if (aArgs[6] == "udp")
455 {
456 aConfig->mTransportProto = OT_DNS_TRANSPORT_UDP;
457 }
458 else
459 {
460 error = OT_ERROR_INVALID_ARGS;
461 }
462
463 exit:
464 return error;
465 }
466
467 const char *const Dns::kServiceModeStrings[] = {
468 "unspec", // OT_DNS_SERVICE_MODE_UNSPECIFIED (0)
469 "srv", // OT_DNS_SERVICE_MODE_SRV (1)
470 "txt", // OT_DNS_SERVICE_MODE_TXT (2)
471 "srv_txt", // OT_DNS_SERVICE_MODE_SRV_TXT (3)
472 "srv_txt_sep", // OT_DNS_SERVICE_MODE_SRV_TXT_SEPARATE (4)
473 "srv_txt_opt", // OT_DNS_SERVICE_MODE_SRV_TXT_OPTIMIZE (5)
474 };
475
476 static_assert(OT_DNS_SERVICE_MODE_UNSPECIFIED == 0, "OT_DNS_SERVICE_MODE_UNSPECIFIED value is incorrect");
477 static_assert(OT_DNS_SERVICE_MODE_SRV == 1, "OT_DNS_SERVICE_MODE_SRV value is incorrect");
478 static_assert(OT_DNS_SERVICE_MODE_TXT == 2, "OT_DNS_SERVICE_MODE_TXT value is incorrect");
479 static_assert(OT_DNS_SERVICE_MODE_SRV_TXT == 3, "OT_DNS_SERVICE_MODE_SRV_TXT value is incorrect");
480 static_assert(OT_DNS_SERVICE_MODE_SRV_TXT_SEPARATE == 4, "OT_DNS_SERVICE_MODE_SRV_TXT_SEPARATE value is incorrect");
481 static_assert(OT_DNS_SERVICE_MODE_SRV_TXT_OPTIMIZE == 5, "OT_DNS_SERVICE_MODE_SRV_TXT_OPTIMIZE value is incorrect");
482
DnsConfigServiceModeToString(otDnsServiceMode aMode) const483 const char *Dns::DnsConfigServiceModeToString(otDnsServiceMode aMode) const
484 {
485 return Stringify(aMode, kServiceModeStrings);
486 }
487
ParseDnsServiceMode(const Arg & aArg,otDnsServiceMode & aMode) const488 otError Dns::ParseDnsServiceMode(const Arg &aArg, otDnsServiceMode &aMode) const
489 {
490 otError error = OT_ERROR_NONE;
491
492 if (aArg == "def")
493 {
494 aMode = OT_DNS_SERVICE_MODE_UNSPECIFIED;
495 ExitNow();
496 }
497
498 for (size_t index = 0; index < OT_ARRAY_LENGTH(kServiceModeStrings); index++)
499 {
500 if (aArg == kServiceModeStrings[index])
501 {
502 aMode = static_cast<otDnsServiceMode>(index);
503 ExitNow();
504 }
505 }
506
507 error = OT_ERROR_INVALID_ARGS;
508
509 exit:
510 return error;
511 }
512
HandleDnsAddressResponse(otError aError,const otDnsAddressResponse * aResponse,void * aContext)513 void Dns::HandleDnsAddressResponse(otError aError, const otDnsAddressResponse *aResponse, void *aContext)
514 {
515 static_cast<Dns *>(aContext)->HandleDnsAddressResponse(aError, aResponse);
516 }
517
HandleDnsAddressResponse(otError aError,const otDnsAddressResponse * aResponse)518 void Dns::HandleDnsAddressResponse(otError aError, const otDnsAddressResponse *aResponse)
519 {
520 char hostName[OT_DNS_MAX_NAME_SIZE];
521 otIp6Address address;
522 uint32_t ttl;
523
524 IgnoreError(otDnsAddressResponseGetHostName(aResponse, hostName, sizeof(hostName)));
525
526 OutputFormat("DNS response for %s - ", hostName);
527
528 if (aError == OT_ERROR_NONE)
529 {
530 uint16_t index = 0;
531
532 while (otDnsAddressResponseGetAddress(aResponse, index, &address, &ttl) == OT_ERROR_NONE)
533 {
534 OutputIp6Address(address);
535 OutputFormat(" TTL:%lu ", ToUlong(ttl));
536 index++;
537 }
538 }
539
540 OutputNewLine();
541 OutputResult(aError);
542 }
543
544 #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
545
OutputDnsServiceInfo(uint8_t aIndentSize,const otDnsServiceInfo & aServiceInfo)546 void Dns::OutputDnsServiceInfo(uint8_t aIndentSize, const otDnsServiceInfo &aServiceInfo)
547 {
548 OutputLine(aIndentSize, "Port:%d, Priority:%d, Weight:%d, TTL:%lu", aServiceInfo.mPort, aServiceInfo.mPriority,
549 aServiceInfo.mWeight, ToUlong(aServiceInfo.mTtl));
550 OutputLine(aIndentSize, "Host:%s", aServiceInfo.mHostNameBuffer);
551 OutputFormat(aIndentSize, "HostAddress:");
552 OutputIp6Address(aServiceInfo.mHostAddress);
553 OutputLine(" TTL:%lu", ToUlong(aServiceInfo.mHostAddressTtl));
554 OutputFormat(aIndentSize, "TXT:");
555
556 if (!aServiceInfo.mTxtDataTruncated)
557 {
558 OutputDnsTxtData(aServiceInfo.mTxtData, aServiceInfo.mTxtDataSize);
559 }
560 else
561 {
562 OutputFormat("[");
563 OutputBytes(aServiceInfo.mTxtData, aServiceInfo.mTxtDataSize);
564 OutputFormat("...]");
565 }
566
567 OutputLine(" TTL:%lu", ToUlong(aServiceInfo.mTxtDataTtl));
568 }
569
HandleDnsBrowseResponse(otError aError,const otDnsBrowseResponse * aResponse,void * aContext)570 void Dns::HandleDnsBrowseResponse(otError aError, const otDnsBrowseResponse *aResponse, void *aContext)
571 {
572 static_cast<Dns *>(aContext)->HandleDnsBrowseResponse(aError, aResponse);
573 }
574
HandleDnsBrowseResponse(otError aError,const otDnsBrowseResponse * aResponse)575 void Dns::HandleDnsBrowseResponse(otError aError, const otDnsBrowseResponse *aResponse)
576 {
577 char name[OT_DNS_MAX_NAME_SIZE];
578 char label[OT_DNS_MAX_LABEL_SIZE];
579 uint8_t txtBuffer[kMaxTxtDataSize];
580 otDnsServiceInfo serviceInfo;
581
582 IgnoreError(otDnsBrowseResponseGetServiceName(aResponse, name, sizeof(name)));
583
584 OutputLine("DNS browse response for %s", name);
585
586 if (aError == OT_ERROR_NONE)
587 {
588 uint16_t index = 0;
589
590 while (otDnsBrowseResponseGetServiceInstance(aResponse, index, label, sizeof(label)) == OT_ERROR_NONE)
591 {
592 OutputLine("%s", label);
593 index++;
594
595 serviceInfo.mHostNameBuffer = name;
596 serviceInfo.mHostNameBufferSize = sizeof(name);
597 serviceInfo.mTxtData = txtBuffer;
598 serviceInfo.mTxtDataSize = sizeof(txtBuffer);
599
600 if (otDnsBrowseResponseGetServiceInfo(aResponse, label, &serviceInfo) == OT_ERROR_NONE)
601 {
602 OutputDnsServiceInfo(kIndentSize, serviceInfo);
603 }
604
605 OutputNewLine();
606 }
607 }
608
609 OutputResult(aError);
610 }
611
HandleDnsServiceResponse(otError aError,const otDnsServiceResponse * aResponse,void * aContext)612 void Dns::HandleDnsServiceResponse(otError aError, const otDnsServiceResponse *aResponse, void *aContext)
613 {
614 static_cast<Dns *>(aContext)->HandleDnsServiceResponse(aError, aResponse);
615 }
616
HandleDnsServiceResponse(otError aError,const otDnsServiceResponse * aResponse)617 void Dns::HandleDnsServiceResponse(otError aError, const otDnsServiceResponse *aResponse)
618 {
619 char name[OT_DNS_MAX_NAME_SIZE];
620 char label[OT_DNS_MAX_LABEL_SIZE];
621 uint8_t txtBuffer[kMaxTxtDataSize];
622 otDnsServiceInfo serviceInfo;
623
624 IgnoreError(otDnsServiceResponseGetServiceName(aResponse, label, sizeof(label), name, sizeof(name)));
625
626 OutputLine("DNS service resolution response for %s for service %s", label, name);
627
628 if (aError == OT_ERROR_NONE)
629 {
630 serviceInfo.mHostNameBuffer = name;
631 serviceInfo.mHostNameBufferSize = sizeof(name);
632 serviceInfo.mTxtData = txtBuffer;
633 serviceInfo.mTxtDataSize = sizeof(txtBuffer);
634
635 if (otDnsServiceResponseGetServiceInfo(aResponse, &serviceInfo) == OT_ERROR_NONE)
636 {
637 OutputDnsServiceInfo(/* aIndentSize */ 0, serviceInfo);
638 OutputNewLine();
639 }
640 }
641
642 OutputResult(aError);
643 }
644
645 #endif // OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
646 #endif // OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE
647
648 #if OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE
649
Process(Arg aArgs[])650 template <> otError Dns::Process<Cmd("server")>(Arg aArgs[])
651 {
652 otError error = OT_ERROR_NONE;
653
654 if (aArgs[0].IsEmpty())
655 {
656 error = OT_ERROR_INVALID_ARGS;
657 }
658 #if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE
659 /**
660 * @cli dns server upstream
661 * @code
662 * dns server upstream
663 * Enabled
664 * Done
665 * @endcode
666 * @par api_copy
667 * #otDnssdUpstreamQueryIsEnabled
668 */
669 else if (aArgs[0] == "upstream")
670 {
671 /**
672 * @cli dns server upstream {enable|disable}
673 * @code
674 * dns server upstream enable
675 * Done
676 * @endcode
677 * @cparam dns server upstream @ca{enable|disable}
678 * @par api_copy
679 * #otDnssdUpstreamQuerySetEnabled
680 */
681 error = ProcessEnableDisable(aArgs + 1, otDnssdUpstreamQueryIsEnabled, otDnssdUpstreamQuerySetEnabled);
682 }
683 #endif // OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE
684 else
685 {
686 ExitNow(error = OT_ERROR_INVALID_COMMAND);
687 }
688
689 exit:
690 return error;
691 }
692
693 #endif // OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE
694
Process(Arg aArgs[])695 otError Dns::Process(Arg aArgs[])
696 {
697 #define CmdEntry(aCommandString) \
698 { \
699 aCommandString, &Dns::Process<Cmd(aCommandString)> \
700 }
701
702 static constexpr Command kCommands[] = {
703
704 #if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE && OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
705 CmdEntry("browse"),
706 #endif
707 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
708 CmdEntry("compression"),
709 #endif
710 #if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE
711 CmdEntry("config"),
712 CmdEntry("resolve"),
713 #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE
714 CmdEntry("resolve4"),
715 #endif
716 #endif // OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE
717 #if OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE
718 CmdEntry("server"),
719 #endif
720 #if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE && OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
721 CmdEntry("service"),
722 CmdEntry("servicehost"),
723 #endif
724 };
725
726 #undef CmdEntry
727
728 static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted");
729
730 otError error = OT_ERROR_INVALID_COMMAND;
731 const Command *command;
732
733 if (aArgs[0].IsEmpty() || (aArgs[0] == "help"))
734 {
735 OutputCommandTable(kCommands);
736 ExitNow(error = aArgs[0].IsEmpty() ? error : OT_ERROR_NONE);
737 }
738
739 command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
740 VerifyOrExit(command != nullptr);
741
742 error = (this->*command->mHandler)(aArgs + 1);
743
744 exit:
745 return error;
746 }
747
748 } // namespace Cli
749 } // namespace ot
750
751 #endif // OPENTHREAD_CLI_DNS_ENABLE
752