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