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