xref: /openbsd-src/lib/libc/asr/getaddrinfo_async.c (revision 6a7a3d640d39dae50928bded135d5992d31570e3)
1 /*	$OpenBSD: getaddrinfo_async.c,v 1.18 2013/06/01 14:34:34 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/uio.h>
20 #include <arpa/nameser.h>
21 #ifdef YP
22 #include <rpc/rpc.h>
23 #include <rpcsvc/yp.h>
24 #include <rpcsvc/ypclnt.h>
25 #include "ypinternal.h"
26 #endif
27 
28 #include <err.h>
29 #include <errno.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33 
34 #include "asr.h"
35 #include "asr_private.h"
36 
37 struct match {
38 	int family;
39 	int socktype;
40 	int protocol;
41 };
42 
43 static int getaddrinfo_async_run(struct async *, struct async_res *);
44 static int get_port(const char *, const char *, int);
45 static int iter_family(struct async *, int);
46 static int addrinfo_add(struct async *, const struct sockaddr *, const char *);
47 static int addrinfo_from_file(struct async *, int,  FILE *);
48 static int addrinfo_from_pkt(struct async *, char *, size_t);
49 #ifdef YP
50 static int addrinfo_from_yp(struct async *, int, char *);
51 #endif
52 
53 static const struct match matches[] = {
54 	{ PF_INET,	SOCK_DGRAM,	IPPROTO_UDP	},
55 	{ PF_INET,	SOCK_STREAM,	IPPROTO_TCP	},
56 	{ PF_INET,	SOCK_RAW,	0		},
57 	{ PF_INET6,	SOCK_DGRAM,	IPPROTO_UDP	},
58 	{ PF_INET6,	SOCK_STREAM,	IPPROTO_TCP	},
59 	{ PF_INET6,	SOCK_RAW,	0		},
60 	{ -1,		0,		0,		},
61 };
62 
63 #define MATCH_FAMILY(a, b) ((a) == matches[(b)].family || (a) == PF_UNSPEC)
64 #define MATCH_PROTO(a, b) ((a) == matches[(b)].protocol || (a) == 0 || matches[(b)].protocol == 0)
65 /* Do not match SOCK_RAW unless explicitely specified */
66 #define MATCH_SOCKTYPE(a, b) ((a) == matches[(b)].socktype || ((a) == 0 && \
67 				matches[(b)].socktype != SOCK_RAW))
68 
69 struct async *
70 getaddrinfo_async(const char *hostname, const char *servname,
71 	const struct addrinfo *hints, struct asr *asr)
72 {
73 	struct asr_ctx	*ac;
74 	struct async	*as;
75 
76 	ac = asr_use_resolver(asr);
77 	if ((as = async_new(ac, ASR_GETADDRINFO)) == NULL)
78 		goto abort; /* errno set */
79 	as->as_run = getaddrinfo_async_run;
80 
81 	if (hostname && (as->as.ai.hostname = strdup(hostname)) == NULL)
82 		goto abort; /* errno set */
83 	if (servname && (as->as.ai.servname = strdup(servname)) == NULL)
84 		goto abort; /* errno set */
85 	if (hints)
86 		memmove(&as->as.ai.hints, hints, sizeof *hints);
87 	else {
88 		memset(&as->as.ai.hints, 0, sizeof as->as.ai.hints);
89 		as->as.ai.hints.ai_family = PF_UNSPEC;
90 	}
91 
92 	asr_ctx_unref(ac);
93 	return (as);
94     abort:
95 	if (as)
96 		async_free(as);
97 	asr_ctx_unref(ac);
98 	return (NULL);
99 }
100 
101 static int
102 getaddrinfo_async_run(struct async *as, struct async_res *ar)
103 {
104 #ifdef YP
105 	static char	*domain = NULL;
106 	char		*res;
107 	int		 len;
108 	char		 alias[MAXDNAME], *name;
109 #endif
110 	const char	*str;
111 	struct addrinfo	*ai;
112 	int		 i, family, r;
113 	FILE		*f;
114 	union {
115 		struct sockaddr		sa;
116 		struct sockaddr_in	sain;
117 		struct sockaddr_in6	sain6;
118 	} sa;
119 
120     next:
121 	switch (as->as_state) {
122 
123 	case ASR_STATE_INIT:
124 
125 		/*
126 		 * First, make sure the parameters are valid.
127 		 */
128 
129 		as->as_count = 0;
130 
131 		if (as->as.ai.hostname == NULL &&
132 		    as->as.ai.servname == NULL) {
133 			ar->ar_gai_errno = EAI_NONAME;
134 			async_set_state(as, ASR_STATE_HALT);
135 			break;
136 		}
137 
138 		ai = &as->as.ai.hints;
139 
140 		if (ai->ai_addrlen ||
141 		    ai->ai_canonname ||
142 		    ai->ai_addr ||
143 		    ai->ai_next) {
144 			ar->ar_gai_errno = EAI_BADHINTS;
145 			async_set_state(as, ASR_STATE_HALT);
146 			break;
147 		}
148 
149 		if (ai->ai_flags & ~AI_MASK ||
150 		    (ai->ai_flags & AI_CANONNAME && ai->ai_flags & AI_FQDN)) {
151 			ar->ar_gai_errno = EAI_BADFLAGS;
152 			async_set_state(as, ASR_STATE_HALT);
153 			break;
154 		}
155 
156 		if (ai->ai_family != PF_UNSPEC &&
157 		    ai->ai_family != PF_INET &&
158 		    ai->ai_family != PF_INET6) {
159 			ar->ar_gai_errno = EAI_FAMILY;
160 			async_set_state(as, ASR_STATE_HALT);
161 			break;
162 		}
163 
164 		if (ai->ai_socktype &&
165 		    ai->ai_socktype != SOCK_DGRAM  &&
166 		    ai->ai_socktype != SOCK_STREAM &&
167 		    ai->ai_socktype != SOCK_RAW) {
168 			ar->ar_gai_errno = EAI_SOCKTYPE;
169 			async_set_state(as, ASR_STATE_HALT);
170 			break;
171 		}
172 
173 		if (ai->ai_socktype == SOCK_RAW &&
174 		    get_port(as->as.ai.servname, NULL, 1) != 0) {
175 			ar->ar_gai_errno = EAI_SERVICE;
176 			async_set_state(as, ASR_STATE_HALT);
177 			break;
178 		}
179 
180 		/* Make sure there is at least a valid combination */
181 		for (i = 0; matches[i].family != -1; i++)
182 			if (MATCH_FAMILY(ai->ai_family, i) &&
183 			    MATCH_SOCKTYPE(ai->ai_socktype, i) &&
184 			    MATCH_PROTO(ai->ai_protocol, i))
185 				break;
186 		if (matches[i].family == -1) {
187 			ar->ar_gai_errno = EAI_BADHINTS;
188 			async_set_state(as, ASR_STATE_HALT);
189 			break;
190 		}
191 
192 		if (ai->ai_protocol == 0 || ai->ai_protocol == IPPROTO_UDP)
193 			as->as.ai.port_udp = get_port(as->as.ai.servname, "udp",
194 			    as->as.ai.hints.ai_flags & AI_NUMERICSERV);
195 		if (ai->ai_protocol == 0 || ai->ai_protocol == IPPROTO_TCP)
196 			as->as.ai.port_tcp = get_port(as->as.ai.servname, "tcp",
197 			    as->as.ai.hints.ai_flags & AI_NUMERICSERV);
198 		if (as->as.ai.port_tcp == -2 || as->as.ai.port_udp == -2 ||
199 		    (as->as.ai.port_tcp == -1 && as->as.ai.port_udp == -1) ||
200 		    (ai->ai_protocol && (as->as.ai.port_udp == -1 ||
201 					 as->as.ai.port_tcp == -1))) {
202 			ar->ar_gai_errno = EAI_SERVICE;
203 			async_set_state(as, ASR_STATE_HALT);
204 			break;
205 		}
206 
207 		ar->ar_gai_errno = 0;
208 
209 		/* If hostname is NULL, use local address */
210 		if (as->as.ai.hostname == NULL) {
211 			for (family = iter_family(as, 1);
212 			    family != -1;
213 			    family = iter_family(as, 0)) {
214 				/*
215 				 * We could use statically built sockaddrs for
216 				 * those, rather than parsing over and over.
217 				 */
218 				if (family == PF_INET)
219 					str = (ai->ai_flags & AI_PASSIVE) ? \
220 						"0.0.0.0" : "127.0.0.1";
221 				else /* PF_INET6 */
222 					str = (ai->ai_flags & AI_PASSIVE) ? \
223 						"::" : "::1";
224 				 /* This can't fail */
225 				sockaddr_from_str(&sa.sa, family, str);
226 				if ((r = addrinfo_add(as, &sa.sa, NULL))) {
227 					ar->ar_gai_errno = r;
228 					break;
229 				}
230 			}
231 			if (ar->ar_gai_errno == 0 && as->as_count == 0) {
232 				ar->ar_gai_errno = EAI_NODATA;
233 			}
234 			async_set_state(as, ASR_STATE_HALT);
235 			break;
236 		}
237 
238 		/* Try numeric addresses first */
239 		for (family = iter_family(as, 1);
240 		    family != -1;
241 		    family = iter_family(as, 0)) {
242 
243 			if (sockaddr_from_str(&sa.sa, family,
244 			    as->as.ai.hostname) == -1)
245 				continue;
246 
247 			if ((r = addrinfo_add(as, &sa.sa, NULL)))
248 				ar->ar_gai_errno = r;
249 			break;
250 		}
251 		if (ar->ar_gai_errno || as->as_count) {
252 			async_set_state(as, ASR_STATE_HALT);
253 			break;
254 		}
255 
256 		if (ai->ai_flags & AI_NUMERICHOST) {
257 			ar->ar_gai_errno = EAI_NONAME;
258 			async_set_state(as, ASR_STATE_HALT);
259 			break;
260 		}
261 
262 		async_set_state(as, ASR_STATE_NEXT_DB);
263 		break;
264 
265 	case ASR_STATE_NEXT_DB:
266 		if (asr_iter_db(as) == -1) {
267 			async_set_state(as, ASR_STATE_NOT_FOUND);
268 			break;
269 		}
270 		as->as_family_idx = 0;
271 		async_set_state(as, ASR_STATE_SAME_DB);
272 		break;
273 
274 	case ASR_STATE_NEXT_FAMILY:
275 		as->as_family_idx += 1;
276 		if (as->as.ai.hints.ai_family != AF_UNSPEC ||
277 		    AS_FAMILY(as) == -1) {
278 			/* The family was specified, or we have tried all
279 			 * families with this DB.
280 			 */
281 			if (as->as_count) {
282 				ar->ar_gai_errno = 0;
283 				async_set_state(as, ASR_STATE_HALT);
284 			} else
285 				async_set_state(as, ASR_STATE_NEXT_DB);
286 			break;
287 		}
288 		async_set_state(as, ASR_STATE_SAME_DB);
289 		break;
290 
291 	case ASR_STATE_SAME_DB:
292 		/* query the current DB again */
293 		switch (AS_DB(as)) {
294 		case ASR_DB_DNS:
295 			family = (as->as.ai.hints.ai_family == AF_UNSPEC) ?
296 			    AS_FAMILY(as) : as->as.ai.hints.ai_family;
297 			if (as->as.ai.fqdn) {
298 				as->as.ai.subq = res_query_async_ctx(
299 				    as->as.ai.fqdn, C_IN,
300 				    (family == AF_INET6) ? T_AAAA : T_A,
301 				    as->as_ctx);
302 			}
303 			else {
304 				as->as.ai.subq = res_search_async_ctx(
305 				    as->as.ai.hostname, C_IN,
306 				    (family == AF_INET6) ? T_AAAA : T_A,
307 				    as->as_ctx);
308 			}
309 			if (as->as.ai.subq == NULL) {
310 				if (errno == ENOMEM)
311 					ar->ar_gai_errno = EAI_MEMORY;
312 				else
313 					ar->ar_gai_errno = EAI_FAIL;
314 				async_set_state(as, ASR_STATE_HALT);
315 				break;
316 			}
317 			async_set_state(as, ASR_STATE_SUBQUERY);
318 			break;
319 
320 		case ASR_DB_FILE:
321 			f = fopen(as->as_ctx->ac_hostfile, "r");
322 			if (f == NULL) {
323 				async_set_state(as, ASR_STATE_NEXT_DB);
324 				break;
325 			}
326 			family = (as->as.ai.hints.ai_family == AF_UNSPEC) ?
327 			    AS_FAMILY(as) : as->as.ai.hints.ai_family;
328 
329 			r = addrinfo_from_file(as, family, f);
330 			if (r == -1) {
331 				if (errno == ENOMEM)
332 					ar->ar_gai_errno = EAI_MEMORY;
333 				else
334 					ar->ar_gai_errno = EAI_FAIL;
335 				async_set_state(as, ASR_STATE_HALT);
336 			} else
337 				async_set_state(as, ASR_STATE_NEXT_FAMILY);
338 			fclose(f);
339 			break;
340 
341 #ifdef YP
342 		case ASR_DB_YP:
343 			if (!domain && _yp_check(&domain) == 0) {
344 				async_set_state(as, ASR_STATE_NEXT_DB);
345 				break;
346 			}
347 			family = (as->as.ai.hints.ai_family == AF_UNSPEC) ?
348 			    AS_FAMILY(as) : as->as.ai.hints.ai_family;
349 
350 			name = asr_hostalias(as->as_ctx, as->as.ai.hostname,
351 			    alias, sizeof(alias));
352 			if (name == NULL)
353 				name = as->as.ai.hostname;
354 
355 			/* XXX
356 			 * ipnodes.byname could also contain IPv4 address
357 			 */
358 			r = yp_match(domain, (family == AF_INET6) ?
359 			    "ipnodes.byname" : "hosts.byname",
360 			    name, strlen(name), &res, &len);
361 			if (r == 0) {
362 				r = addrinfo_from_yp(as, family, res);
363 				free(res);
364 				if (r == -1) {
365 					if (errno == ENOMEM)
366 						ar->ar_gai_errno = EAI_MEMORY;
367 					else
368 						ar->ar_gai_errno = EAI_FAIL;
369 					async_set_state(as, ASR_STATE_HALT);
370 					break;
371 				}
372 			}
373 			async_set_state(as, ASR_STATE_NEXT_FAMILY);
374 			break;
375 #endif
376 		default:
377 			async_set_state(as, ASR_STATE_NEXT_DB);
378 		}
379 		break;
380 
381 	case ASR_STATE_SUBQUERY:
382 		if ((r = async_run(as->as.ai.subq, ar)) == ASYNC_COND)
383 			return (ASYNC_COND);
384 		as->as.ai.subq = NULL;
385 
386 		if (ar->ar_datalen == -1) {
387 			async_set_state(as, ASR_STATE_NEXT_FAMILY);
388 			break;
389 		}
390 
391 		r = addrinfo_from_pkt(as, ar->ar_data, ar->ar_datalen);
392 		if (r == -1) {
393 			if (errno == ENOMEM)
394 				ar->ar_gai_errno = EAI_MEMORY;
395 			else
396 				ar->ar_gai_errno = EAI_FAIL;
397 			async_set_state(as, ASR_STATE_HALT);
398 		} else
399 			async_set_state(as, ASR_STATE_NEXT_FAMILY);
400 		free(ar->ar_data);
401 		break;
402 
403 	case ASR_STATE_NOT_FOUND:
404 		/* No result found. Maybe we can try again. */
405 		if (as->as.ai.flags & ASYNC_AGAIN)
406 			ar->ar_gai_errno = EAI_AGAIN;
407 		else
408 			ar->ar_gai_errno = EAI_NODATA;
409 		async_set_state(as, ASR_STATE_HALT);
410 		break;
411 
412 	case ASR_STATE_HALT:
413 		if (ar->ar_gai_errno == 0) {
414 			ar->ar_count = as->as_count;
415 			ar->ar_addrinfo = as->as.ai.aifirst;
416 			as->as.ai.aifirst = NULL;
417 		} else {
418 			ar->ar_count = 0;
419 			ar->ar_addrinfo = NULL;
420 		}
421 		return (ASYNC_DONE);
422 
423 	default:
424 		ar->ar_errno = EOPNOTSUPP;
425 		ar->ar_gai_errno = EAI_SYSTEM;
426 		async_set_state(as, ASR_STATE_HALT);
427 		break;
428 	}
429 	goto next;
430 }
431 
432 /*
433  * Retreive the port number for the service name "servname" and
434  * the protocol "proto".
435  */
436 static int
437 get_port(const char *servname, const char *proto, int numonly)
438 {
439 	struct servent		se;
440 	struct servent_data	sed;
441 	int			port, r;
442 	const char		*e;
443 
444 	if (servname == NULL)
445 		return (0);
446 
447 	e = NULL;
448 	port = strtonum(servname, 0, USHRT_MAX, &e);
449 	if (e == NULL)
450 		return (port);
451 	if (errno == ERANGE)
452 		return (-2); /* invalid */
453 	if (numonly)
454 		return (-2);
455 
456 	memset(&sed, 0, sizeof(sed));
457 	r = getservbyname_r(servname, proto, &se, &sed);
458 	port = ntohs(se.s_port);
459 	endservent_r(&sed);
460 
461 	if (r == -1)
462 		return (-1); /* not found */
463 
464 	return (port);
465 }
466 
467 /*
468  * Iterate over the address families that are to be queried. Use the
469  * list on the async context, unless a specific family was given in hints.
470  */
471 static int
472 iter_family(struct async *as, int first)
473 {
474 	if (first) {
475 		as->as_family_idx = 0;
476 		if (as->as.ai.hints.ai_family != PF_UNSPEC)
477 			return as->as.ai.hints.ai_family;
478 		return AS_FAMILY(as);
479 	}
480 
481 	if (as->as.ai.hints.ai_family != PF_UNSPEC)
482 		return (-1);
483 
484 	as->as_family_idx++;
485 
486 	return AS_FAMILY(as);
487 }
488 
489 /*
490  * Use the sockaddr at "sa" to extend the result list on the "as" context,
491  * with the specified canonical name "cname". This function adds one
492  * entry per protocol/socktype match.
493  */
494 static int
495 addrinfo_add(struct async *as, const struct sockaddr *sa, const char *cname)
496 {
497 	struct addrinfo		*ai;
498 	int			 i, port, proto;
499 
500 	for (i = 0; matches[i].family != -1; i++) {
501 		if (matches[i].family != sa->sa_family ||
502 		    !MATCH_SOCKTYPE(as->as.ai.hints.ai_socktype, i) ||
503 		    !MATCH_PROTO(as->as.ai.hints.ai_protocol, i))
504 			continue;
505 
506 		proto = as->as.ai.hints.ai_protocol;
507 		if (!proto)
508 			proto = matches[i].protocol;
509 
510 		if (proto == IPPROTO_TCP)
511 			port = as->as.ai.port_tcp;
512 		else if (proto == IPPROTO_UDP)
513 			port = as->as.ai.port_udp;
514 		else
515 			port = 0;
516 
517 		/* servname specified, but not defined for this protocol */
518 		if (port == -1)
519 			continue;
520 
521 		ai = calloc(1, sizeof(*ai) + sa->sa_len);
522 		if (ai == NULL)
523 			return (EAI_MEMORY);
524 		ai->ai_family = sa->sa_family;
525 		ai->ai_socktype = matches[i].socktype;
526 		ai->ai_protocol = proto;
527 		ai->ai_flags = as->as.ai.hints.ai_flags;
528 		ai->ai_addrlen = sa->sa_len;
529 		ai->ai_addr = (void *)(ai + 1);
530 		if (cname &&
531 		    as->as.ai.hints.ai_flags & (AI_CANONNAME | AI_FQDN)) {
532 			if ((ai->ai_canonname = strdup(cname)) == NULL) {
533 				free(ai);
534 				return (EAI_MEMORY);
535 			}
536 		}
537 		memmove(ai->ai_addr, sa, sa->sa_len);
538 		if (sa->sa_family == PF_INET)
539 			((struct sockaddr_in *)ai->ai_addr)->sin_port =
540 			    htons(port);
541 		else if (sa->sa_family == PF_INET6)
542 			((struct sockaddr_in6 *)ai->ai_addr)->sin6_port =
543 			    htons(port);
544 
545 		if (as->as.ai.aifirst == NULL)
546 			as->as.ai.aifirst = ai;
547 		if (as->as.ai.ailast)
548 			as->as.ai.ailast->ai_next = ai;
549 		as->as.ai.ailast = ai;
550 		as->as_count += 1;
551 	}
552 
553 	return (0);
554 }
555 
556 static int
557 addrinfo_from_file(struct async *as, int family, FILE *f)
558 {
559 	char		*tokens[MAXTOKEN], buf[MAXDNAME], *name, *c;
560 	int		 n, i;
561 	union {
562 		struct sockaddr		sa;
563 		struct sockaddr_in	sain;
564 		struct sockaddr_in6	sain6;
565 	} u;
566 
567 	name = asr_hostalias(as->as_ctx, as->as.ai.hostname, buf, sizeof(buf));
568 	if (name == NULL)
569 		name = as->as.ai.hostname;
570 
571 	for (;;) {
572 		n = asr_parse_namedb_line(f, tokens, MAXTOKEN);
573 		if (n == -1)
574 			break; /* ignore errors reading the file */
575 
576 		for (i = 1; i < n; i++) {
577 			if (strcasecmp(name, tokens[i]))
578 				continue;
579 			if (sockaddr_from_str(&u.sa, family, tokens[0]) == -1)
580 				continue;
581 			break;
582 		}
583 		if (i == n)
584 			continue;
585 
586 		if (as->as.ai.hints.ai_flags & (AI_CANONNAME | AI_FQDN))
587 			c = tokens[1];
588 		else
589 			c = NULL;
590 
591 		if (addrinfo_add(as, &u.sa, c))
592 			return (-1); /* errno set */
593 	}
594 	return (0);
595 }
596 
597 static int
598 addrinfo_from_pkt(struct async *as, char *pkt, size_t pktlen)
599 {
600 	struct unpack	 p;
601 	struct header	 h;
602 	struct query	 q;
603 	struct rr	 rr;
604 	int		 i;
605 	union {
606 		struct sockaddr		sa;
607 		struct sockaddr_in	sain;
608 		struct sockaddr_in6	sain6;
609 	} u;
610 	char		 buf[MAXDNAME], *c;
611 
612 	unpack_init(&p, pkt, pktlen);
613 	unpack_header(&p, &h);
614 	for (; h.qdcount; h.qdcount--)
615 		unpack_query(&p, &q);
616 
617 	for (i = 0; i < h.ancount; i++) {
618 		unpack_rr(&p, &rr);
619 		if (rr.rr_type != q.q_type ||
620 		    rr.rr_class != q.q_class)
621 			continue;
622 
623 		if (as->as.ai.fqdn == NULL) {
624 			asr_strdname(q.q_dname, buf, sizeof buf);
625 			buf[strlen(buf) - 1] = '\0';
626 			as->as.ai.fqdn = strdup(buf);
627 			if (as->as.ai.fqdn == NULL)
628 				return (-1); /* errno set */
629 		}
630 
631 		memset(&u, 0, sizeof u);
632 		if (rr.rr_type == T_A) {
633 			u.sain.sin_len = sizeof u.sain;
634 			u.sain.sin_family = AF_INET;
635 			u.sain.sin_addr = rr.rr.in_a.addr;
636 			u.sain.sin_port = 0;
637 		} else if (rr.rr_type == T_AAAA) {
638 			u.sain6.sin6_len = sizeof u.sain6;
639 			u.sain6.sin6_family = AF_INET6;
640 			u.sain6.sin6_addr = rr.rr.in_aaaa.addr6;
641 			u.sain6.sin6_port = 0;
642 		} else
643 			continue;
644 
645 		if (as->as.ai.hints.ai_flags & AI_CANONNAME) {
646 			asr_strdname(rr.rr_dname, buf, sizeof buf);
647 			buf[strlen(buf) - 1] = '\0';
648 			c = buf;
649 		} else if (as->as.ai.hints.ai_flags & AI_FQDN)
650 			c = as->as.ai.fqdn;
651 		else
652 			c = NULL;
653 
654 		if (addrinfo_add(as, &u.sa, c))
655 			return (-1); /* errno set */
656 	}
657 	return (0);
658 }
659 
660 #ifdef YP
661 static int
662 strsplit(char *line, char **tokens, int ntokens)
663 {
664 	int	ntok;
665 	char	*cp, **tp;
666 
667 	for (cp = line, tp = tokens, ntok = 0;
668 	    ntok < ntokens && (*tp = strsep(&cp, " \t")) != NULL; )
669 		if (**tp != '\0') {
670 			tp++;
671 			ntok++;
672 		}
673 
674 	return (ntok);
675 }
676 
677 static int
678 addrinfo_from_yp(struct async *as, int family, char *line)
679 {
680 	char		*next, *tokens[MAXTOKEN], *c;
681 	int		 ntok;
682 	union {
683 		struct sockaddr		sa;
684 		struct sockaddr_in	sain;
685 		struct sockaddr_in6	sain6;
686 	} u;
687 
688 	for (next = line; line; line = next) {
689 		if ((next = strchr(line, '\n'))) {
690 			*next = '\0';
691 			next += 1;
692 		}
693 		ntok = strsplit(line, tokens, MAXTOKEN);
694 		if (ntok < 2)
695 			continue;
696 
697 		if (sockaddr_from_str(&u.sa, family, tokens[0]) == -1)
698 			continue;
699 
700 		if (as->as.ai.hints.ai_flags & (AI_CANONNAME | AI_FQDN))
701 			c = tokens[1];
702 		else
703 			c = NULL;
704 
705 		if (addrinfo_add(as, &u.sa, c))
706 			return (-1); /* errno set */
707 	}
708 	return (0);
709 }
710 #endif
711