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