1 /*
2 * Copyright (c) 2017 Intel Corporation.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/logging/log.h>
8 LOG_MODULE_REGISTER(net_dns_resolve_client_sample, LOG_LEVEL_DBG);
9
10 #include <zephyr/kernel.h>
11 #include <zephyr/linker/sections.h>
12 #include <errno.h>
13 #include <stdio.h>
14
15 #include <zephyr/net/net_core.h>
16 #include <zephyr/net/net_if.h>
17 #include <zephyr/net/net_mgmt.h>
18 #include <zephyr/net/dns_resolve.h>
19
20 #include "net_sample_common.h"
21
22 #if defined(CONFIG_MDNS_RESOLVER)
23 #if defined(CONFIG_NET_IPV4)
24 static struct k_work_delayable mdns_ipv4_timer;
25 static void do_mdns_ipv4_lookup(struct k_work *work);
26 #endif
27 #if defined(CONFIG_NET_IPV6)
28 static struct k_work_delayable mdns_ipv6_timer;
29 static void do_mdns_ipv6_lookup(struct k_work *work);
30 #endif
31 #endif
32
33 #define DNS_TIMEOUT (2 * MSEC_PER_SEC)
34
dns_result_cb(enum dns_resolve_status status,struct dns_addrinfo * info,void * user_data)35 void dns_result_cb(enum dns_resolve_status status,
36 struct dns_addrinfo *info,
37 void *user_data)
38 {
39 char hr_addr[NET_IPV6_ADDR_LEN];
40 char *hr_family;
41 void *addr;
42
43 switch (status) {
44 case DNS_EAI_CANCELED:
45 LOG_INF("DNS query was canceled");
46 return;
47 case DNS_EAI_FAIL:
48 LOG_INF("DNS resolve failed");
49 return;
50 case DNS_EAI_NODATA:
51 LOG_INF("Cannot resolve address");
52 return;
53 case DNS_EAI_ALLDONE:
54 LOG_INF("DNS resolving finished");
55 return;
56 case DNS_EAI_INPROGRESS:
57 break;
58 default:
59 LOG_INF("DNS resolving error (%d)", status);
60 return;
61 }
62
63 if (!info) {
64 return;
65 }
66
67 if (info->ai_family == AF_INET) {
68 hr_family = "IPv4";
69 addr = &net_sin(&info->ai_addr)->sin_addr;
70 } else if (info->ai_family == AF_INET6) {
71 hr_family = "IPv6";
72 addr = &net_sin6(&info->ai_addr)->sin6_addr;
73 } else {
74 LOG_ERR("Invalid IP address family %d", info->ai_family);
75 return;
76 }
77
78 LOG_INF("%s %s address: %s", user_data ? (char *)user_data : "<null>",
79 hr_family,
80 net_addr_ntop(info->ai_family, addr,
81 hr_addr, sizeof(hr_addr)));
82 }
83
mdns_result_cb(enum dns_resolve_status status,struct dns_addrinfo * info,void * user_data)84 void mdns_result_cb(enum dns_resolve_status status,
85 struct dns_addrinfo *info,
86 void *user_data)
87 {
88 char hr_addr[NET_IPV6_ADDR_LEN];
89 char *hr_family;
90 void *addr;
91
92 switch (status) {
93 case DNS_EAI_CANCELED:
94 LOG_INF("mDNS query was canceled");
95 return;
96 case DNS_EAI_FAIL:
97 LOG_INF("mDNS resolve failed");
98 return;
99 case DNS_EAI_NODATA:
100 LOG_INF("Cannot resolve address using mDNS");
101 return;
102 case DNS_EAI_ALLDONE:
103 LOG_INF("mDNS resolving finished");
104 return;
105 case DNS_EAI_INPROGRESS:
106 break;
107 default:
108 LOG_INF("mDNS resolving error (%d)", status);
109 return;
110 }
111
112 if (!info) {
113 return;
114 }
115
116 if (info->ai_family == AF_INET) {
117 hr_family = "IPv4";
118 addr = &net_sin(&info->ai_addr)->sin_addr;
119 } else if (info->ai_family == AF_INET6) {
120 hr_family = "IPv6";
121 addr = &net_sin6(&info->ai_addr)->sin6_addr;
122 } else {
123 LOG_ERR("Invalid IP address family %d", info->ai_family);
124 return;
125 }
126
127 LOG_INF("%s %s address: %s", user_data ? (char *)user_data : "<null>",
128 hr_family,
129 net_addr_ntop(info->ai_family, addr,
130 hr_addr, sizeof(hr_addr)));
131 }
132
133 #if defined(CONFIG_NET_DHCPV4)
134 static struct net_mgmt_event_callback mgmt4_cb;
135 static struct k_work_delayable ipv4_timer;
136
do_ipv4_lookup(struct k_work * work)137 static void do_ipv4_lookup(struct k_work *work)
138 {
139 static const char *query = "www.zephyrproject.org";
140 static uint16_t dns_id;
141 int ret;
142
143 ret = dns_get_addr_info(query,
144 DNS_QUERY_TYPE_A,
145 &dns_id,
146 dns_result_cb,
147 (void *)query,
148 DNS_TIMEOUT);
149 if (ret < 0) {
150 LOG_ERR("Cannot resolve IPv4 address (%d)", ret);
151 return;
152 }
153
154 LOG_DBG("DNS id %u", dns_id);
155 }
156
schedule_ipv4_queries(void)157 static void schedule_ipv4_queries(void)
158 {
159 k_work_init_delayable(&ipv4_timer, do_ipv4_lookup);
160 k_work_reschedule(&ipv4_timer, K_NO_WAIT);
161
162 #if defined(CONFIG_MDNS_RESOLVER)
163 k_work_init_delayable(&mdns_ipv4_timer, do_mdns_ipv4_lookup);
164 k_work_reschedule(&mdns_ipv4_timer, K_NO_WAIT);
165 #endif
166 }
167
print_dhcpv4_addr(struct net_if * iface,struct net_if_addr * if_addr,void * user_data)168 static void print_dhcpv4_addr(struct net_if *iface, struct net_if_addr *if_addr,
169 void *user_data)
170 {
171 bool *found = (bool *)user_data;
172 char hr_addr[NET_IPV4_ADDR_LEN];
173 struct in_addr netmask;
174
175 if (*found) {
176 return;
177 }
178
179 if (if_addr->addr_type != NET_ADDR_DHCP) {
180 return;
181 }
182
183 LOG_INF("IPv4 address: %s",
184 net_addr_ntop(AF_INET, &if_addr->address.in_addr,
185 hr_addr, NET_IPV4_ADDR_LEN));
186 LOG_INF("Lease time: %u seconds", iface->config.dhcpv4.lease_time);
187
188 netmask = net_if_ipv4_get_netmask_by_addr(iface,
189 &if_addr->address.in_addr);
190 LOG_INF("Subnet: %s",
191 net_addr_ntop(AF_INET, &netmask, hr_addr, NET_IPV4_ADDR_LEN));
192 LOG_INF("Router: %s",
193 net_addr_ntop(AF_INET,
194 &iface->config.ip.ipv4->gw,
195 hr_addr, NET_IPV4_ADDR_LEN));
196
197 *found = true;
198 }
199
ipv4_addr_add_handler(struct net_mgmt_event_callback * cb,uint32_t mgmt_event,struct net_if * iface)200 static void ipv4_addr_add_handler(struct net_mgmt_event_callback *cb,
201 uint32_t mgmt_event,
202 struct net_if *iface)
203 {
204
205 bool found = false;
206
207 if (mgmt_event != NET_EVENT_IPV4_ADDR_ADD) {
208 return;
209 }
210
211 net_if_ipv4_addr_foreach(iface, print_dhcpv4_addr, &found);
212
213 /* We cannot run DNS lookup directly from this thread as the
214 * management event thread stack is very small by default.
215 * So run it from work queue instead.
216 */
217 schedule_ipv4_queries();
218 }
219
check_dhcpv4_addr(struct net_if * iface,struct net_if_addr * if_addr,void * user_data)220 static void check_dhcpv4_addr(struct net_if *iface, struct net_if_addr *if_addr,
221 void *user_data)
222 {
223 bool *found = (bool *)user_data;
224
225 if (if_addr->addr_type != NET_ADDR_DHCP) {
226 return;
227 }
228
229 *found = true;
230 }
231
setup_dhcpv4(struct net_if * iface)232 static void setup_dhcpv4(struct net_if *iface)
233 {
234 bool found;
235
236 /* If DHCP registers an IP address before we register the
237 * ipv4_addr_add_handler() callback, we won't be notified. Check
238 * whether this is the case.
239 */
240 net_if_ipv4_addr_foreach(iface, check_dhcpv4_addr, &found);
241
242 if (found) {
243 /* Already have DHCP assigned address, schedule queries. */
244 schedule_ipv4_queries();
245 return;
246 }
247
248 /* Otherwise, wait for DHCP to assign an address. */
249 LOG_INF("Getting IPv4 address via DHCP before issuing DNS query");
250
251 net_mgmt_init_event_callback(&mgmt4_cb, ipv4_addr_add_handler,
252 NET_EVENT_IPV4_ADDR_ADD);
253 net_mgmt_add_event_callback(&mgmt4_cb);
254
255 net_dhcpv4_start(iface);
256 }
257
258 #else
259 #define setup_dhcpv4(...)
260 #endif /* CONFIG_NET_DHCPV4 */
261
262 #if defined(CONFIG_NET_IPV4) || defined(CONFIG_NET_DHCPV4)
263 #if defined(CONFIG_MDNS_RESOLVER)
do_mdns_ipv4_lookup(struct k_work * work)264 static void do_mdns_ipv4_lookup(struct k_work *work)
265 {
266 static const char *query = "zephyr.local";
267 int ret;
268
269 LOG_DBG("Doing mDNS IPv4 query");
270
271 ret = dns_get_addr_info(query,
272 DNS_QUERY_TYPE_A,
273 NULL,
274 mdns_result_cb,
275 (void *)query,
276 DNS_TIMEOUT);
277 if (ret < 0) {
278 LOG_ERR("Cannot resolve mDNS IPv4 address (%d)", ret);
279 return;
280 }
281
282 LOG_DBG("mDNS v4 query sent");
283 }
284 #endif
285 #endif
286
287 #if defined(CONFIG_NET_IPV4) && !defined(CONFIG_NET_DHCPV4)
288
289 #if !defined(CONFIG_NET_CONFIG_MY_IPV4_ADDR)
290 #error "You need to define an IPv4 address or enable DHCPv4!"
291 #endif
292
do_ipv4_lookup(void)293 static void do_ipv4_lookup(void)
294 {
295 static const char *query = "www.zephyrproject.org";
296 static uint16_t dns_id;
297 int ret;
298
299 ret = dns_get_addr_info(query,
300 DNS_QUERY_TYPE_A,
301 &dns_id,
302 dns_result_cb,
303 (void *)query,
304 DNS_TIMEOUT);
305 if (ret < 0) {
306 LOG_ERR("Cannot resolve IPv4 address (%d)", ret);
307 return;
308 }
309
310 LOG_DBG("DNS id %u", dns_id);
311 }
312
setup_ipv4(struct net_if * iface)313 static void setup_ipv4(struct net_if *iface)
314 {
315 ARG_UNUSED(iface);
316
317 do_ipv4_lookup();
318
319 #if defined(CONFIG_MDNS_RESOLVER) && defined(CONFIG_NET_IPV4)
320 k_work_init_delayable(&mdns_ipv4_timer, do_mdns_ipv4_lookup);
321 k_work_reschedule(&mdns_ipv4_timer, K_NO_WAIT);
322 #endif
323 }
324
325 #else
326 #define setup_ipv4(...)
327 #endif /* CONFIG_NET_IPV4 && !CONFIG_NET_DHCPV4 */
328
329 #if defined(CONFIG_NET_IPV6)
330
331 #if !defined(CONFIG_NET_CONFIG_MY_IPV6_ADDR)
332 #error "You need to define an IPv6 address!"
333 #endif
334
do_ipv6_lookup(void)335 static void do_ipv6_lookup(void)
336 {
337 static const char *query = "www.zephyrproject.org";
338 static uint16_t dns_id;
339 int ret;
340
341 ret = dns_get_addr_info(query,
342 DNS_QUERY_TYPE_AAAA,
343 &dns_id,
344 dns_result_cb,
345 (void *)query,
346 DNS_TIMEOUT);
347 if (ret < 0) {
348 LOG_ERR("Cannot resolve IPv6 address (%d)", ret);
349 return;
350 }
351
352 LOG_DBG("DNS id %u", dns_id);
353 }
354
setup_ipv6(struct net_if * iface)355 static void setup_ipv6(struct net_if *iface)
356 {
357 ARG_UNUSED(iface);
358
359 do_ipv6_lookup();
360
361 #if defined(CONFIG_MDNS_RESOLVER) && defined(CONFIG_NET_IPV6)
362 k_work_init_delayable(&mdns_ipv6_timer, do_mdns_ipv6_lookup);
363 k_work_reschedule(&mdns_ipv6_timer, K_NO_WAIT);
364 #endif
365 }
366
367 #if defined(CONFIG_MDNS_RESOLVER)
do_mdns_ipv6_lookup(struct k_work * work)368 static void do_mdns_ipv6_lookup(struct k_work *work)
369 {
370 static const char *query = "zephyr.local";
371 int ret;
372
373 LOG_DBG("Doing mDNS IPv6 query");
374
375 ret = dns_get_addr_info(query,
376 DNS_QUERY_TYPE_AAAA,
377 NULL,
378 mdns_result_cb,
379 (void *)query,
380 DNS_TIMEOUT);
381 if (ret < 0) {
382 LOG_ERR("Cannot resolve mDNS IPv6 address (%d)", ret);
383 return;
384 }
385
386 LOG_DBG("mDNS v6 query sent");
387 }
388 #endif
389
390 #else
391 #define setup_ipv6(...)
392 #endif /* CONFIG_NET_IPV6 */
393
main(void)394 int main(void)
395 {
396 struct net_if *iface = net_if_get_default();
397
398 LOG_INF("Starting DNS resolve sample");
399
400 wait_for_network();
401
402 setup_ipv4(iface);
403
404 setup_dhcpv4(iface);
405
406 setup_ipv6(iface);
407 return 0;
408 }
409