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