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