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