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