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