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