xref: /openbsd-src/lib/libc/asr/getaddrinfo_async.c (revision a55a423af49ffc58174bbeae564544af69217dcc)
1 /*	$OpenBSD: getaddrinfo_async.c,v 1.45 2015/09/20 14:19:21 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 
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 <errno.h>
28 #include <ifaddrs.h>
29 #include <resolv.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <limits.h>
34 
35 #ifdef YP
36 #include <rpc/rpc.h>
37 #include <rpcsvc/yp.h>
38 #include <rpcsvc/ypclnt.h>
39 #include "ypinternal.h"
40 #endif
41 
42 #include "asr_private.h"
43 
44 struct match {
45 	int family;
46 	int socktype;
47 	int protocol;
48 };
49 
50 static int getaddrinfo_async_run(struct asr_query *, struct asr_result *);
51 static int get_port(const char *, const char *, int);
52 static int iter_family(struct asr_query *, int);
53 static int addrinfo_add(struct asr_query *, const struct sockaddr *, const char *);
54 static int addrinfo_from_file(struct asr_query *, int,  FILE *);
55 static int addrinfo_from_pkt(struct asr_query *, char *, size_t);
56 static int addrconfig_setup(struct asr_query *);
57 #ifdef YP
58 static int addrinfo_from_yp(struct asr_query *, int, char *);
59 #endif
60 
61 static const struct match matches[] = {
62 	{ PF_INET,	SOCK_DGRAM,	IPPROTO_UDP	},
63 	{ PF_INET,	SOCK_STREAM,	IPPROTO_TCP	},
64 	{ PF_INET,	SOCK_RAW,	0		},
65 	{ PF_INET6,	SOCK_DGRAM,	IPPROTO_UDP	},
66 	{ PF_INET6,	SOCK_STREAM,	IPPROTO_TCP	},
67 	{ PF_INET6,	SOCK_RAW,	0		},
68 	{ -1,		0,		0,		},
69 };
70 
71 #define MATCH_FAMILY(a, b) ((a) == matches[(b)].family || (a) == PF_UNSPEC)
72 #define MATCH_PROTO(a, b) ((a) == matches[(b)].protocol || (a) == 0 || matches[(b)].protocol == 0)
73 /* Do not match SOCK_RAW unless explicitely specified */
74 #define MATCH_SOCKTYPE(a, b) ((a) == matches[(b)].socktype || ((a) == 0 && \
75 				matches[(b)].socktype != SOCK_RAW))
76 
77 enum {
78 	DOM_INIT,
79 	DOM_DOMAIN,
80 	DOM_DONE
81 };
82 
83 struct asr_query *
84 getaddrinfo_async(const char *hostname, const char *servname,
85 	const struct addrinfo *hints, void *asr)
86 {
87 	struct asr_ctx		*ac;
88 	struct asr_query	*as;
89 	char			 alias[MAXDNAME];
90 
91 	ac = _asr_use_resolver(asr);
92 	if ((as = _asr_async_new(ac, ASR_GETADDRINFO)) == NULL)
93 		goto abort; /* errno set */
94 	as->as_run = getaddrinfo_async_run;
95 
96 	if (hostname) {
97 		if (_asr_hostalias(ac, hostname, alias, sizeof(alias)))
98 			hostname = alias;
99 		if ((as->as.ai.hostname = strdup(hostname)) == NULL)
100 			goto abort; /* errno set */
101 	}
102 	if (servname && (as->as.ai.servname = strdup(servname)) == NULL)
103 		goto abort; /* errno set */
104 	if (hints)
105 		memmove(&as->as.ai.hints, hints, sizeof *hints);
106 	else {
107 		memset(&as->as.ai.hints, 0, sizeof as->as.ai.hints);
108 		as->as.ai.hints.ai_family = PF_UNSPEC;
109 		as->as.ai.hints.ai_flags = AI_ADDRCONFIG;
110 	}
111 
112 	_asr_ctx_unref(ac);
113 	return (as);
114     abort:
115 	if (as)
116 		_asr_async_free(as);
117 	_asr_ctx_unref(ac);
118 	return (NULL);
119 }
120 DEF_WEAK(getaddrinfo_async);
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  * Use the sockaddr at "sa" to extend the result list on the "as" context,
566  * with the specified canonical name "cname". This function adds one
567  * entry per protocol/socktype match.
568  */
569 static int
570 addrinfo_add(struct asr_query *as, const struct sockaddr *sa, const char *cname)
571 {
572 	struct addrinfo		*ai;
573 	int			 i, port, proto;
574 
575 	for (i = 0; matches[i].family != -1; i++) {
576 		if (matches[i].family != sa->sa_family ||
577 		    !MATCH_SOCKTYPE(as->as.ai.hints.ai_socktype, i) ||
578 		    !MATCH_PROTO(as->as.ai.hints.ai_protocol, i))
579 			continue;
580 
581 		proto = as->as.ai.hints.ai_protocol;
582 		if (!proto)
583 			proto = matches[i].protocol;
584 
585 		if (proto == IPPROTO_TCP)
586 			port = as->as.ai.port_tcp;
587 		else if (proto == IPPROTO_UDP)
588 			port = as->as.ai.port_udp;
589 		else
590 			port = 0;
591 
592 		/* servname specified, but not defined for this protocol */
593 		if (port == -1)
594 			continue;
595 
596 		ai = calloc(1, sizeof(*ai) + sa->sa_len);
597 		if (ai == NULL)
598 			return (EAI_MEMORY);
599 		ai->ai_family = sa->sa_family;
600 		ai->ai_socktype = matches[i].socktype;
601 		ai->ai_protocol = proto;
602 		ai->ai_flags = as->as.ai.hints.ai_flags;
603 		ai->ai_addrlen = sa->sa_len;
604 		ai->ai_addr = (void *)(ai + 1);
605 		if (cname &&
606 		    as->as.ai.hints.ai_flags & (AI_CANONNAME | AI_FQDN)) {
607 			if ((ai->ai_canonname = strdup(cname)) == NULL) {
608 				free(ai);
609 				return (EAI_MEMORY);
610 			}
611 		}
612 		memmove(ai->ai_addr, sa, sa->sa_len);
613 		if (sa->sa_family == PF_INET)
614 			((struct sockaddr_in *)ai->ai_addr)->sin_port =
615 			    htons(port);
616 		else if (sa->sa_family == PF_INET6)
617 			((struct sockaddr_in6 *)ai->ai_addr)->sin6_port =
618 			    htons(port);
619 
620 		if (as->as.ai.aifirst == NULL)
621 			as->as.ai.aifirst = ai;
622 		if (as->as.ai.ailast)
623 			as->as.ai.ailast->ai_next = ai;
624 		as->as.ai.ailast = ai;
625 		as->as_count += 1;
626 	}
627 
628 	return (0);
629 }
630 
631 static int
632 addrinfo_from_file(struct asr_query *as, int family, FILE *f)
633 {
634 	char		*tokens[MAXTOKEN], *c, buf[BUFSIZ + 1];
635 	int		 n, i;
636 	union {
637 		struct sockaddr		sa;
638 		struct sockaddr_in	sain;
639 		struct sockaddr_in6	sain6;
640 	} u;
641 
642 	for (;;) {
643 		n = _asr_parse_namedb_line(f, tokens, MAXTOKEN, buf, sizeof(buf));
644 		if (n == -1)
645 			break; /* ignore errors reading the file */
646 
647 		for (i = 1; i < n; i++) {
648 			if (strcasecmp(as->as.ai.hostname, tokens[i]))
649 				continue;
650 			if (_asr_sockaddr_from_str(&u.sa, family, tokens[0]) == -1)
651 				continue;
652 			break;
653 		}
654 		if (i == n)
655 			continue;
656 
657 		if (as->as.ai.hints.ai_flags & (AI_CANONNAME | AI_FQDN))
658 			c = tokens[1];
659 		else
660 			c = NULL;
661 
662 		if (addrinfo_add(as, &u.sa, c))
663 			return (-1); /* errno set */
664 	}
665 	return (0);
666 }
667 
668 static int
669 addrinfo_from_pkt(struct asr_query *as, char *pkt, size_t pktlen)
670 {
671 	struct asr_unpack	 p;
672 	struct asr_dns_header	 h;
673 	struct asr_dns_query	 q;
674 	struct asr_dns_rr	 rr;
675 	int			 i;
676 	union {
677 		struct sockaddr		sa;
678 		struct sockaddr_in	sain;
679 		struct sockaddr_in6	sain6;
680 	} u;
681 	char		 buf[MAXDNAME], *c;
682 
683 	_asr_unpack_init(&p, pkt, pktlen);
684 	_asr_unpack_header(&p, &h);
685 	for (; h.qdcount; h.qdcount--)
686 		_asr_unpack_query(&p, &q);
687 
688 	for (i = 0; i < h.ancount; i++) {
689 		_asr_unpack_rr(&p, &rr);
690 		if (rr.rr_type != q.q_type ||
691 		    rr.rr_class != q.q_class)
692 			continue;
693 
694 		memset(&u, 0, sizeof u);
695 		if (rr.rr_type == T_A) {
696 			u.sain.sin_len = sizeof u.sain;
697 			u.sain.sin_family = AF_INET;
698 			u.sain.sin_addr = rr.rr.in_a.addr;
699 			u.sain.sin_port = 0;
700 		} else if (rr.rr_type == T_AAAA) {
701 			u.sain6.sin6_len = sizeof u.sain6;
702 			u.sain6.sin6_family = AF_INET6;
703 			u.sain6.sin6_addr = rr.rr.in_aaaa.addr6;
704 			u.sain6.sin6_port = 0;
705 		} else
706 			continue;
707 
708 		if (as->as.ai.hints.ai_flags & AI_CANONNAME) {
709 			_asr_strdname(rr.rr_dname, buf, sizeof buf);
710 			buf[strlen(buf) - 1] = '\0';
711 			c = res_hnok(buf) ? buf : NULL;
712 		} else if (as->as.ai.hints.ai_flags & AI_FQDN)
713 			c = as->as.ai.fqdn;
714 		else
715 			c = NULL;
716 
717 		if (addrinfo_add(as, &u.sa, c))
718 			return (-1); /* errno set */
719 	}
720 	return (0);
721 }
722 
723 static int
724 addrconfig_setup(struct asr_query *as)
725 {
726 	struct ifaddrs		*ifa, *ifa0;
727 	struct sockaddr_in	*sinp;
728 	struct sockaddr_in6	*sin6p;
729 
730 	if (getifaddrs(&ifa0) != 0)
731 		return (-1);
732 
733 	as->as.ai.flags |= ASYNC_NO_INET | ASYNC_NO_INET6;
734 
735 	for (ifa = ifa0; ifa != NULL; ifa = ifa->ifa_next) {
736 		if (ifa->ifa_addr == NULL)
737 			continue;
738 
739 		switch (ifa->ifa_addr->sa_family) {
740 		case PF_INET:
741 			sinp = (struct sockaddr_in *)ifa->ifa_addr;
742 
743 			if (sinp->sin_addr.s_addr == htonl(INADDR_LOOPBACK))
744 				continue;
745 
746 			as->as.ai.flags &= ~ASYNC_NO_INET;
747 			break;
748 		case PF_INET6:
749 			sin6p = (struct sockaddr_in6 *)ifa->ifa_addr;
750 
751 			if (IN6_IS_ADDR_LOOPBACK(&sin6p->sin6_addr))
752 				continue;
753 
754 			if (IN6_IS_ADDR_LINKLOCAL(&sin6p->sin6_addr))
755 				continue;
756 
757 			as->as.ai.flags &= ~ASYNC_NO_INET6;
758 			break;
759 		}
760 	}
761 
762 	freeifaddrs(ifa0);
763 
764 	return (0);
765 }
766 
767 #ifdef YP
768 static int
769 strsplit(char *line, char **tokens, int ntokens)
770 {
771 	int	ntok;
772 	char	*cp, **tp;
773 
774 	for (cp = line, tp = tokens, ntok = 0;
775 	    ntok < ntokens && (*tp = strsep(&cp, " \t")) != NULL; )
776 		if (**tp != '\0') {
777 			tp++;
778 			ntok++;
779 		}
780 
781 	return (ntok);
782 }
783 
784 static int
785 addrinfo_from_yp(struct asr_query *as, int family, char *line)
786 {
787 	char		*next, *tokens[MAXTOKEN], *c;
788 	int		 ntok;
789 	union {
790 		struct sockaddr		sa;
791 		struct sockaddr_in	sain;
792 		struct sockaddr_in6	sain6;
793 	} u;
794 
795 	for (next = line; line; line = next) {
796 		if ((next = strchr(line, '\n'))) {
797 			*next = '\0';
798 			next += 1;
799 		}
800 		ntok = strsplit(line, tokens, MAXTOKEN);
801 		if (ntok < 2)
802 			continue;
803 
804 		if (_asr_sockaddr_from_str(&u.sa, family, tokens[0]) == -1)
805 			continue;
806 
807 		if (as->as.ai.hints.ai_flags & (AI_CANONNAME | AI_FQDN))
808 			c = tokens[1];
809 		else
810 			c = NULL;
811 
812 		if (addrinfo_add(as, &u.sa, c))
813 			return (-1); /* errno set */
814 	}
815 	return (0);
816 }
817 #endif
818