1 /*
2 * Copyright (c) 2016 Intel Corporation
3 * Copyright (c) 2023 Nordic Semiconductor ASA
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #include <stdio.h>
9 #include <strings.h>
10
11 #include <zephyr/logging/log.h>
12 LOG_MODULE_DECLARE(net_shell);
13
14 #include <zephyr/net/socket.h>
15 #include <zephyr/net/dns_resolve.h>
16 #include <zephyr/net/dns_sd.h>
17 #include "dns/dns_sd.h"
18
19 #include "net_shell_private.h"
20
21 #define DNS_TIMEOUT CONFIG_NET_SOCKETS_DNS_TIMEOUT
22
23 #if defined(CONFIG_DNS_RESOLVER)
dns_result_cb(enum dns_resolve_status status,struct dns_addrinfo * info,void * user_data)24 static void dns_result_cb(enum dns_resolve_status status,
25 struct dns_addrinfo *info,
26 void *user_data)
27 {
28 const struct shell *sh = user_data;
29
30 if (status == DNS_EAI_CANCELED) {
31 PR_WARNING("dns: Timeout while resolving name.\n");
32 return;
33 }
34
35 if (status == DNS_EAI_INPROGRESS && info) {
36 #define MAX_STR_LEN CONFIG_DNS_RESOLVER_MAX_NAME_LEN
37 char str[MAX_STR_LEN + 1];
38
39 switch (info->ai_family) {
40 case NET_AF_INET:
41 net_addr_ntop(NET_AF_INET,
42 &net_sin(&info->ai_addr)->sin_addr,
43 str, sizeof(str));
44 break;
45
46 case NET_AF_INET6:
47 net_addr_ntop(NET_AF_INET6,
48 &net_sin6(&info->ai_addr)->sin6_addr,
49 str, sizeof(str));
50 break;
51
52 case NET_AF_LOCAL:
53 /* service discovery */
54 memset(str, 0, MAX_STR_LEN);
55 memcpy(str, info->ai_canonname,
56 MIN(info->ai_addrlen, MAX_STR_LEN));
57 break;
58
59 case NET_AF_UNSPEC:
60 if (info->ai_extension == DNS_RESOLVE_TXT) {
61 memset(str, 0, MAX_STR_LEN);
62 memcpy(str, info->ai_txt.text,
63 MIN(info->ai_txt.textlen, MAX_STR_LEN));
64 break;
65 } else if (info->ai_extension == DNS_RESOLVE_SRV) {
66 snprintf(str, sizeof(str), "%d %d %d %.*s",
67 info->ai_srv.priority,
68 info->ai_srv.weight,
69 info->ai_srv.port,
70 (int)info->ai_srv.targetlen,
71 info->ai_srv.target);
72 break;
73 }
74
75 __fallthrough;
76 default:
77 strncpy(str, "Invalid proto family", MAX_STR_LEN + 1);
78 break;
79 }
80
81 str[MAX_STR_LEN] = '\0';
82
83 PR("dns: %s\n", str);
84 return;
85 }
86
87 if (status == DNS_EAI_ALLDONE) {
88 PR("dns: All results received\n");
89 return;
90 }
91
92 if (status == DNS_EAI_FAIL) {
93 PR_WARNING("dns: No such name found.\n");
94 return;
95 }
96
97 PR_WARNING("dns: Unhandled status %d received (errno %d)\n", status, errno);
98 }
99
100 K_MSGQ_DEFINE(dns_infoq, sizeof(struct dns_addrinfo), 3, 1);
101
dns_service_cb(enum dns_resolve_status status,struct dns_addrinfo * info,void * user_data)102 static void dns_service_cb(enum dns_resolve_status status,
103 struct dns_addrinfo *info,
104 void *user_data)
105 {
106 int r;
107 const struct shell *sh = user_data;
108
109 if (status == DNS_EAI_CANCELED) {
110 PR_WARNING("dns: Timeout while resolving service.\n");
111 return;
112 }
113
114 if ((status == DNS_EAI_INPROGRESS) && (info != NULL)) {
115 /*
116 * Only queue results that can be further processed.
117 */
118 r = k_msgq_put(&dns_infoq, info, K_NO_WAIT);
119 if (r < 0) {
120 PR_WARNING("dns: k_msgq_put error %d", r);
121 }
122 }
123 }
124
printable_iface(const char * iface_name,const char * found,const char * not_found)125 static const char *printable_iface(const char *iface_name,
126 const char *found,
127 const char *not_found)
128 {
129 if (iface_name[0] != '\0') {
130 return found;
131 }
132
133 return not_found;
134 }
135
print_dns_info(const struct shell * sh,struct dns_resolve_context * ctx)136 static void print_dns_info(const struct shell *sh,
137 struct dns_resolve_context *ctx)
138 {
139 int i, ret;
140
141 PR("DNS servers:\n");
142
143 for (i = 0; i < CONFIG_DNS_RESOLVER_MAX_SERVERS +
144 DNS_MAX_MCAST_SERVERS; i++) {
145 char iface_name[NET_IFNAMSIZ] = { 0 };
146
147 if (ctx->servers[i].if_index > 0) {
148 ret = net_if_get_name(
149 net_if_get_by_index(ctx->servers[i].if_index),
150 iface_name, sizeof(iface_name));
151 if (ret < 0) {
152 snprintk(iface_name, sizeof(iface_name), "%d",
153 ctx->servers[i].if_index);
154 }
155 }
156
157 if (ctx->servers[i].dns_server.sa_family == NET_AF_INET) {
158 PR("\t%s:%u%s%s%s%s%s\n",
159 net_sprint_ipv4_addr(
160 &net_sin(&ctx->servers[i].dns_server)->
161 sin_addr),
162 net_ntohs(net_sin(&ctx->servers[i].dns_server)->sin_port),
163 printable_iface(iface_name, " via ", ""),
164 printable_iface(iface_name, iface_name, ""),
165 ctx->servers[i].source != DNS_SOURCE_UNKNOWN ? " (" : "",
166 ctx->servers[i].source != DNS_SOURCE_UNKNOWN ?
167 dns_get_source_str(ctx->servers[i].source) : "",
168 ctx->servers[i].source != DNS_SOURCE_UNKNOWN ? ")" : "");
169
170 } else if (ctx->servers[i].dns_server.sa_family == NET_AF_INET6) {
171 PR("\t[%s]:%u%s%s%s%s%s\n",
172 net_sprint_ipv6_addr(
173 &net_sin6(&ctx->servers[i].dns_server)->
174 sin6_addr),
175 net_ntohs(net_sin6(&ctx->servers[i].dns_server)->sin6_port),
176 printable_iface(iface_name, " via ", ""),
177 printable_iface(iface_name, iface_name, ""),
178 ctx->servers[i].source != DNS_SOURCE_UNKNOWN ? " (" : "",
179 ctx->servers[i].source != DNS_SOURCE_UNKNOWN ?
180 dns_get_source_str(ctx->servers[i].source) : "",
181 ctx->servers[i].source != DNS_SOURCE_UNKNOWN ? ")" : "");
182 }
183 }
184
185 PR("Pending queries:\n");
186
187 for (i = 0; i < CONFIG_DNS_NUM_CONCUR_QUERIES; i++) {
188 int32_t remaining;
189
190 if (!ctx->queries[i].cb || !ctx->queries[i].query) {
191 continue;
192 }
193
194 remaining = k_ticks_to_ms_ceil32(
195 k_work_delayable_remaining_get(&ctx->queries[i].timer));
196
197 if (ctx->queries[i].query_type == DNS_QUERY_TYPE_A) {
198 PR("\t%s[%u]: %s remaining %d\n", "IPv4",
199 ctx->queries[i].id,
200 ctx->queries[i].query,
201 remaining);
202 } else if (ctx->queries[i].query_type == DNS_QUERY_TYPE_AAAA) {
203 PR("\t%s[%u]: %s remaining %d\n", "IPv6",
204 ctx->queries[i].id,
205 ctx->queries[i].query,
206 remaining);
207 } else if (ctx->queries[i].query_type == DNS_QUERY_TYPE_PTR) {
208 PR("\t%s[%u]: %s remaining %d\n", "PTR",
209 ctx->queries[i].id,
210 ctx->queries[i].query,
211 remaining);
212 } else {
213 PR_WARNING("\tUnknown query type %d for query %s[%u] "
214 "remaining %d\n",
215 ctx->queries[i].query_type,
216 ctx->queries[i].query, ctx->queries[i].id,
217 remaining);
218 }
219 }
220 }
221 #endif
222
cmd_net_dns_cancel(const struct shell * sh,size_t argc,char * argv[])223 static int cmd_net_dns_cancel(const struct shell *sh, size_t argc, char *argv[])
224 {
225 #if defined(CONFIG_DNS_RESOLVER)
226 struct dns_resolve_context *ctx;
227 int ret, i;
228 #endif
229
230 ARG_UNUSED(argc);
231 ARG_UNUSED(argv);
232
233 #if defined(CONFIG_DNS_RESOLVER)
234 ctx = dns_resolve_get_default();
235 if (!ctx) {
236 PR_WARNING("No default DNS context found.\n");
237 return -ENOEXEC;
238 }
239
240 for (ret = 0, i = 0; i < CONFIG_DNS_NUM_CONCUR_QUERIES; i++) {
241 if (!ctx->queries[i].cb) {
242 continue;
243 }
244
245 if (!dns_resolve_cancel(ctx, ctx->queries[i].id)) {
246 ret++;
247 }
248 }
249
250 if (ret) {
251 PR("Cancelled %d pending requests.\n", ret);
252 } else {
253 PR("No pending DNS requests.\n");
254 }
255 #else
256 PR_INFO("Set %s to enable %s support.\n", "CONFIG_DNS_RESOLVER",
257 "DNS resolver");
258 #endif
259
260 return 0;
261 }
262
cmd_net_dns_query(const struct shell * sh,size_t argc,char * argv[])263 static int cmd_net_dns_query(const struct shell *sh, size_t argc, char *argv[])
264 {
265
266 #if defined(CONFIG_DNS_RESOLVER)
267 struct dns_resolve_context *ctx;
268 enum dns_query_type qtype = DNS_QUERY_TYPE_A;
269 char *host, *type = NULL;
270 int ret, arg = 1;
271
272 host = argv[arg++];
273 if (!host) {
274 PR_WARNING("Hostname not specified.\n");
275 return -ENOEXEC;
276 }
277
278 if (argv[arg]) {
279 type = argv[arg];
280 }
281
282 if (type) {
283 if (strcasecmp(type, "A") == 0) {
284 qtype = DNS_QUERY_TYPE_A;
285 PR("IPv4 address query type\n");
286 } else if (strcasecmp(type, "CNAME") == 0) {
287 qtype = DNS_QUERY_TYPE_CNAME;
288 PR("CNAME query type\n");
289 } else if (strcasecmp(type, "PTR") == 0) {
290 qtype = DNS_QUERY_TYPE_PTR;
291 PR("Pointer query type\n");
292 } else if (strcasecmp(type, "TXT") == 0) {
293 qtype = DNS_QUERY_TYPE_TXT;
294 PR("Text query type\n");
295 } else if (strcasecmp(type, "AAAA") == 0) {
296 qtype = DNS_QUERY_TYPE_AAAA;
297 PR("IPv6 address query type\n");
298 } else if (strcasecmp(type, "SRV") == 0) {
299 qtype = DNS_QUERY_TYPE_SRV;
300 PR("Service query type\n");
301 } else {
302 PR_WARNING("Unknown query type, specify either "
303 "A, CNAME, PTR, TXT, AAAA, or SRV\n");
304 return -ENOEXEC;
305 }
306 }
307
308 ctx = dns_resolve_get_default();
309 if (!ctx) {
310 PR_WARNING("No default DNS context found.\n");
311 return -ENOEXEC;
312 }
313
314 ret = dns_resolve_name(ctx, host, qtype, NULL, dns_result_cb,
315 (void *)sh, DNS_TIMEOUT);
316 if (ret < 0) {
317 PR_WARNING("Cannot resolve '%s' (%d)\n", host, ret);
318 } else {
319 PR("Query for '%s' sent.\n", host);
320 }
321 #else
322 PR_INFO("DNS resolver not supported. Set CONFIG_DNS_RESOLVER to "
323 "enable it.\n");
324 #endif
325
326 return 0;
327 }
328
cmd_net_dns(const struct shell * sh,size_t argc,char * argv[])329 static int cmd_net_dns(const struct shell *sh, size_t argc, char *argv[])
330 {
331 #if defined(CONFIG_DNS_RESOLVER)
332 struct dns_resolve_context *ctx;
333 #endif
334
335 #if defined(CONFIG_DNS_RESOLVER)
336 if (argv[1]) {
337 /* So this is a query then */
338 cmd_net_dns_query(sh, argc, argv);
339 return 0;
340 }
341
342 /* DNS status */
343 ctx = dns_resolve_get_default();
344 if (!ctx) {
345 PR_WARNING("No default DNS context found.\n");
346 return -ENOEXEC;
347 }
348
349 print_dns_info(sh, ctx);
350 #else
351 PR_INFO("DNS resolver not supported. Set CONFIG_DNS_RESOLVER to "
352 "enable it.\n");
353 #endif
354
355 return 0;
356 }
357
cmd_net_dns_list(const struct shell * sh,size_t argc,char * argv[])358 static int cmd_net_dns_list(const struct shell *sh, size_t argc, char *argv[])
359 {
360 #if defined(CONFIG_DNS_SD)
361 #define MAX_PORT_LEN 6
362 char buf[MAX_PORT_LEN];
363 int n_records = 0;
364
365 DNS_SD_FOREACH(record) {
366 if (!dns_sd_rec_is_valid(record)) {
367 continue;
368 }
369
370 if (n_records == 0) {
371 PR(" DNS service records\n");
372 }
373
374 ++n_records;
375
376 if (record->port != NULL) {
377 snprintk(buf, sizeof(buf), "%u", net_ntohs(*record->port));
378 }
379
380 PR("[%2d] %s.%s%s%s%s%s%s%s\n",
381 n_records,
382 record->instance != NULL ? record->instance : "",
383 record->service != NULL ? record->service : "",
384 record->proto != NULL ? "." : "",
385 record->proto != NULL ? record->proto : "",
386 record->domain != NULL ? "." : "",
387 record->domain != NULL ? record->domain : "",
388 record->port != NULL ? ":" : "",
389 record->port != NULL ? buf : "");
390 }
391
392 if (n_records == 0) {
393 PR("No DNS service records found.\n");
394 return 0;
395 }
396 #else
397 PR_INFO("DNS service discovery not supported. Set CONFIG_DNS_SD to "
398 "enable it.\n");
399 #endif
400
401 return 0;
402 }
403
cmd_net_dns_service(const struct shell * sh,size_t argc,char * argv[])404 static int cmd_net_dns_service(const struct shell *sh, size_t argc, char *argv[])
405 {
406 #if defined(CONFIG_DNS_RESOLVER)
407 struct dns_resolve_context *ctx;
408 char *cp;
409 char *service;
410 uint16_t port;
411 uint16_t dns_id;
412 int ret, arg = 1;
413
414 service = argv[arg++];
415 if (service == NULL) {
416 PR_WARNING("Service not specified.\n");
417 return -ENOEXEC;
418 }
419
420 /* remove any lingering info data */
421 k_msgq_purge(&dns_infoq);
422
423 ctx = dns_resolve_get_default();
424 if (ctx == NULL) {
425 PR_WARNING("No default DNS context found.\n");
426 return -ENOEXEC;
427 }
428
429 ret = dns_resolve_service(ctx, service, &dns_id, dns_service_cb,
430 (void *)sh, DNS_TIMEOUT);
431 if (ret < 0) {
432 PR_WARNING("Cannot resolve '%s' (%d)\n", service, ret);
433 return ret;
434 }
435
436 PR("Resolve for '%s' service sent.\n", service);
437 port = 0;
438 for (;;) {
439 struct dns_addrinfo info;
440 enum dns_query_type qtype;
441 char query[DNS_MAX_NAME_SIZE + 1];
442 union {
443 char in4[NET_INET_ADDRSTRLEN];
444 char in6[NET_INET6_ADDRSTRLEN];
445 } str;
446
447 ret = k_msgq_get(&dns_infoq, &info, K_MSEC(DNS_TIMEOUT));
448 if (ret < 0) {
449 /* just assume a timeout so no more data to process */
450 break;
451 }
452
453 switch (info.ai_family) {
454 case NET_AF_INET:
455 cp = net_addr_ntop(NET_AF_INET,
456 &net_sin(&info.ai_addr)->sin_addr,
457 str.in4, sizeof(str.in4));
458 PR("AF_INET %s:%u\n", cp ? cp : "<invalid>", port);
459 break;
460
461 case NET_AF_INET6:
462 cp = net_addr_ntop(NET_AF_INET6,
463 &net_sin6(&info.ai_addr)->sin6_addr,
464 str.in6, sizeof(str.in6));
465 PR("AF_INET6 [%s]:%u\n", cp ? cp : "<invalid>", port);
466 break;
467
468 case NET_AF_LOCAL:
469 PR("AF_LOCAL %.*s\n",
470 (int)info.ai_addrlen, info.ai_canonname);
471
472 snprintf(query, sizeof(query), "%.*s",
473 info.ai_addrlen, info.ai_canonname);
474
475 qtype = DNS_QUERY_TYPE_SRV;
476 ret = dns_resolve_name(ctx, query, qtype,
477 &dns_id,
478 dns_service_cb, (void *)sh,
479 DNS_TIMEOUT);
480 if (ret < 0) {
481 return ret;
482 }
483 break;
484
485 case NET_AF_UNSPEC:
486 if (info.ai_extension == DNS_RESOLVE_SRV) {
487 PR("SRV %d %d %d %.*s\n",
488 info.ai_srv.priority,
489 info.ai_srv.weight,
490 info.ai_srv.port,
491 (int)info.ai_srv.targetlen,
492 info.ai_srv.target);
493
494 port = info.ai_srv.port;
495
496 snprintf(query, sizeof(query), "%.*s",
497 (int)info.ai_srv.targetlen,
498 info.ai_srv.target);
499
500 /*
501 * Sending a query for both AAAA and A records
502 * should be ok, but the resolver doesn't
503 * gracefully handle the query for different
504 * types.
505 */
506 qtype = DNS_QUERY_TYPE_AAAA;
507 ret = dns_resolve_name(ctx, query, qtype,
508 &dns_id,
509 dns_service_cb,
510 (void *)sh,
511 DNS_TIMEOUT);
512 if (ret < 0) {
513 return ret;
514 }
515 break;
516 }
517
518 __fallthrough;
519 default:
520 PR_WARNING("dns: unhandled info %u on msgq\n", info.ai_family);
521 break;
522 }
523 }
524 #else
525 PR_INFO("DNS resolver not supported. Set CONFIG_DNS_RESOLVER to "
526 "enable it.\n");
527 #endif
528
529 return 0;
530 }
531
532 SHELL_STATIC_SUBCMD_SET_CREATE(net_cmd_dns,
533 SHELL_CMD(cancel, NULL, "Cancel all pending requests.",
534 cmd_net_dns_cancel),
535 SHELL_CMD(query, NULL,
536 "'net dns <hostname> [A or AAAA]' queries IPv4 address "
537 "(default) or IPv6 address for a host name.",
538 cmd_net_dns_query),
539 SHELL_CMD(list, NULL,
540 "List local DNS service records.",
541 cmd_net_dns_list),
542 SHELL_CMD(service, NULL,
543 "'net dns service <service-description>\n"
544 "Execute DNS service discovery query.",
545 cmd_net_dns_service),
546 SHELL_SUBCMD_SET_END
547 );
548
549 SHELL_SUBCMD_ADD((net), dns, &net_cmd_dns,
550 "Show how DNS is configured. Optionally do a query using a given name.",
551 cmd_net_dns, 1, 2);
552