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 <zephyr/logging/log.h>
9 LOG_MODULE_DECLARE(net_shell);
10
11 #include <zephyr/net/socket.h>
12 #include <zephyr/net/dns_resolve.h>
13 #include <zephyr/net/dns_sd.h>
14 #include "dns/dns_sd.h"
15
16 #include "net_shell_private.h"
17
18 #if defined(CONFIG_DNS_RESOLVER)
dns_result_cb(enum dns_resolve_status status,struct dns_addrinfo * info,void * user_data)19 static void dns_result_cb(enum dns_resolve_status status,
20 struct dns_addrinfo *info,
21 void *user_data)
22 {
23 const struct shell *sh = user_data;
24
25 if (status == DNS_EAI_CANCELED) {
26 PR_WARNING("dns: Timeout while resolving name.\n");
27 return;
28 }
29
30 if (status == DNS_EAI_INPROGRESS && info) {
31 #define MAX_STR_LEN CONFIG_DNS_RESOLVER_MAX_NAME_LEN
32 char str[MAX_STR_LEN + 1];
33
34 if (info->ai_family == AF_INET) {
35 net_addr_ntop(AF_INET,
36 &net_sin(&info->ai_addr)->sin_addr,
37 str, NET_IPV4_ADDR_LEN);
38 } else if (info->ai_family == AF_INET6) {
39 net_addr_ntop(AF_INET6,
40 &net_sin6(&info->ai_addr)->sin6_addr,
41 str, NET_IPV6_ADDR_LEN);
42 } else if (info->ai_family == AF_LOCAL) {
43 /* service discovery */
44 memset(str, 0, MAX_STR_LEN);
45 memcpy(str, info->ai_canonname,
46 MIN(info->ai_addrlen, MAX_STR_LEN));
47 } else {
48 strncpy(str, "Invalid proto family", MAX_STR_LEN + 1);
49 }
50
51 str[MAX_STR_LEN] = '\0';
52
53 PR("dns: %s\n", str);
54 return;
55 }
56
57 if (status == DNS_EAI_ALLDONE) {
58 PR("dns: All results received\n");
59 return;
60 }
61
62 if (status == DNS_EAI_FAIL) {
63 PR_WARNING("dns: No such name found.\n");
64 return;
65 }
66
67 PR_WARNING("dns: Unhandled status %d received (errno %d)\n", status, errno);
68 }
69
printable_iface(const char * iface_name,const char * found,const char * not_found)70 static const char *printable_iface(const char *iface_name,
71 const char *found,
72 const char *not_found)
73 {
74 if (iface_name[0] != '\0') {
75 return found;
76 }
77
78 return not_found;
79 }
80
print_dns_info(const struct shell * sh,struct dns_resolve_context * ctx)81 static void print_dns_info(const struct shell *sh,
82 struct dns_resolve_context *ctx)
83 {
84 int i, ret;
85
86 PR("DNS servers:\n");
87
88 for (i = 0; i < CONFIG_DNS_RESOLVER_MAX_SERVERS +
89 DNS_MAX_MCAST_SERVERS; i++) {
90 char iface_name[IFNAMSIZ] = { 0 };
91
92 if (ctx->servers[i].if_index > 0) {
93 ret = net_if_get_name(
94 net_if_get_by_index(ctx->servers[i].if_index),
95 iface_name, sizeof(iface_name));
96 if (ret < 0) {
97 snprintk(iface_name, sizeof(iface_name), "%d",
98 ctx->servers[i].if_index);
99 }
100 }
101
102 if (ctx->servers[i].dns_server.sa_family == AF_INET) {
103 PR("\t%s:%u%s%s%s%s%s\n",
104 net_sprint_ipv4_addr(
105 &net_sin(&ctx->servers[i].dns_server)->
106 sin_addr),
107 ntohs(net_sin(&ctx->servers[i].dns_server)->sin_port),
108 printable_iface(iface_name, " via ", ""),
109 printable_iface(iface_name, iface_name, ""),
110 ctx->servers[i].source != DNS_SOURCE_UNKNOWN ? " (" : "",
111 ctx->servers[i].source != DNS_SOURCE_UNKNOWN ?
112 dns_get_source_str(ctx->servers[i].source) : "",
113 ctx->servers[i].source != DNS_SOURCE_UNKNOWN ? ")" : "");
114
115 } else if (ctx->servers[i].dns_server.sa_family == AF_INET6) {
116 PR("\t[%s]:%u%s%s%s%s%s\n",
117 net_sprint_ipv6_addr(
118 &net_sin6(&ctx->servers[i].dns_server)->
119 sin6_addr),
120 ntohs(net_sin6(&ctx->servers[i].dns_server)->sin6_port),
121 printable_iface(iface_name, " via ", ""),
122 printable_iface(iface_name, iface_name, ""),
123 ctx->servers[i].source != DNS_SOURCE_UNKNOWN ? " (" : "",
124 ctx->servers[i].source != DNS_SOURCE_UNKNOWN ?
125 dns_get_source_str(ctx->servers[i].source) : "",
126 ctx->servers[i].source != DNS_SOURCE_UNKNOWN ? ")" : "");
127 }
128 }
129
130 PR("Pending queries:\n");
131
132 for (i = 0; i < CONFIG_DNS_NUM_CONCUR_QUERIES; i++) {
133 int32_t remaining;
134
135 if (!ctx->queries[i].cb || !ctx->queries[i].query) {
136 continue;
137 }
138
139 remaining = k_ticks_to_ms_ceil32(
140 k_work_delayable_remaining_get(&ctx->queries[i].timer));
141
142 if (ctx->queries[i].query_type == DNS_QUERY_TYPE_A) {
143 PR("\t%s[%u]: %s remaining %d\n", "IPv4",
144 ctx->queries[i].id,
145 ctx->queries[i].query,
146 remaining);
147 } else if (ctx->queries[i].query_type == DNS_QUERY_TYPE_AAAA) {
148 PR("\t%s[%u]: %s remaining %d\n", "IPv6",
149 ctx->queries[i].id,
150 ctx->queries[i].query,
151 remaining);
152 } else if (ctx->queries[i].query_type == DNS_QUERY_TYPE_PTR) {
153 PR("\t%s[%u]: %s remaining %d\n", "PTR",
154 ctx->queries[i].id,
155 ctx->queries[i].query,
156 remaining);
157 } else {
158 PR_WARNING("\tUnknown query type %d for query %s[%u] "
159 "remaining %d\n",
160 ctx->queries[i].query_type,
161 ctx->queries[i].query, ctx->queries[i].id,
162 remaining);
163 }
164 }
165 }
166 #endif
167
cmd_net_dns_cancel(const struct shell * sh,size_t argc,char * argv[])168 static int cmd_net_dns_cancel(const struct shell *sh, size_t argc, char *argv[])
169 {
170 #if defined(CONFIG_DNS_RESOLVER)
171 struct dns_resolve_context *ctx;
172 int ret, i;
173 #endif
174
175 ARG_UNUSED(argc);
176 ARG_UNUSED(argv);
177
178 #if defined(CONFIG_DNS_RESOLVER)
179 ctx = dns_resolve_get_default();
180 if (!ctx) {
181 PR_WARNING("No default DNS context found.\n");
182 return -ENOEXEC;
183 }
184
185 for (ret = 0, i = 0; i < CONFIG_DNS_NUM_CONCUR_QUERIES; i++) {
186 if (!ctx->queries[i].cb) {
187 continue;
188 }
189
190 if (!dns_resolve_cancel(ctx, ctx->queries[i].id)) {
191 ret++;
192 }
193 }
194
195 if (ret) {
196 PR("Cancelled %d pending requests.\n", ret);
197 } else {
198 PR("No pending DNS requests.\n");
199 }
200 #else
201 PR_INFO("Set %s to enable %s support.\n", "CONFIG_DNS_RESOLVER",
202 "DNS resolver");
203 #endif
204
205 return 0;
206 }
207
cmd_net_dns_query(const struct shell * sh,size_t argc,char * argv[])208 static int cmd_net_dns_query(const struct shell *sh, size_t argc, char *argv[])
209 {
210
211 #if defined(CONFIG_DNS_RESOLVER)
212 #define DNS_TIMEOUT (MSEC_PER_SEC * 2) /* ms */
213 enum dns_query_type qtype = DNS_QUERY_TYPE_A;
214 char *host, *type = NULL;
215 int ret, arg = 1;
216
217 host = argv[arg++];
218 if (!host) {
219 PR_WARNING("Hostname not specified.\n");
220 return -ENOEXEC;
221 }
222
223 if (argv[arg]) {
224 type = argv[arg];
225 }
226
227 if (type) {
228 if (strcmp(type, "A") == 0) {
229 qtype = DNS_QUERY_TYPE_A;
230 PR("IPv4 address type\n");
231 } else if (strcmp(type, "AAAA") == 0) {
232 qtype = DNS_QUERY_TYPE_AAAA;
233 PR("IPv6 address type\n");
234 } else {
235 PR_WARNING("Unknown query type, specify either "
236 "A or AAAA\n");
237 return -ENOEXEC;
238 }
239 }
240
241 ret = dns_get_addr_info(host, qtype, NULL, dns_result_cb,
242 (void *)sh, DNS_TIMEOUT);
243 if (ret < 0) {
244 PR_WARNING("Cannot resolve '%s' (%d)\n", host, ret);
245 } else {
246 PR("Query for '%s' sent.\n", host);
247 }
248 #else
249 PR_INFO("DNS resolver not supported. Set CONFIG_DNS_RESOLVER to "
250 "enable it.\n");
251 #endif
252
253 return 0;
254 }
255
cmd_net_dns(const struct shell * sh,size_t argc,char * argv[])256 static int cmd_net_dns(const struct shell *sh, size_t argc, char *argv[])
257 {
258 #if defined(CONFIG_DNS_RESOLVER)
259 struct dns_resolve_context *ctx;
260 #endif
261
262 #if defined(CONFIG_DNS_RESOLVER)
263 if (argv[1]) {
264 /* So this is a query then */
265 cmd_net_dns_query(sh, argc, argv);
266 return 0;
267 }
268
269 /* DNS status */
270 ctx = dns_resolve_get_default();
271 if (!ctx) {
272 PR_WARNING("No default DNS context found.\n");
273 return -ENOEXEC;
274 }
275
276 print_dns_info(sh, ctx);
277 #else
278 PR_INFO("DNS resolver not supported. Set CONFIG_DNS_RESOLVER to "
279 "enable it.\n");
280 #endif
281
282 return 0;
283 }
284
cmd_net_dns_list(const struct shell * sh,size_t argc,char * argv[])285 static int cmd_net_dns_list(const struct shell *sh, size_t argc, char *argv[])
286 {
287 #if defined(CONFIG_DNS_SD)
288 #define MAX_PORT_LEN 6
289 char buf[MAX_PORT_LEN];
290 int n_records = 0;
291
292 DNS_SD_FOREACH(record) {
293 if (!dns_sd_rec_is_valid(record)) {
294 continue;
295 }
296
297 if (n_records == 0) {
298 PR(" DNS service records\n");
299 }
300
301 ++n_records;
302
303 if (record->port != NULL) {
304 snprintk(buf, sizeof(buf), "%u", ntohs(*record->port));
305 }
306
307 PR("[%2d] %s.%s%s%s%s%s%s%s\n",
308 n_records,
309 record->instance != NULL ? record->instance : "",
310 record->service != NULL ? record->service : "",
311 record->proto != NULL ? "." : "",
312 record->proto != NULL ? record->proto : "",
313 record->domain != NULL ? "." : "",
314 record->domain != NULL ? record->domain : "",
315 record->port != NULL ? ":" : "",
316 record->port != NULL ? buf : "");
317 }
318
319 if (n_records == 0) {
320 PR("No DNS service records found.\n");
321 return 0;
322 }
323 #else
324 PR_INFO("DNS service discovery not supported. Set CONFIG_DNS_SD to "
325 "enable it.\n");
326 #endif
327
328 return 0;
329 }
330
cmd_net_dns_service(const struct shell * sh,size_t argc,char * argv[])331 static int cmd_net_dns_service(const struct shell *sh, size_t argc, char *argv[])
332 {
333 #if defined(CONFIG_DNS_RESOLVER)
334 #define DNS_TIMEOUT (MSEC_PER_SEC * 2) /* ms */
335 struct dns_resolve_context *ctx;
336 char *service;
337 uint16_t dns_id;
338 int ret, arg = 1;
339
340 service = argv[arg++];
341 if (service == NULL) {
342 PR_WARNING("Service not specified.\n");
343 return -ENOEXEC;
344 }
345
346 ctx = dns_resolve_get_default();
347 if (ctx == NULL) {
348 PR_WARNING("No default DNS context found.\n");
349 return -ENOEXEC;
350 }
351
352 ret = dns_resolve_service(ctx, service, &dns_id, dns_result_cb,
353 (void *)sh, DNS_TIMEOUT);
354 if (ret < 0) {
355 PR_WARNING("Cannot resolve '%s' (%d)\n", service, ret);
356 } else {
357 PR("Query for '%s' sent.\n", service);
358 }
359 #else
360 PR_INFO("DNS resolver not supported. Set CONFIG_DNS_RESOLVER to "
361 "enable it.\n");
362 #endif
363
364 return 0;
365 }
366
367 SHELL_STATIC_SUBCMD_SET_CREATE(net_cmd_dns,
368 SHELL_CMD(cancel, NULL, "Cancel all pending requests.",
369 cmd_net_dns_cancel),
370 SHELL_CMD(query, NULL,
371 "'net dns <hostname> [A or AAAA]' queries IPv4 address "
372 "(default) or IPv6 address for a host name.",
373 cmd_net_dns_query),
374 SHELL_CMD(list, NULL,
375 "List local DNS service records.",
376 cmd_net_dns_list),
377 SHELL_CMD(service, NULL,
378 "'net dns service <service-description>\n"
379 "Execute DNS service discovery query.",
380 cmd_net_dns_service),
381 SHELL_SUBCMD_SET_END
382 );
383
384 SHELL_SUBCMD_ADD((net), dns, &net_cmd_dns,
385 "Show how DNS is configured. Optionally do a query using a given name.",
386 cmd_net_dns, 1, 2);
387