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