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