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