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