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