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