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