xref: /openbsd-src/lib/libc/asr/getaddrinfo_async.c (revision d95d6a5584802dfbfc6e7b3fad160cd435955680)
1 /*	$OpenBSD: getaddrinfo_async.c,v 1.6 2012/09/05 15:56:13 eric Exp $	*/
2 /*
3  * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 #include <sys/types.h>
18 #include <sys/uio.h>
19 
20 #include <arpa/nameser.h>
21 
22 #include <err.h>
23 #include <errno.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 
28 #include "asr.h"
29 #include "asr_private.h"
30 
31 struct match {
32 	int family;
33 	int socktype;
34 	int protocol;
35 };
36 
37 static int getaddrinfo_async_run(struct async *, struct async_res *);
38 static int get_port(const char *, const char *, int);
39 static int iter_family(struct async *, int);
40 static int add_sockaddr(struct async *, struct sockaddr *, const char *);
41 
42 static const struct match matches[] = {
43 	{ PF_INET,	SOCK_DGRAM,	IPPROTO_UDP	},
44 	{ PF_INET,	SOCK_STREAM,	IPPROTO_TCP	},
45 	{ PF_INET,	SOCK_RAW,	0		},
46 	{ PF_INET6,	SOCK_DGRAM,	IPPROTO_UDP	},
47 	{ PF_INET6,	SOCK_STREAM,	IPPROTO_TCP	},
48 	{ PF_INET6,	SOCK_RAW,	0		},
49 	{ -1, 		0, 		0, 		},
50 };
51 
52 #define MATCH_FAMILY(a, b) ((a) == matches[(b)].family || (a) == PF_UNSPEC)
53 #define MATCH_PROTO(a, b) ((a) == matches[(b)].protocol || (a) == 0)
54 /* Do not match SOCK_RAW unless explicitely specified */
55 #define MATCH_SOCKTYPE(a, b) ((a) == matches[(b)].socktype || ((a) == 0 && \
56 				matches[(b)].socktype != SOCK_RAW))
57 
58 struct async *
59 getaddrinfo_async(const char *hostname, const char *servname,
60 	const struct addrinfo *hints, struct asr *asr)
61 {
62 	struct asr_ctx	*ac;
63 	struct async	*as;
64 
65 	ac = asr_use_resolver(asr);
66 	if ((as = async_new(ac, ASR_GETADDRINFO)) == NULL)
67 		goto abort; /* errno set */
68 	as->as_run = getaddrinfo_async_run;
69 
70 	if (hostname && (as->as.ai.hostname = strdup(hostname)) == NULL)
71 		goto abort; /* errno set */
72 	if (servname && (as->as.ai.servname = strdup(servname)) == NULL)
73 		goto abort; /* errno set */
74 	if (hints)
75 		memmove(&as->as.ai.hints, hints, sizeof *hints);
76 	else {
77 		memset(&as->as.ai.hints, 0, sizeof as->as.ai.hints);
78 		as->as.ai.hints.ai_family = PF_UNSPEC;
79 	}
80 
81 	asr_ctx_unref(ac);
82 	return (as);
83     abort:
84 	if (as)
85 		async_free(as);
86 	asr_ctx_unref(ac);
87 	return (NULL);
88 }
89 
90 static int
91 getaddrinfo_async_run(struct async *as, struct async_res *ar)
92 {
93 	const char	*str;
94 	struct addrinfo	*ai;
95 	int		 i, family, r;
96 	char		 fqdn[MAXDNAME];
97 	union {
98 		struct sockaddr		sa;
99 		struct sockaddr_in	sain;
100 		struct sockaddr_in6	sain6;
101 	} sa;
102 
103     next:
104 	switch(as->as_state) {
105 
106 	case ASR_STATE_INIT:
107 
108 		/*
109 		 * First, make sure the parameters are valid.
110 		 */
111 
112 		as->as_count = 0;
113 
114 		if (as->as.ai.hostname == NULL &&
115 		    as->as.ai.servname == NULL) {
116 			ar->ar_gai_errno = EAI_NONAME;
117 			async_set_state(as, ASR_STATE_HALT);
118 			break;
119 		}
120 
121 		ai = &as->as.ai.hints;
122 
123 		if (ai->ai_addrlen ||
124 		    ai->ai_canonname ||
125 		    ai->ai_addr ||
126 		    ai->ai_next) {
127 			ar->ar_gai_errno = EAI_BADHINTS;
128 			async_set_state(as, ASR_STATE_HALT);
129 			break;
130 		}
131 
132 		if (ai->ai_flags & ~AI_MASK ||
133 		    (ai->ai_flags & AI_CANONNAME && ai->ai_flags & AI_FQDN)) {
134 			ar->ar_gai_errno = EAI_BADFLAGS;
135 			async_set_state(as, ASR_STATE_HALT);
136 			break;
137 		}
138 
139 		if (ai->ai_family != PF_UNSPEC &&
140 		    ai->ai_family != PF_INET &&
141 		    ai->ai_family != PF_INET6) {
142 			ar->ar_gai_errno = EAI_FAMILY;
143 			async_set_state(as, ASR_STATE_HALT);
144 			break;
145 		}
146 
147 		if (ai->ai_socktype &&
148 		    ai->ai_socktype != SOCK_DGRAM  &&
149 		    ai->ai_socktype != SOCK_STREAM &&
150 		    ai->ai_socktype != SOCK_RAW) {
151 			ar->ar_gai_errno = EAI_SOCKTYPE;
152 			async_set_state(as, ASR_STATE_HALT);
153 			break;
154 		}
155 
156 		if (ai->ai_protocol &&
157 		    ai->ai_protocol != IPPROTO_UDP  &&
158 		    ai->ai_protocol != IPPROTO_TCP) {
159 			ar->ar_gai_errno = EAI_PROTOCOL;
160 			async_set_state(as, ASR_STATE_HALT);
161 			break;
162 		}
163 
164 		if (ai->ai_socktype == SOCK_RAW &&
165 		    as->as.ai.servname != NULL) {
166 			ar->ar_gai_errno = EAI_SERVICE;
167 			async_set_state(as, ASR_STATE_HALT);
168 			break;
169 		}
170 
171 		/* Make sure there is at least a valid combination */
172 		for (i = 0; matches[i].family != -1; i++)
173 			if (MATCH_FAMILY(ai->ai_family, i) &&
174 			    MATCH_SOCKTYPE(ai->ai_socktype, i) &&
175 			    MATCH_PROTO(ai->ai_protocol, i))
176 				break;
177 		if (matches[i].family == -1) {
178 			ar->ar_gai_errno = EAI_BADHINTS;
179 			async_set_state(as, ASR_STATE_HALT);
180 			break;
181 		}
182 
183 		if (ai->ai_protocol == 0 || ai->ai_protocol == IPPROTO_UDP)
184 			as->as.ai.port_udp = get_port(as->as.ai.servname, "udp",
185 			    as->as.ai.hints.ai_flags & AI_NUMERICSERV);
186 		if (ai->ai_protocol == 0 || ai->ai_protocol == IPPROTO_TCP)
187 			as->as.ai.port_tcp = get_port(as->as.ai.servname, "tcp",
188 			    as->as.ai.hints.ai_flags & AI_NUMERICSERV);
189 		if (as->as.ai.port_tcp == -2 || as->as.ai.port_udp == -2 ||
190 		    (as->as.ai.port_tcp == -1 && as->as.ai.port_udp == -1) ||
191 		    (ai->ai_protocol && (as->as.ai.port_udp == -1 ||
192 					 as->as.ai.port_tcp == -1))) {
193 			ar->ar_gai_errno = EAI_SERVICE;
194 			async_set_state(as, ASR_STATE_HALT);
195 			break;
196 		}
197 
198 		ar->ar_gai_errno = 0;
199 
200 		/* If hostname is NULL, use local address */
201 		if (as->as.ai.hostname == NULL) {
202 			for(family = iter_family(as, 1);
203 			    family != -1;
204 			    family = iter_family(as, 0)) {
205 				/*
206 				 * We could use statically built sockaddrs for
207 				 * those, rather than parsing over and over.
208 				 */
209 				if (family == PF_INET)
210 					str = (ai->ai_flags & AI_PASSIVE) ? \
211 						"0.0.0.0" : "127.0.0.1";
212 				else /* PF_INET6 */
213 					str = (ai->ai_flags & AI_PASSIVE) ? \
214 						"::" : "::1";
215 				 /* This can't fail */
216 				sockaddr_from_str(&sa.sa, family, str);
217 				if ((r = add_sockaddr(as, &sa.sa, NULL))) {
218 					ar->ar_gai_errno = r;
219 					break;
220 				}
221 			}
222 			if (ar->ar_gai_errno == 0 && as->as_count == 0) {
223 				ar->ar_gai_errno = EAI_NODATA;
224 			}
225 			async_set_state(as, ASR_STATE_HALT);
226 			break;
227 		}
228 
229 		/* Try numeric addresses first */
230 		for(family = iter_family(as, 1);
231 		    family != -1;
232 		    family = iter_family(as, 0)) {
233 
234 			if (sockaddr_from_str(&sa.sa, family,
235 					      as->as.ai.hostname) == -1)
236 				continue;
237 
238 			if ((r = add_sockaddr(as, &sa.sa, NULL))) {
239 				ar->ar_gai_errno = r;
240 			}
241 			break;
242 		}
243 		if (ar->ar_gai_errno || as->as_count) {
244 			async_set_state(as, ASR_STATE_HALT);
245 			break;
246 		}
247 
248 		if (ai->ai_flags & AI_NUMERICHOST) {
249 			ar->ar_gai_errno = EAI_FAIL;
250 			async_set_state(as, ASR_STATE_HALT);
251 			break;
252 		}
253 
254 		/* Starting domain lookup */
255 		async_set_state(as, ASR_STATE_SEARCH_DOMAIN);
256 		break;
257 
258 	case ASR_STATE_SEARCH_DOMAIN:
259 
260 		r = asr_iter_domain(as, as->as.ai.hostname, fqdn, sizeof(fqdn));
261 		if (r == -1) {
262 			async_set_state(as, ASR_STATE_NOT_FOUND);
263 			break;
264 		}
265 		if (r > (int)sizeof(fqdn)) {
266 			ar->ar_gai_errno = EAI_OVERFLOW;
267 			async_set_state(as, ASR_STATE_HALT);
268 			break;
269 		}
270 
271 		/*
272 		 * Create a subquery to lookup the host addresses.
273 		 * We use the special hostaddr_async() API, which has the
274 		 * nice property of honoring the "lookup" and "family" keyword
275 		 * in the configuration, thus returning the right address
276 		 * families in the right order, and thus fixing the current
277 		 * getaddrinfo() feature documented in the BUGS section of
278 		 * resolver.conf(5).
279 		 */
280 		as->as.ai.subq = hostaddr_async_ctx(fqdn,
281 		    as->as.ai.hints.ai_family, as->as.ai.hints.ai_flags,
282 		    as->as_ctx);
283 		if (as->as.ai.subq == NULL) {
284 			if (errno == EINVAL) {
285 				ar->ar_gai_errno = EAI_FAIL;
286 			} else {
287 				ar->ar_gai_errno = EAI_MEMORY;
288 			}
289 			async_set_state(as, ASR_STATE_HALT);
290 			break;
291 		}
292 		async_set_state(as, ASR_STATE_LOOKUP_DOMAIN);
293 		break;
294 
295 	case ASR_STATE_LOOKUP_DOMAIN:
296 
297 		/* Run the subquery */
298 		if ((r = async_run(as->as.ai.subq, ar)) == ASYNC_COND)
299 			return (ASYNC_COND);
300 
301 		/*
302 		 * The subquery is done. Stop there if we have at least one
303 		 * answer.
304 		 */
305 		as->as.ai.subq = NULL;
306 		if (ar->ar_count == 0) {
307 			/*
308 			 * No anwser for this domain, but we might be suggested
309 			 * to try again later, so remember this.  Then search
310 			 * the next domain.
311 			 */
312 			if (ar->ar_gai_errno == EAI_AGAIN)
313 				as->as.ai.flags |= ASYNC_AGAIN;
314 			async_set_state(as, ASR_STATE_SEARCH_DOMAIN);
315 			break;
316 		}
317 
318 		/* iterate over and expand results */
319 
320 		for(ai = ar->ar_addrinfo; ai; ai = ai->ai_next) {
321 			r = add_sockaddr(as, ai->ai_addr, ai->ai_canonname);
322 			if (r)
323 				break;
324 		}
325 		freeaddrinfo(ar->ar_addrinfo);
326 		ar->ar_gai_errno = r;
327 		async_set_state(as, ASR_STATE_HALT);
328 		break;
329 
330 	case ASR_STATE_NOT_FOUND:
331 
332 		/*
333 		 * No result found. Maybe we can try again.
334 		 */
335 		if (as->as.ai.flags & ASYNC_AGAIN) {
336 			ar->ar_gai_errno = EAI_AGAIN;
337 		} else {
338 			ar->ar_gai_errno = EAI_NODATA;
339 		}
340 		async_set_state(as, ASR_STATE_HALT);
341 		break;
342 
343 	case ASR_STATE_HALT:
344 
345 		/* Set the results. */
346 
347 		if (ar->ar_gai_errno == 0) {
348 			ar->ar_count = as->as_count;
349 			ar->ar_addrinfo = as->as.ai.aifirst;
350 			as->as.ai.aifirst = NULL;
351 		} else {
352 			ar->ar_count = 0;
353 			ar->ar_addrinfo = NULL;
354 		}
355 		return (ASYNC_DONE);
356 
357 	default:
358 		ar->ar_errno = EOPNOTSUPP;
359 		ar->ar_gai_errno = EAI_SYSTEM;
360 		async_set_state(as, ASR_STATE_HALT);
361                 break;
362 	}
363 	goto next;
364 }
365 
366 /*
367  * Retreive the port number for the service name "servname" and
368  * the protocol "proto".
369  */
370 static int
371 get_port(const char *servname, const char *proto, int numonly)
372 {
373 	struct servent		se;
374 	struct servent_data	sed;
375 	int			port, r;
376 	const char*		e;
377 
378 	if (servname == NULL)
379 		return (0);
380 
381 	e = NULL;
382 	port = strtonum(servname, 0, USHRT_MAX, &e);
383 	if (e == NULL)
384 		return (port);
385 	if (errno == ERANGE)
386 		return (-2); /* invalid */
387 	if (numonly)
388 		return (-2);
389 
390 	memset(&sed, 0, sizeof(sed));
391 	r = getservbyname_r(servname, proto, &se, &sed);
392 	port = ntohs(se.s_port);
393 	endservent_r(&sed);
394 
395 	if (r == -1)
396 		return (-1); /* not found */
397 
398 	return (port);
399 }
400 
401 /*
402  * Iterate over the address families that are to be queried. Use the
403  * list on the async context, unless a specific family was given in hints.
404  */
405 static int
406 iter_family(struct async *as, int first)
407 {
408 	if (first) {
409 		as->as_family_idx = 0;
410 		if (as->as.ai.hints.ai_family != PF_UNSPEC)
411 			return as->as.ai.hints.ai_family;
412 		return AS_FAMILY(as);
413 	}
414 
415 	if (as->as.ai.hints.ai_family != PF_UNSPEC)
416 		return (-1);
417 
418 	as->as_family_idx++;
419 
420 	return AS_FAMILY(as);
421 }
422 
423 /*
424  * Use the sockaddr at "sa" to extend the result list on the "as" context,
425  * with the specified canonical name "cname". This function adds one
426  * entry per protocol/socktype match.
427  */
428 static int
429 add_sockaddr(struct async *as, struct sockaddr *sa, const char *cname)
430 {
431 	struct addrinfo		*ai;
432 	int			 i, port;
433 
434 	for(i = 0; matches[i].family != -1; i++) {
435 		if (matches[i].family != sa->sa_family ||
436 		    !MATCH_SOCKTYPE(as->as.ai.hints.ai_socktype, i) ||
437 		    !MATCH_PROTO(as->as.ai.hints.ai_protocol, i))
438 			continue;
439 
440 		if (matches[i].protocol == IPPROTO_TCP)
441 			port = as->as.ai.port_tcp;
442 		else if (matches[i].protocol == IPPROTO_UDP)
443 			port = as->as.ai.port_udp;
444 		else
445 			port = 0;
446 
447 		/* servname specified, but not defined for this protocol */
448 		if (port == -1)
449 			continue;
450 
451 		ai = calloc(1, sizeof(*ai) + sa->sa_len);
452 		if (ai == NULL)
453 			return (EAI_MEMORY);
454 		ai->ai_family = sa->sa_family;
455 		ai->ai_socktype = matches[i].socktype;
456 		ai->ai_protocol = matches[i].protocol;
457 		ai->ai_addrlen = sa->sa_len;
458 		ai->ai_addr = (void*)(ai + 1);
459 		if (cname &&
460 		    as->as.ai.hints.ai_flags & (AI_CANONNAME | AI_FQDN)) {
461 			if ((ai->ai_canonname = strdup(cname)) == NULL) {
462 				free(ai);
463 				return (EAI_MEMORY);
464 			}
465 		}
466 		memmove(ai->ai_addr, sa, sa->sa_len);
467 		if (sa->sa_family == PF_INET)
468 			((struct sockaddr_in *)ai->ai_addr)->sin_port =
469 			    htons(port);
470 		else if (sa->sa_family == PF_INET6)
471 			((struct sockaddr_in6 *)ai->ai_addr)->sin6_port =
472 			    htons(port);
473 
474 		if (as->as.ai.aifirst == NULL)
475 			as->as.ai.aifirst = ai;
476 		if (as->as.ai.ailast)
477 			as->as.ai.ailast->ai_next = ai;
478 		as->as.ai.ailast = ai;
479 		as->as_count += 1;
480 	}
481 
482 	return (0);
483 }
484