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