xref: /openbsd-src/lib/libc/asr/getaddrinfo_async.c (revision 0af765e74c6fdcf437994d9d94bce71f85ebe4a7)
1 /*	$OpenBSD: getaddrinfo_async.c,v 1.22 2014/02/26 20:50:24 deraadt 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 
18 #include <sys/types.h>
19 #include <sys/uio.h>
20 #include <sys/socket.h>
21 #include <netinet/in.h>
22 #include <arpa/nameser.h>
23 #ifdef YP
24 #include <rpc/rpc.h>
25 #include <rpcsvc/yp.h>
26 #include <rpcsvc/ypclnt.h>
27 #include "ypinternal.h"
28 #endif
29 
30 #include <err.h>
31 #include <errno.h>
32 #include <resolv.h> /* for res_hnok */
33 #include <stdlib.h>
34 #include <string.h>
35 #include <unistd.h>
36 
37 #include "asr.h"
38 #include "asr_private.h"
39 
40 struct match {
41 	int family;
42 	int socktype;
43 	int protocol;
44 };
45 
46 static int getaddrinfo_async_run(struct async *, struct async_res *);
47 static int get_port(const char *, const char *, int);
48 static int iter_family(struct async *, int);
49 static int iter_domain(struct async *, const char *, char *, size_t);
50 static int addrinfo_add(struct async *, const struct sockaddr *, const char *);
51 static int addrinfo_from_file(struct async *, int,  FILE *);
52 static int addrinfo_from_pkt(struct async *, char *, size_t);
53 #ifdef YP
54 static int addrinfo_from_yp(struct async *, int, char *);
55 #endif
56 
57 static const struct match matches[] = {
58 	{ PF_INET,	SOCK_DGRAM,	IPPROTO_UDP	},
59 	{ PF_INET,	SOCK_STREAM,	IPPROTO_TCP	},
60 	{ PF_INET,	SOCK_RAW,	0		},
61 	{ PF_INET6,	SOCK_DGRAM,	IPPROTO_UDP	},
62 	{ PF_INET6,	SOCK_STREAM,	IPPROTO_TCP	},
63 	{ PF_INET6,	SOCK_RAW,	0		},
64 	{ -1,		0,		0,		},
65 };
66 
67 #define MATCH_FAMILY(a, b) ((a) == matches[(b)].family || (a) == PF_UNSPEC)
68 #define MATCH_PROTO(a, b) ((a) == matches[(b)].protocol || (a) == 0 || matches[(b)].protocol == 0)
69 /* Do not match SOCK_RAW unless explicitely specified */
70 #define MATCH_SOCKTYPE(a, b) ((a) == matches[(b)].socktype || ((a) == 0 && \
71 				matches[(b)].socktype != SOCK_RAW))
72 
73 enum {
74 	DOM_INIT,
75 	DOM_DOMAIN,
76 	DOM_DONE
77 };
78 
79 struct async *
80 getaddrinfo_async(const char *hostname, const char *servname,
81 	const struct addrinfo *hints, struct asr *asr)
82 {
83 	struct asr_ctx	*ac;
84 	struct async	*as;
85 
86 	ac = asr_use_resolver(asr);
87 	if ((as = asr_async_new(ac, ASR_GETADDRINFO)) == NULL)
88 		goto abort; /* errno set */
89 	as->as_run = getaddrinfo_async_run;
90 
91 	if (hostname && (as->as.ai.hostname = strdup(hostname)) == NULL)
92 		goto abort; /* errno set */
93 	if (servname && (as->as.ai.servname = strdup(servname)) == NULL)
94 		goto abort; /* errno set */
95 	if (hints)
96 		memmove(&as->as.ai.hints, hints, sizeof *hints);
97 	else {
98 		memset(&as->as.ai.hints, 0, sizeof as->as.ai.hints);
99 		as->as.ai.hints.ai_family = PF_UNSPEC;
100 	}
101 
102 	asr_ctx_unref(ac);
103 	return (as);
104     abort:
105 	if (as)
106 		asr_async_free(as);
107 	asr_ctx_unref(ac);
108 	return (NULL);
109 }
110 
111 static int
112 getaddrinfo_async_run(struct async *as, struct async_res *ar)
113 {
114 #ifdef YP
115 	static char	*domain = NULL;
116 	char		*res;
117 	int		 len;
118 	char		 alias[MAXDNAME], *name;
119 #endif
120 	char		 fqdn[MAXDNAME];
121 	const char	*str;
122 	struct addrinfo	*ai;
123 	int		 i, family, r;
124 	FILE		*f;
125 	union {
126 		struct sockaddr		sa;
127 		struct sockaddr_in	sain;
128 		struct sockaddr_in6	sain6;
129 	} sa;
130 
131     next:
132 	switch (as->as_state) {
133 
134 	case ASR_STATE_INIT:
135 
136 		/*
137 		 * First, make sure the parameters are valid.
138 		 */
139 
140 		as->as_count = 0;
141 
142 		if (as->as.ai.hostname == NULL &&
143 		    as->as.ai.servname == NULL) {
144 			ar->ar_gai_errno = EAI_NONAME;
145 			async_set_state(as, ASR_STATE_HALT);
146 			break;
147 		}
148 
149 		ai = &as->as.ai.hints;
150 
151 		if (ai->ai_addrlen ||
152 		    ai->ai_canonname ||
153 		    ai->ai_addr ||
154 		    ai->ai_next) {
155 			ar->ar_gai_errno = EAI_BADHINTS;
156 			async_set_state(as, ASR_STATE_HALT);
157 			break;
158 		}
159 
160 		if (ai->ai_flags & ~AI_MASK ||
161 		    (ai->ai_flags & AI_CANONNAME && ai->ai_flags & AI_FQDN)) {
162 			ar->ar_gai_errno = EAI_BADFLAGS;
163 			async_set_state(as, ASR_STATE_HALT);
164 			break;
165 		}
166 
167 		if (ai->ai_family != PF_UNSPEC &&
168 		    ai->ai_family != PF_INET &&
169 		    ai->ai_family != PF_INET6) {
170 			ar->ar_gai_errno = EAI_FAMILY;
171 			async_set_state(as, ASR_STATE_HALT);
172 			break;
173 		}
174 
175 		if (ai->ai_socktype &&
176 		    ai->ai_socktype != SOCK_DGRAM  &&
177 		    ai->ai_socktype != SOCK_STREAM &&
178 		    ai->ai_socktype != SOCK_RAW) {
179 			ar->ar_gai_errno = EAI_SOCKTYPE;
180 			async_set_state(as, ASR_STATE_HALT);
181 			break;
182 		}
183 
184 		if (ai->ai_socktype == SOCK_RAW &&
185 		    get_port(as->as.ai.servname, NULL, 1) != 0) {
186 			ar->ar_gai_errno = EAI_SERVICE;
187 			async_set_state(as, ASR_STATE_HALT);
188 			break;
189 		}
190 
191 		/* Make sure there is at least a valid combination */
192 		for (i = 0; matches[i].family != -1; i++)
193 			if (MATCH_FAMILY(ai->ai_family, i) &&
194 			    MATCH_SOCKTYPE(ai->ai_socktype, i) &&
195 			    MATCH_PROTO(ai->ai_protocol, i))
196 				break;
197 		if (matches[i].family == -1) {
198 			ar->ar_gai_errno = EAI_BADHINTS;
199 			async_set_state(as, ASR_STATE_HALT);
200 			break;
201 		}
202 
203 		if (ai->ai_protocol == 0 || ai->ai_protocol == IPPROTO_UDP)
204 			as->as.ai.port_udp = get_port(as->as.ai.servname, "udp",
205 			    as->as.ai.hints.ai_flags & AI_NUMERICSERV);
206 		if (ai->ai_protocol == 0 || ai->ai_protocol == IPPROTO_TCP)
207 			as->as.ai.port_tcp = get_port(as->as.ai.servname, "tcp",
208 			    as->as.ai.hints.ai_flags & AI_NUMERICSERV);
209 		if (as->as.ai.port_tcp == -2 || as->as.ai.port_udp == -2 ||
210 		    (as->as.ai.port_tcp == -1 && as->as.ai.port_udp == -1) ||
211 		    (ai->ai_protocol && (as->as.ai.port_udp == -1 ||
212 					 as->as.ai.port_tcp == -1))) {
213 			ar->ar_gai_errno = EAI_SERVICE;
214 			async_set_state(as, ASR_STATE_HALT);
215 			break;
216 		}
217 
218 		ar->ar_gai_errno = 0;
219 
220 		/* If hostname is NULL, use local address */
221 		if (as->as.ai.hostname == NULL) {
222 			for (family = iter_family(as, 1);
223 			    family != -1;
224 			    family = iter_family(as, 0)) {
225 				/*
226 				 * We could use statically built sockaddrs for
227 				 * those, rather than parsing over and over.
228 				 */
229 				if (family == PF_INET)
230 					str = (ai->ai_flags & AI_PASSIVE) ? \
231 						"0.0.0.0" : "127.0.0.1";
232 				else /* PF_INET6 */
233 					str = (ai->ai_flags & AI_PASSIVE) ? \
234 						"::" : "::1";
235 				 /* This can't fail */
236 				asr_sockaddr_from_str(&sa.sa, family, str);
237 				if ((r = addrinfo_add(as, &sa.sa, NULL))) {
238 					ar->ar_gai_errno = r;
239 					break;
240 				}
241 			}
242 			if (ar->ar_gai_errno == 0 && as->as_count == 0) {
243 				ar->ar_gai_errno = EAI_NODATA;
244 			}
245 			async_set_state(as, ASR_STATE_HALT);
246 			break;
247 		}
248 
249 		/* Try numeric addresses first */
250 		for (family = iter_family(as, 1);
251 		    family != -1;
252 		    family = iter_family(as, 0)) {
253 
254 			if (asr_sockaddr_from_str(&sa.sa, family,
255 			    as->as.ai.hostname) == -1)
256 				continue;
257 
258 			if ((r = addrinfo_add(as, &sa.sa, NULL)))
259 				ar->ar_gai_errno = r;
260 			break;
261 		}
262 		if (ar->ar_gai_errno || as->as_count) {
263 			async_set_state(as, ASR_STATE_HALT);
264 			break;
265 		}
266 
267 		if (ai->ai_flags & AI_NUMERICHOST) {
268 			ar->ar_gai_errno = EAI_NONAME;
269 			async_set_state(as, ASR_STATE_HALT);
270 			break;
271 		}
272 
273 		async_set_state(as, ASR_STATE_NEXT_DB);
274 		break;
275 
276 	case ASR_STATE_NEXT_DB:
277 		if (asr_iter_db(as) == -1) {
278 			async_set_state(as, ASR_STATE_NOT_FOUND);
279 			break;
280 		}
281 		as->as_family_idx = 0;
282 		async_set_state(as, ASR_STATE_SAME_DB);
283 		break;
284 
285 	case ASR_STATE_NEXT_FAMILY:
286 		as->as_family_idx += 1;
287 		if (as->as.ai.hints.ai_family != AF_UNSPEC ||
288 		    AS_FAMILY(as) == -1) {
289 			/* The family was specified, or we have tried all
290 			 * families with this DB.
291 			 */
292 			if (as->as_count) {
293 				ar->ar_gai_errno = 0;
294 				async_set_state(as, ASR_STATE_HALT);
295 			} else
296 				async_set_state(as, ASR_STATE_NEXT_DOMAIN);
297 			break;
298 		}
299 		async_set_state(as, ASR_STATE_SAME_DB);
300 		break;
301 
302 	case ASR_STATE_NEXT_DOMAIN:
303 		/* domain search is only for dns */
304 		if (AS_DB(as) != ASR_DB_DNS) {
305 			async_set_state(as, ASR_STATE_NEXT_DB);
306 			break;
307 		}
308 		as->as_family_idx = 0;
309 
310 		free(as->as.ai.fqdn);
311 		as->as.ai.fqdn = NULL;
312 		r = iter_domain(as, as->as.ai.hostname, fqdn, sizeof(fqdn));
313 		if (r == -1) {
314 			async_set_state(as, ASR_STATE_NEXT_DB);
315 			break;
316 		}
317 		if (r == 0) {
318 			ar->ar_gai_errno = EAI_FAIL;
319 			async_set_state(as, ASR_STATE_HALT);
320 			break;
321 		}
322 		as->as.ai.fqdn = strdup(fqdn);
323 		if (as->as.ai.fqdn == NULL) {
324 			ar->ar_gai_errno = EAI_MEMORY;
325 			async_set_state(as, ASR_STATE_HALT);
326 		}
327 
328 		async_set_state(as, ASR_STATE_SAME_DB);
329 		break;
330 
331 	case ASR_STATE_SAME_DB:
332 		/* query the current DB again */
333 		switch (AS_DB(as)) {
334 		case ASR_DB_DNS:
335 			if (as->as.ai.fqdn == NULL) {
336 				/* First try, initialize domain iteration */
337 				as->as_dom_flags = 0;
338 				as->as_dom_step = DOM_INIT;
339 				async_set_state(as, ASR_STATE_NEXT_DOMAIN);
340 				break;
341 			}
342 
343 			family = (as->as.ai.hints.ai_family == AF_UNSPEC) ?
344 			    AS_FAMILY(as) : as->as.ai.hints.ai_family;
345 
346 			as->as.ai.subq = res_query_async_ctx(as->as.ai.fqdn,
347 			    C_IN, (family == AF_INET6) ? T_AAAA : T_A,
348 			    as->as_ctx);
349 
350 			if (as->as.ai.subq == NULL) {
351 				if (errno == ENOMEM)
352 					ar->ar_gai_errno = EAI_MEMORY;
353 				else
354 					ar->ar_gai_errno = EAI_FAIL;
355 				async_set_state(as, ASR_STATE_HALT);
356 				break;
357 			}
358 			async_set_state(as, ASR_STATE_SUBQUERY);
359 			break;
360 
361 		case ASR_DB_FILE:
362 			f = fopen(as->as_ctx->ac_hostfile, "r");
363 			if (f == NULL) {
364 				async_set_state(as, ASR_STATE_NEXT_DB);
365 				break;
366 			}
367 			family = (as->as.ai.hints.ai_family == AF_UNSPEC) ?
368 			    AS_FAMILY(as) : as->as.ai.hints.ai_family;
369 
370 			r = addrinfo_from_file(as, family, f);
371 			if (r == -1) {
372 				if (errno == ENOMEM)
373 					ar->ar_gai_errno = EAI_MEMORY;
374 				else
375 					ar->ar_gai_errno = EAI_FAIL;
376 				async_set_state(as, ASR_STATE_HALT);
377 			} else
378 				async_set_state(as, ASR_STATE_NEXT_FAMILY);
379 			fclose(f);
380 			break;
381 
382 #ifdef YP
383 		case ASR_DB_YP:
384 			if (!domain && _yp_check(&domain) == 0) {
385 				async_set_state(as, ASR_STATE_NEXT_DB);
386 				break;
387 			}
388 			family = (as->as.ai.hints.ai_family == AF_UNSPEC) ?
389 			    AS_FAMILY(as) : as->as.ai.hints.ai_family;
390 
391 			name = asr_hostalias(as->as_ctx, as->as.ai.hostname,
392 			    alias, sizeof(alias));
393 			if (name == NULL)
394 				name = as->as.ai.hostname;
395 
396 			/* XXX
397 			 * ipnodes.byname could also contain IPv4 address
398 			 */
399 			r = yp_match(domain, (family == AF_INET6) ?
400 			    "ipnodes.byname" : "hosts.byname",
401 			    name, strlen(name), &res, &len);
402 			if (r == 0) {
403 				r = addrinfo_from_yp(as, family, res);
404 				free(res);
405 				if (r == -1) {
406 					if (errno == ENOMEM)
407 						ar->ar_gai_errno = EAI_MEMORY;
408 					else
409 						ar->ar_gai_errno = EAI_FAIL;
410 					async_set_state(as, ASR_STATE_HALT);
411 					break;
412 				}
413 			}
414 			async_set_state(as, ASR_STATE_NEXT_FAMILY);
415 			break;
416 #endif
417 		default:
418 			async_set_state(as, ASR_STATE_NEXT_DB);
419 		}
420 		break;
421 
422 	case ASR_STATE_SUBQUERY:
423 		if ((r = asr_async_run(as->as.ai.subq, ar)) == ASYNC_COND)
424 			return (ASYNC_COND);
425 		as->as.ai.subq = NULL;
426 
427 		if (ar->ar_datalen == -1) {
428 			async_set_state(as, ASR_STATE_NEXT_FAMILY);
429 			break;
430 		}
431 
432 		r = addrinfo_from_pkt(as, ar->ar_data, ar->ar_datalen);
433 		if (r == -1) {
434 			if (errno == ENOMEM)
435 				ar->ar_gai_errno = EAI_MEMORY;
436 			else
437 				ar->ar_gai_errno = EAI_FAIL;
438 			async_set_state(as, ASR_STATE_HALT);
439 		} else
440 			async_set_state(as, ASR_STATE_NEXT_FAMILY);
441 		free(ar->ar_data);
442 		break;
443 
444 	case ASR_STATE_NOT_FOUND:
445 		/* No result found. Maybe we can try again. */
446 		if (as->as.ai.flags & ASYNC_AGAIN)
447 			ar->ar_gai_errno = EAI_AGAIN;
448 		else
449 			ar->ar_gai_errno = EAI_NODATA;
450 		async_set_state(as, ASR_STATE_HALT);
451 		break;
452 
453 	case ASR_STATE_HALT:
454 		if (ar->ar_gai_errno == 0) {
455 			ar->ar_count = as->as_count;
456 			ar->ar_addrinfo = as->as.ai.aifirst;
457 			as->as.ai.aifirst = NULL;
458 		} else {
459 			ar->ar_count = 0;
460 			ar->ar_addrinfo = NULL;
461 		}
462 		return (ASYNC_DONE);
463 
464 	default:
465 		ar->ar_errno = EOPNOTSUPP;
466 		ar->ar_gai_errno = EAI_SYSTEM;
467 		async_set_state(as, ASR_STATE_HALT);
468 		break;
469 	}
470 	goto next;
471 }
472 
473 /*
474  * Retreive the port number for the service name "servname" and
475  * the protocol "proto".
476  */
477 static int
478 get_port(const char *servname, const char *proto, int numonly)
479 {
480 	struct servent		se;
481 	struct servent_data	sed;
482 	int			port, r;
483 	const char		*e;
484 
485 	if (servname == NULL)
486 		return (0);
487 
488 	e = NULL;
489 	port = strtonum(servname, 0, USHRT_MAX, &e);
490 	if (e == NULL)
491 		return (port);
492 	if (errno == ERANGE)
493 		return (-2); /* invalid */
494 	if (numonly)
495 		return (-2);
496 
497 	memset(&sed, 0, sizeof(sed));
498 	r = getservbyname_r(servname, proto, &se, &sed);
499 	port = ntohs(se.s_port);
500 	endservent_r(&sed);
501 
502 	if (r == -1)
503 		return (-1); /* not found */
504 
505 	return (port);
506 }
507 
508 /*
509  * Iterate over the address families that are to be queried. Use the
510  * list on the async context, unless a specific family was given in hints.
511  */
512 static int
513 iter_family(struct async *as, int first)
514 {
515 	if (first) {
516 		as->as_family_idx = 0;
517 		if (as->as.ai.hints.ai_family != PF_UNSPEC)
518 			return as->as.ai.hints.ai_family;
519 		return AS_FAMILY(as);
520 	}
521 
522 	if (as->as.ai.hints.ai_family != PF_UNSPEC)
523 		return (-1);
524 
525 	as->as_family_idx++;
526 
527 	return AS_FAMILY(as);
528 }
529 
530 /*
531  * Concatenate a name and a domain name. The result has no trailing dot.
532  * Return the resulting string length, or 0 in case of error.
533  */
534 static size_t
535 domcat(const char *name, const char *domain, char *buf, size_t buflen)
536 {
537 	size_t	r;
538 
539 	r = asr_make_fqdn(name, domain, buf, buflen);
540 	if (r == 0)
541 		return (0);
542 	buf[r - 1] = '\0';
543 
544 	return (r - 1);
545 }
546 
547 /*
548  * Implement the search domain strategy.
549  *
550  * XXX duplicate from res_search_async
551  *
552  * This function works as a generator that constructs complete domains in
553  * buffer "buf" of size "len" for the given host name "name", according to the
554  * search rules defined by the resolving context.  It is supposed to be called
555  * multiple times (with the same name) to generate the next possible domain
556  * name, if any.
557  *
558  * It returns -1 if all possibilities have been exhausted, 0 if there was an
559  * error generating the next name, or the resulting name length.
560  */
561 static int
562 iter_domain(struct async *as, const char *name, char * buf, size_t len)
563 {
564 	const char	*c;
565 	int		 dots;
566 
567 	switch (as->as_dom_step) {
568 
569 	case DOM_INIT:
570 		/* First call */
571 
572 		/*
573 		 * If "name" is an FQDN, that's the only result and we
574 		 * don't try anything else.
575 		 */
576 		if (strlen(name) && name[strlen(name) - 1] ==  '.') {
577 			DPRINT("asr: iter_domain(\"%s\") fqdn\n", name);
578 			as->as_dom_flags |= ASYNC_DOM_FQDN;
579 			as->as_dom_step = DOM_DONE;
580 			return (domcat(name, NULL, buf, len));
581 		}
582 
583 		/*
584 		 * Otherwise, we iterate through the specified search domains.
585 		 */
586 		as->as_dom_step = DOM_DOMAIN;
587 		as->as_dom_idx = 0;
588 
589 		/*
590 		 * If "name" as enough dots, use it as-is first, as indicated
591 		 * in resolv.conf(5).
592 		 */
593 		dots = 0;
594 		for (c = name; *c; c++)
595 			dots += (*c == '.');
596 		if (dots >= as->as_ctx->ac_ndots) {
597 			DPRINT("asr: iter_domain(\"%s\") ndots\n", name);
598 			as->as_dom_flags |= ASYNC_DOM_NDOTS;
599 			if (strlcpy(buf, name, len) >= len)
600 				return (0);
601 			return (strlen(buf));
602 		}
603 		/* Otherwise, starts using the search domains */
604 		/* FALLTHROUGH */
605 
606 	case DOM_DOMAIN:
607 		if (as->as_dom_idx < as->as_ctx->ac_domcount) {
608 			DPRINT("asr: iter_domain(\"%s\") domain \"%s\"\n",
609 			    name, as->as_ctx->ac_dom[as->as_dom_idx]);
610 			as->as_dom_flags |= ASYNC_DOM_DOMAIN;
611 			return (domcat(name,
612 			    as->as_ctx->ac_dom[as->as_dom_idx++], buf, len));
613 		}
614 
615 		/* No more domain to try. */
616 
617 		as->as_dom_step = DOM_DONE;
618 
619 		/*
620 		 * If the name was not tried as an absolute name before,
621 		 * do it now.
622 		 */
623 		if (!(as->as_dom_flags & ASYNC_DOM_NDOTS)) {
624 			DPRINT("asr: iter_domain(\"%s\") as is\n", name);
625 			as->as_dom_flags |= ASYNC_DOM_ASIS;
626 			if (strlcpy(buf, name, len) >= len)
627 				return (0);
628 			return (strlen(buf));
629 		}
630 		/* Otherwise, we are done. */
631 
632 	case DOM_DONE:
633 	default:
634 		DPRINT("asr: iter_domain(\"%s\") done\n", name);
635 		return (-1);
636 	}
637 }
638 
639 /*
640  * Use the sockaddr at "sa" to extend the result list on the "as" context,
641  * with the specified canonical name "cname". This function adds one
642  * entry per protocol/socktype match.
643  */
644 static int
645 addrinfo_add(struct async *as, const struct sockaddr *sa, const char *cname)
646 {
647 	struct addrinfo		*ai;
648 	int			 i, port, proto;
649 
650 	for (i = 0; matches[i].family != -1; i++) {
651 		if (matches[i].family != sa->sa_family ||
652 		    !MATCH_SOCKTYPE(as->as.ai.hints.ai_socktype, i) ||
653 		    !MATCH_PROTO(as->as.ai.hints.ai_protocol, i))
654 			continue;
655 
656 		proto = as->as.ai.hints.ai_protocol;
657 		if (!proto)
658 			proto = matches[i].protocol;
659 
660 		if (proto == IPPROTO_TCP)
661 			port = as->as.ai.port_tcp;
662 		else if (proto == IPPROTO_UDP)
663 			port = as->as.ai.port_udp;
664 		else
665 			port = 0;
666 
667 		/* servname specified, but not defined for this protocol */
668 		if (port == -1)
669 			continue;
670 
671 		ai = calloc(1, sizeof(*ai) + sa->sa_len);
672 		if (ai == NULL)
673 			return (EAI_MEMORY);
674 		ai->ai_family = sa->sa_family;
675 		ai->ai_socktype = matches[i].socktype;
676 		ai->ai_protocol = proto;
677 		ai->ai_flags = as->as.ai.hints.ai_flags;
678 		ai->ai_addrlen = sa->sa_len;
679 		ai->ai_addr = (void *)(ai + 1);
680 		if (cname &&
681 		    as->as.ai.hints.ai_flags & (AI_CANONNAME | AI_FQDN)) {
682 			if ((ai->ai_canonname = strdup(cname)) == NULL) {
683 				free(ai);
684 				return (EAI_MEMORY);
685 			}
686 		}
687 		memmove(ai->ai_addr, sa, sa->sa_len);
688 		if (sa->sa_family == PF_INET)
689 			((struct sockaddr_in *)ai->ai_addr)->sin_port =
690 			    htons(port);
691 		else if (sa->sa_family == PF_INET6)
692 			((struct sockaddr_in6 *)ai->ai_addr)->sin6_port =
693 			    htons(port);
694 
695 		if (as->as.ai.aifirst == NULL)
696 			as->as.ai.aifirst = ai;
697 		if (as->as.ai.ailast)
698 			as->as.ai.ailast->ai_next = ai;
699 		as->as.ai.ailast = ai;
700 		as->as_count += 1;
701 	}
702 
703 	return (0);
704 }
705 
706 static int
707 addrinfo_from_file(struct async *as, int family, FILE *f)
708 {
709 	char		*tokens[MAXTOKEN], buf[MAXDNAME], *name, *c;
710 	int		 n, i;
711 	union {
712 		struct sockaddr		sa;
713 		struct sockaddr_in	sain;
714 		struct sockaddr_in6	sain6;
715 	} u;
716 
717 	name = asr_hostalias(as->as_ctx, as->as.ai.hostname, buf, sizeof(buf));
718 	if (name == NULL)
719 		name = as->as.ai.hostname;
720 
721 	for (;;) {
722 		n = asr_parse_namedb_line(f, tokens, MAXTOKEN);
723 		if (n == -1)
724 			break; /* ignore errors reading the file */
725 
726 		for (i = 1; i < n; i++) {
727 			if (strcasecmp(name, tokens[i]))
728 				continue;
729 			if (asr_sockaddr_from_str(&u.sa, family, tokens[0]) == -1)
730 				continue;
731 			break;
732 		}
733 		if (i == n)
734 			continue;
735 
736 		if (as->as.ai.hints.ai_flags & (AI_CANONNAME | AI_FQDN))
737 			c = tokens[1];
738 		else
739 			c = NULL;
740 
741 		if (addrinfo_add(as, &u.sa, c))
742 			return (-1); /* errno set */
743 	}
744 	return (0);
745 }
746 
747 static int
748 addrinfo_from_pkt(struct async *as, char *pkt, size_t pktlen)
749 {
750 	struct unpack	 p;
751 	struct header	 h;
752 	struct query	 q;
753 	struct rr	 rr;
754 	int		 i;
755 	union {
756 		struct sockaddr		sa;
757 		struct sockaddr_in	sain;
758 		struct sockaddr_in6	sain6;
759 	} u;
760 	char		 buf[MAXDNAME], *c;
761 
762 	asr_unpack_init(&p, pkt, pktlen);
763 	asr_unpack_header(&p, &h);
764 	for (; h.qdcount; h.qdcount--)
765 		asr_unpack_query(&p, &q);
766 
767 	for (i = 0; i < h.ancount; i++) {
768 		asr_unpack_rr(&p, &rr);
769 		if (rr.rr_type != q.q_type ||
770 		    rr.rr_class != q.q_class)
771 			continue;
772 
773 		memset(&u, 0, sizeof u);
774 		if (rr.rr_type == T_A) {
775 			u.sain.sin_len = sizeof u.sain;
776 			u.sain.sin_family = AF_INET;
777 			u.sain.sin_addr = rr.rr.in_a.addr;
778 			u.sain.sin_port = 0;
779 		} else if (rr.rr_type == T_AAAA) {
780 			u.sain6.sin6_len = sizeof u.sain6;
781 			u.sain6.sin6_family = AF_INET6;
782 			u.sain6.sin6_addr = rr.rr.in_aaaa.addr6;
783 			u.sain6.sin6_port = 0;
784 		} else
785 			continue;
786 
787 		if (as->as.ai.hints.ai_flags & AI_CANONNAME) {
788 			asr_strdname(rr.rr_dname, buf, sizeof buf);
789 			buf[strlen(buf) - 1] = '\0';
790 			c = res_hnok(buf) ? buf : NULL;
791 		} else if (as->as.ai.hints.ai_flags & AI_FQDN)
792 			c = as->as.ai.fqdn;
793 		else
794 			c = NULL;
795 
796 		if (addrinfo_add(as, &u.sa, c))
797 			return (-1); /* errno set */
798 	}
799 	return (0);
800 }
801 
802 #ifdef YP
803 static int
804 strsplit(char *line, char **tokens, int ntokens)
805 {
806 	int	ntok;
807 	char	*cp, **tp;
808 
809 	for (cp = line, tp = tokens, ntok = 0;
810 	    ntok < ntokens && (*tp = strsep(&cp, " \t")) != NULL; )
811 		if (**tp != '\0') {
812 			tp++;
813 			ntok++;
814 		}
815 
816 	return (ntok);
817 }
818 
819 static int
820 addrinfo_from_yp(struct async *as, int family, char *line)
821 {
822 	char		*next, *tokens[MAXTOKEN], *c;
823 	int		 ntok;
824 	union {
825 		struct sockaddr		sa;
826 		struct sockaddr_in	sain;
827 		struct sockaddr_in6	sain6;
828 	} u;
829 
830 	for (next = line; line; line = next) {
831 		if ((next = strchr(line, '\n'))) {
832 			*next = '\0';
833 			next += 1;
834 		}
835 		ntok = strsplit(line, tokens, MAXTOKEN);
836 		if (ntok < 2)
837 			continue;
838 
839 		if (asr_sockaddr_from_str(&u.sa, family, tokens[0]) == -1)
840 			continue;
841 
842 		if (as->as.ai.hints.ai_flags & (AI_CANONNAME | AI_FQDN))
843 			c = tokens[1];
844 		else
845 			c = NULL;
846 
847 		if (addrinfo_add(as, &u.sa, c))
848 			return (-1); /* errno set */
849 	}
850 	return (0);
851 }
852 #endif
853