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 
14 #include "net_shell_private.h"
15 
16 #if defined(CONFIG_DNS_RESOLVER)
dns_result_cb(enum dns_resolve_status status,struct dns_addrinfo * info,void * user_data)17 static void dns_result_cb(enum dns_resolve_status status,
18 			  struct dns_addrinfo *info,
19 			  void *user_data)
20 {
21 	const struct shell *sh = user_data;
22 
23 	if (status == DNS_EAI_CANCELED) {
24 		PR_WARNING("dns: Timeout while resolving name.\n");
25 		return;
26 	}
27 
28 	if (status == DNS_EAI_INPROGRESS && info) {
29 		char addr[NET_IPV6_ADDR_LEN];
30 
31 		if (info->ai_family == AF_INET) {
32 			net_addr_ntop(AF_INET,
33 				      &net_sin(&info->ai_addr)->sin_addr,
34 				      addr, NET_IPV4_ADDR_LEN);
35 		} else if (info->ai_family == AF_INET6) {
36 			net_addr_ntop(AF_INET6,
37 				      &net_sin6(&info->ai_addr)->sin6_addr,
38 				      addr, NET_IPV6_ADDR_LEN);
39 		} else {
40 			strncpy(addr, "Invalid protocol family",
41 				sizeof(addr));
42 			/* strncpy() doesn't guarantee NUL byte at the end. */
43 			addr[sizeof(addr) - 1] = 0;
44 		}
45 
46 		PR("dns: %s\n", addr);
47 		return;
48 	}
49 
50 	if (status == DNS_EAI_ALLDONE) {
51 		PR("dns: All results received\n");
52 		return;
53 	}
54 
55 	if (status == DNS_EAI_FAIL) {
56 		PR_WARNING("dns: No such name found.\n");
57 		return;
58 	}
59 
60 	PR_WARNING("dns: Unhandled status %d received\n", status);
61 }
62 
printable_iface(const char * iface_name,const char * found,const char * not_found)63 static const char *printable_iface(const char *iface_name,
64 				   const char *found,
65 				   const char *not_found)
66 {
67 	if (iface_name[0] != '\0') {
68 		return found;
69 	}
70 
71 	return not_found;
72 }
73 
print_dns_info(const struct shell * sh,struct dns_resolve_context * ctx)74 static void print_dns_info(const struct shell *sh,
75 			   struct dns_resolve_context *ctx)
76 {
77 	int i, ret;
78 
79 	PR("DNS servers:\n");
80 
81 	for (i = 0; i < CONFIG_DNS_RESOLVER_MAX_SERVERS +
82 		     DNS_MAX_MCAST_SERVERS; i++) {
83 		char iface_name[IFNAMSIZ] = { 0 };
84 
85 		if (ctx->servers[i].if_index > 0) {
86 			ret = net_if_get_name(
87 				net_if_get_by_index(ctx->servers[i].if_index),
88 				iface_name, sizeof(iface_name));
89 			if (ret < 0) {
90 				snprintk(iface_name, sizeof(iface_name), "%d",
91 					 ctx->servers[i].if_index);
92 			}
93 		}
94 
95 		if (ctx->servers[i].dns_server.sa_family == AF_INET) {
96 			PR("\t%s:%u%s%s\n",
97 			   net_sprint_ipv4_addr(
98 				   &net_sin(&ctx->servers[i].dns_server)->
99 				   sin_addr),
100 			   ntohs(net_sin(&ctx->servers[i].dns_server)->sin_port),
101 			   printable_iface(iface_name, " via ", ""),
102 			   printable_iface(iface_name, iface_name, ""));
103 
104 		} else if (ctx->servers[i].dns_server.sa_family == AF_INET6) {
105 			PR("\t[%s]:%u%s%s\n",
106 			   net_sprint_ipv6_addr(
107 				   &net_sin6(&ctx->servers[i].dns_server)->
108 				   sin6_addr),
109 			   ntohs(net_sin6(&ctx->servers[i].dns_server)->sin6_port),
110 			   printable_iface(iface_name, " via ", ""),
111 			   printable_iface(iface_name, iface_name, ""));
112 		}
113 	}
114 
115 	PR("Pending queries:\n");
116 
117 	for (i = 0; i < CONFIG_DNS_NUM_CONCUR_QUERIES; i++) {
118 		int32_t remaining;
119 
120 		if (!ctx->queries[i].cb || !ctx->queries[i].query) {
121 			continue;
122 		}
123 
124 		remaining = k_ticks_to_ms_ceil32(
125 			k_work_delayable_remaining_get(&ctx->queries[i].timer));
126 
127 		if (ctx->queries[i].query_type == DNS_QUERY_TYPE_A) {
128 			PR("\tIPv4[%u]: %s remaining %d\n",
129 			   ctx->queries[i].id,
130 			   ctx->queries[i].query,
131 			   remaining);
132 		} else if (ctx->queries[i].query_type == DNS_QUERY_TYPE_AAAA) {
133 			PR("\tIPv6[%u]: %s remaining %d\n",
134 			   ctx->queries[i].id,
135 			   ctx->queries[i].query,
136 			   remaining);
137 		}
138 	}
139 }
140 #endif
141 
cmd_net_dns_cancel(const struct shell * sh,size_t argc,char * argv[])142 static int cmd_net_dns_cancel(const struct shell *sh, size_t argc, char *argv[])
143 {
144 #if defined(CONFIG_DNS_RESOLVER)
145 	struct dns_resolve_context *ctx;
146 	int ret, i;
147 #endif
148 
149 	ARG_UNUSED(argc);
150 	ARG_UNUSED(argv);
151 
152 #if defined(CONFIG_DNS_RESOLVER)
153 	ctx = dns_resolve_get_default();
154 	if (!ctx) {
155 		PR_WARNING("No default DNS context found.\n");
156 		return -ENOEXEC;
157 	}
158 
159 	for (ret = 0, i = 0; i < CONFIG_DNS_NUM_CONCUR_QUERIES; i++) {
160 		if (!ctx->queries[i].cb) {
161 			continue;
162 		}
163 
164 		if (!dns_resolve_cancel(ctx, ctx->queries[i].id)) {
165 			ret++;
166 		}
167 	}
168 
169 	if (ret) {
170 		PR("Cancelled %d pending requests.\n", ret);
171 	} else {
172 		PR("No pending DNS requests.\n");
173 	}
174 #else
175 	PR_INFO("Set %s to enable %s support.\n", "CONFIG_DNS_RESOLVER",
176 		"DNS resolver");
177 #endif
178 
179 	return 0;
180 }
181 
cmd_net_dns_query(const struct shell * sh,size_t argc,char * argv[])182 static int cmd_net_dns_query(const struct shell *sh, size_t argc, char *argv[])
183 {
184 
185 #if defined(CONFIG_DNS_RESOLVER)
186 #define DNS_TIMEOUT (MSEC_PER_SEC * 2) /* ms */
187 	enum dns_query_type qtype = DNS_QUERY_TYPE_A;
188 	char *host, *type = NULL;
189 	int ret, arg = 1;
190 
191 	host = argv[arg++];
192 	if (!host) {
193 		PR_WARNING("Hostname not specified.\n");
194 		return -ENOEXEC;
195 	}
196 
197 	if (argv[arg]) {
198 		type = argv[arg];
199 	}
200 
201 	if (type) {
202 		if (strcmp(type, "A") == 0) {
203 			qtype = DNS_QUERY_TYPE_A;
204 			PR("IPv4 address type\n");
205 		} else if (strcmp(type, "AAAA") == 0) {
206 			qtype = DNS_QUERY_TYPE_AAAA;
207 			PR("IPv6 address type\n");
208 		} else {
209 			PR_WARNING("Unknown query type, specify either "
210 				   "A or AAAA\n");
211 			return -ENOEXEC;
212 		}
213 	}
214 
215 	ret = dns_get_addr_info(host, qtype, NULL, dns_result_cb,
216 				(void *)sh, DNS_TIMEOUT);
217 	if (ret < 0) {
218 		PR_WARNING("Cannot resolve '%s' (%d)\n", host, ret);
219 	} else {
220 		PR("Query for '%s' sent.\n", host);
221 	}
222 #else
223 	PR_INFO("DNS resolver not supported. Set CONFIG_DNS_RESOLVER to "
224 		"enable it.\n");
225 #endif
226 
227 	return 0;
228 }
229 
cmd_net_dns(const struct shell * sh,size_t argc,char * argv[])230 static int cmd_net_dns(const struct shell *sh, size_t argc, char *argv[])
231 {
232 #if defined(CONFIG_DNS_RESOLVER)
233 	struct dns_resolve_context *ctx;
234 #endif
235 
236 #if defined(CONFIG_DNS_RESOLVER)
237 	if (argv[1]) {
238 		/* So this is a query then */
239 		cmd_net_dns_query(sh, argc, argv);
240 		return 0;
241 	}
242 
243 	/* DNS status */
244 	ctx = dns_resolve_get_default();
245 	if (!ctx) {
246 		PR_WARNING("No default DNS context found.\n");
247 		return -ENOEXEC;
248 	}
249 
250 	print_dns_info(sh, ctx);
251 #else
252 	PR_INFO("DNS resolver not supported. Set CONFIG_DNS_RESOLVER to "
253 		"enable it.\n");
254 #endif
255 
256 	return 0;
257 }
258 
259 SHELL_STATIC_SUBCMD_SET_CREATE(net_cmd_dns,
260 	SHELL_CMD(cancel, NULL, "Cancel all pending requests.",
261 		  cmd_net_dns_cancel),
262 	SHELL_CMD(query, NULL,
263 		  "'net dns <hostname> [A or AAAA]' queries IPv4 address "
264 		  "(default) or IPv6 address for a host name.",
265 		  cmd_net_dns_query),
266 	SHELL_SUBCMD_SET_END
267 );
268 
269 SHELL_SUBCMD_ADD((net), dns, &net_cmd_dns,
270 		 "Show how DNS is configured. Optionally do a query using a given name.",
271 		 cmd_net_dns, 1, 2);
272