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