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