xref: /openbsd-src/lib/libc/asr/gethostnamadr_async.c (revision cd22283f337d6c4f570980c1a75a3e0c4576d0fa)
1 /*	$OpenBSD: gethostnamadr_async.c,v 1.11 2012/11/24 18:58:49 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/socket.h>
20 #include <netinet/in.h>
21 #include <arpa/inet.h>
22 #include <arpa/nameser.h>
23 #ifdef YP
24 #include <rpc/rpc.h>
25 #include <rpcsvc/yp.h>
26 #include <rpcsvc/ypclnt.h>
27 #include "ypinternal.h"
28 #endif
29 
30 #include <err.h>
31 #include <errno.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <unistd.h>
35 
36 #include "asr.h"
37 #include "asr_private.h"
38 
39 #define MAXALIASES	16
40 #define MAXADDRS	16
41 
42 struct hostent_ext {
43 	struct hostent	 h;
44 	char		*aliases[MAXALIASES + 1];
45 	char		*addrs[MAXADDRS + 1];
46 	char		*end;
47 	char		*pos;
48 };
49 
50 ssize_t addr_as_fqdn(const char *, int, char *, size_t);
51 
52 static int gethostnamadr_async_run(struct async *, struct async_res *);
53 static struct hostent_ext *hostent_alloc(int);
54 static int hostent_set_cname(struct hostent_ext *, const char *, int);
55 static int hostent_add_alias(struct hostent_ext *, const char *, int);
56 static int hostent_add_addr(struct hostent_ext *, const void *, size_t);
57 static struct hostent_ext *hostent_file_match(FILE *, int, int, const char *,
58     int);
59 static struct hostent_ext *hostent_from_packet(int, int, char *, size_t);
60 #ifdef YP
61 static struct hostent_ext *_yp_gethostnamadr(int, const void *);
62 static struct hostent_ext *hostent_from_yp(int, char *);
63 #endif
64 
65 struct async *
66 gethostbyname_async(const char *name, struct asr *asr)
67 {
68 	return gethostbyname2_async(name, AF_INET, asr);
69 }
70 
71 struct async *
72 gethostbyname2_async(const char *name, int af, struct asr *asr)
73 {
74 	struct asr_ctx	*ac;
75 	struct async	*as;
76 
77 	/* the original segfaults */
78 	if (name == NULL) {
79 		errno = EINVAL;
80 		return (NULL);
81 	}
82 
83 	ac = asr_use_resolver(asr);
84 	if ((as = async_new(ac, ASR_GETHOSTBYNAME)) == NULL)
85 		goto abort; /* errno set */
86 	as->as_run = gethostnamadr_async_run;
87 
88 	as->as.hostnamadr.family = af;
89 	if (af == AF_INET)
90 		as->as.hostnamadr.addrlen = INADDRSZ;
91 	else if (af == AF_INET6)
92 		as->as.hostnamadr.addrlen = IN6ADDRSZ;
93 	as->as.hostnamadr.name = strdup(name);
94 	if (as->as.hostnamadr.name == NULL)
95 		goto abort; /* errno set */
96 
97 	asr_ctx_unref(ac);
98 	return (as);
99 
100     abort:
101 	if (as)
102 		async_free(as);
103 	asr_ctx_unref(ac);
104 	return (NULL);
105 }
106 
107 struct async *
108 gethostbyaddr_async(const void *addr, socklen_t len, int af, struct asr *asr)
109 {
110 	struct asr_ctx	*ac;
111 	struct async	*as;
112 
113 	ac = asr_use_resolver(asr);
114 	as = gethostbyaddr_async_ctx(addr, len, af, ac);
115 	asr_ctx_unref(ac);
116 
117 	return (as);
118 }
119 
120 struct async *
121 gethostbyaddr_async_ctx(const void *addr, socklen_t len, int af,
122     struct asr_ctx *ac)
123 {
124 	struct async	*as;
125 
126 	if ((as = async_new(ac, ASR_GETHOSTBYADDR)) == NULL)
127 		goto abort; /* errno set */
128 	as->as_run = gethostnamadr_async_run;
129 
130 	as->as.hostnamadr.family = af;
131 	as->as.hostnamadr.addrlen = len;
132 	if (len > 0)
133 		memmove(as->as.hostnamadr.addr, addr, (len > 16) ? 16 : len);
134 
135 	return (as);
136 
137     abort:
138 	if (as)
139 		async_free(as);
140 	return (NULL);
141 }
142 
143 static int
144 gethostnamadr_async_run(struct async *as, struct async_res *ar)
145 {
146 	struct hostent_ext	*h;
147 	int			 r, type, saved_errno;
148 	FILE			*f;
149 	char			 dname[MAXDNAME], *data;
150 
151     next:
152 	switch (as->as_state) {
153 
154 	case ASR_STATE_INIT:
155 
156 		if (as->as.hostnamadr.family != AF_INET &&
157 		    as->as.hostnamadr.family != AF_INET6) {
158 			ar->ar_h_errno = NETDB_INTERNAL;
159 			ar->ar_errno = EAFNOSUPPORT;
160 			async_set_state(as, ASR_STATE_HALT);
161 			break;
162 		}
163 
164 		if ((as->as.hostnamadr.family == AF_INET &&
165 		     as->as.hostnamadr.addrlen != INADDRSZ) ||
166 		    (as->as.hostnamadr.family == AF_INET6 &&
167 		     as->as.hostnamadr.addrlen != IN6ADDRSZ)) {
168 			ar->ar_h_errno = NETDB_INTERNAL;
169 			ar->ar_errno = EINVAL;
170 			async_set_state(as, ASR_STATE_HALT);
171 			break;
172 		}
173 
174 		if (as->as_type == ASR_GETHOSTBYNAME)
175 			async_set_state(as, ASR_STATE_NEXT_DOMAIN);
176 		else
177 			async_set_state(as, ASR_STATE_NEXT_DB);
178 		break;
179 
180 	case ASR_STATE_NEXT_DOMAIN:
181 
182 		r = asr_iter_domain(as, as->as.hostnamadr.name, dname,
183 		    sizeof(dname));
184 		if (r == -1) {
185 			async_set_state(as, ASR_STATE_NOT_FOUND);
186 			break;
187 		}
188 
189 		if (as->as.hostnamadr.dname)
190 			free(as->as.hostnamadr.dname);
191 		if ((as->as.hostnamadr.dname = strdup(dname)) == NULL) {
192 			ar->ar_h_errno = NETDB_INTERNAL;
193 			ar->ar_errno = errno;
194 			async_set_state(as, ASR_STATE_HALT);
195 		}
196 
197 		as->as_db_idx = 0;
198 		async_set_state(as, ASR_STATE_NEXT_DB);
199 		break;
200 
201 	case ASR_STATE_NEXT_DB:
202 
203 		if (asr_iter_db(as) == -1) {
204 			if (as->as_type == ASR_GETHOSTBYNAME)
205 				async_set_state(as, ASR_STATE_NEXT_DOMAIN);
206 			else
207 				async_set_state(as, ASR_STATE_NOT_FOUND);
208 			break;
209 		}
210 
211 		switch (AS_DB(as)) {
212 
213 		case ASR_DB_DNS:
214 
215 			/* Create a subquery to do the DNS lookup */
216 
217 			if (as->as_type == ASR_GETHOSTBYNAME) {
218 				type = (as->as.hostnamadr.family == AF_INET) ?
219 				    T_A : T_AAAA;
220 				as->as.hostnamadr.subq = res_query_async_ctx(
221 				    as->as.hostnamadr.dname,
222 				    C_IN, type, NULL, 0, as->as_ctx);
223 			} else {
224 				addr_as_fqdn(as->as.hostnamadr.addr,
225 				    as->as.hostnamadr.family,
226 				    dname, sizeof(dname));
227 				as->as.hostnamadr.subq = res_query_async_ctx(
228 				    dname, C_IN, T_PTR, NULL, 0, as->as_ctx);
229 			}
230 
231 			if (as->as.hostnamadr.subq == NULL) {
232 				ar->ar_errno = errno;
233 				ar->ar_h_errno = NETDB_INTERNAL;
234 				async_set_state(as, ASR_STATE_HALT);
235 				break;
236 			}
237 
238 			async_set_state(as, ASR_STATE_SUBQUERY);
239 			break;
240 
241 		case ASR_DB_FILE:
242 
243 			/* Try to find a match in the host file */
244 
245 			if ((f = fopen(as->as_ctx->ac_hostfile, "r")) == NULL)
246 				break;
247 
248 			if (as->as_type == ASR_GETHOSTBYNAME)
249 				data = as->as.hostnamadr.dname;
250 			else
251 				data = as->as.hostnamadr.addr;
252 
253 			h = hostent_file_match(f, as->as_type,
254 			    as->as.hostnamadr.family, data,
255 			    as->as.hostnamadr.addrlen);
256 			saved_errno = errno;
257 			fclose(f);
258 			errno = saved_errno;
259 
260 			if (h == NULL) {
261 				if (errno) {
262 					ar->ar_errno = errno;
263 					ar->ar_h_errno = NETDB_INTERNAL;
264 					async_set_state(as, ASR_STATE_HALT);
265 				}
266 				/* otherwise not found */
267 				break;
268 			}
269 			ar->ar_hostent = &h->h;
270 			ar->ar_h_errno = NETDB_SUCCESS;
271 			async_set_state(as, ASR_STATE_HALT);
272 			break;
273 #ifdef YP
274 		case ASR_DB_YP:
275 			/* IPv4 only */
276 			if (as->as.hostnamadr.family != AF_INET)
277 				break;
278 			if (as->as_type == ASR_GETHOSTBYNAME)
279 				data = as->as.hostnamadr.dname;
280 			else
281 				data = as->as.hostnamadr.addr;
282 			h = _yp_gethostnamadr(as->as_type, data);
283 			if (h == NULL) {
284 				if (errno) {
285 					ar->ar_errno = errno;
286 					ar->ar_h_errno = NETDB_INTERNAL;
287 					async_set_state(as, ASR_STATE_HALT);
288 				}
289 				/* otherwise not found */
290 				break;
291 			}
292 			ar->ar_hostent = &h->h;
293 			ar->ar_h_errno = NETDB_SUCCESS;
294 			async_set_state(as, ASR_STATE_HALT);
295 			break;
296 #endif
297 		}
298 		break;
299 
300 	case ASR_STATE_SUBQUERY:
301 
302 		/* Run the DNS subquery. */
303 
304 		if ((r = async_run(as->as.hostnamadr.subq, ar)) == ASYNC_COND)
305 			return (ASYNC_COND);
306 
307 		/* Done. */
308 		as->as.hostnamadr.subq = NULL;
309 
310 		if (ar->ar_datalen == -1) {
311 			async_set_state(as, ASR_STATE_NEXT_DB);
312 			break;
313 		}
314 
315 		/* If we got a packet but no anwser, use the next DB. */
316 		if (ar->ar_count == 0) {
317 			free(ar->ar_data);
318 			async_set_state(as, ASR_STATE_NEXT_DB);
319 			break;
320 		}
321 
322 		/* Read the hostent from the packet. */
323 
324 		h = hostent_from_packet(as->as_type,
325 		    as->as.hostnamadr.family, ar->ar_data, ar->ar_datalen);
326 		free(ar->ar_data);
327 		if (h == NULL) {
328 			ar->ar_errno = errno;
329 			ar->ar_h_errno = NETDB_INTERNAL;
330 			async_set_state(as, ASR_STATE_HALT);
331 			break;
332 		}
333 
334 		if (as->as_type == ASR_GETHOSTBYADDR) {
335 			if (hostent_add_addr(h, as->as.hostnamadr.addr,
336 			    as->as.hostnamadr.addrlen) == -1) {
337 				free(h);
338 				ar->ar_errno = errno;
339 				ar->ar_h_errno = NETDB_INTERNAL;
340 				async_set_state(as, ASR_STATE_HALT);
341 				break;
342 			}
343 		}
344 
345 		/*
346 		 * No address found in the dns packet. The blocking version
347 		 * reports this as an error.
348 		 */
349 		if (as->as_type == ASR_GETHOSTBYNAME &&
350 		    h->h.h_addr_list[0] == NULL) {
351 			free(h);
352 			async_set_state(as, ASR_STATE_NEXT_DB);
353 			break;
354 		}
355 
356 		ar->ar_hostent = &h->h;
357 		ar->ar_h_errno = NETDB_SUCCESS;
358 		async_set_state(as, ASR_STATE_HALT);
359 		break;
360 
361 	case ASR_STATE_NOT_FOUND:
362 		ar->ar_errno = 0;
363 		ar->ar_h_errno = HOST_NOT_FOUND;
364 		async_set_state(as, ASR_STATE_HALT);
365 		break;
366 
367 	case ASR_STATE_HALT:
368 		if (ar->ar_h_errno)
369 			ar->ar_hostent = NULL;
370 		else
371 			ar->ar_errno = 0;
372 		return (ASYNC_DONE);
373 
374 	default:
375 		ar->ar_errno = EOPNOTSUPP;
376 		ar->ar_h_errno = NETDB_INTERNAL;
377 		ar->ar_gai_errno = EAI_SYSTEM;
378 		async_set_state(as, ASR_STATE_HALT);
379 		break;
380 	}
381 	goto next;
382 }
383 
384 /*
385  * Lookup the first matching entry in the hostfile, either by address or by
386  * name depending on reqtype, and build a hostent from the line.
387  */
388 static struct hostent_ext *
389 hostent_file_match(FILE *f, int reqtype, int family, const char *data,
390     int datalen)
391 {
392 	char	*tokens[MAXTOKEN], addr[16];
393 	struct	 hostent_ext *h;
394 	int	 n, i;
395 
396 	for (;;) {
397 		n = asr_parse_namedb_line(f, tokens, MAXTOKEN);
398 		if (n == -1) {
399 			errno = 0; /* ignore errors reading the file */
400 			return (NULL);
401 		}
402 
403 		if (reqtype == ASR_GETHOSTBYNAME) {
404 			for (i = 1; i < n; i++) {
405 				if (strcasecmp(data, tokens[i]))
406 					continue;
407 				if (inet_pton(family, tokens[0], addr) == 1)
408 					goto found;
409 			}
410 		} else {
411 			if (inet_pton(family, tokens[0], addr) == 1 &&
412 			    memcmp(addr, data, datalen) == 0)
413 				goto found;
414 		}
415 	}
416 
417 found:
418 	if ((h = hostent_alloc(family)) == NULL)
419 		return (NULL);
420 	if (hostent_set_cname(h, tokens[1], 0) == -1)
421 		goto fail;
422 	for (i = 2; i < n; i ++)
423 		if (hostent_add_alias(h, tokens[i], 0) == -1)
424 			goto fail;
425 	if (hostent_add_addr(h, addr, h->h.h_length) == -1)
426 		goto fail;
427 	return (h);
428 fail:
429 	free(h);
430 	return (NULL);
431 }
432 
433 /*
434  * Fill the hostent from the given DNS packet.
435  */
436 static struct hostent_ext *
437 hostent_from_packet(int reqtype, int family, char *pkt, size_t pktlen)
438 {
439 	struct hostent_ext *h;
440 	struct unpack	 p;
441 	struct header	 hdr;
442 	struct query	 q;
443 	struct rr	 rr;
444 
445 	if ((h = hostent_alloc(family)) == NULL)
446 		return (NULL);
447 
448 	unpack_init(&p, pkt, pktlen);
449 	unpack_header(&p, &hdr);
450 	for (; hdr.qdcount; hdr.qdcount--)
451 		unpack_query(&p, &q);
452 	for (; hdr.ancount; hdr.ancount--) {
453 		unpack_rr(&p, &rr);
454 		if (rr.rr_class != C_IN)
455 			continue;
456 		switch (rr.rr_type) {
457 
458 		case T_CNAME:
459 			if (reqtype == ASR_GETHOSTBYNAME) {
460 				if (hostent_add_alias(h, rr.rr_dname, 1) == -1)
461 					goto fail;
462 			} else {
463 				if (hostent_set_cname(h, rr.rr_dname, 1) == -1)
464 					goto fail;
465 			}
466 			break;
467 
468 		case T_PTR:
469 			if (reqtype != ASR_GETHOSTBYADDR)
470 				break;
471 			if (hostent_set_cname(h, rr.rr.ptr.ptrname, 1) == -1)
472 				goto fail;
473 			/* XXX See if we need MULTI_PTRS_ARE_ALIASES */
474 			break;
475 
476 		case T_A:
477 			if (reqtype != ASR_GETHOSTBYNAME)
478 				break;
479 			if (family != AF_INET)
480 				break;
481 			if (hostent_set_cname(h, rr.rr_dname, 1) == -1)
482 				goto fail;
483 			if (hostent_add_addr(h, &rr.rr.in_a.addr, 4) == -1)
484 				goto fail;
485 			break;
486 
487 		case T_AAAA:
488 			if (reqtype != ASR_GETHOSTBYNAME)
489 				break;
490 			if (family != AF_INET6)
491 				break;
492 			if (hostent_set_cname(h, rr.rr_dname, 1) == -1)
493 				goto fail;
494 			if (hostent_add_addr(h, &rr.rr.in_aaaa.addr6, 16) == -1)
495 				goto fail;
496 			break;
497 		}
498 	}
499 
500 	return (h);
501 fail:
502 	free(h);
503 	return (NULL);
504 }
505 
506 static struct hostent_ext *
507 hostent_alloc(int family)
508 {
509 	struct hostent_ext     *h;
510 	size_t			alloc;
511 
512 	alloc = sizeof(*h) + 1024;
513 	if ((h = calloc(1, alloc)) == NULL)
514 		return (NULL);
515 
516 	h->h.h_addrtype = family;
517 	h->h.h_length = (family == AF_INET) ? 4 : 16;
518 	h->h.h_aliases = h->aliases;
519 	h->h.h_addr_list = h->addrs;
520 	h->pos = (char*)(h) + sizeof(*h);
521 	h->end = h->pos + 1024;
522 
523 	return (h);
524 }
525 
526 static int
527 hostent_set_cname(struct hostent_ext *h, const char *name, int isdname)
528 {
529 	char	buf[MAXDNAME];
530 	size_t	n;
531 
532 	if (h->h.h_name)
533 		return (-1);
534 
535 	if (isdname) {
536 		asr_strdname(name, buf, sizeof buf);
537 		buf[strlen(buf) - 1] = '\0';
538 		name = buf;
539 	}
540 
541 	n = strlen(name) + 1;
542 	if (h->pos + n >= h->end)
543 		return (-1);
544 
545 	h->h.h_name = h->pos;
546 	memmove(h->pos, name, n);
547 	h->pos += n;
548 	return (0);
549 }
550 
551 static int
552 hostent_add_alias(struct hostent_ext *h, const char *name, int isdname)
553 {
554 	char	buf[MAXDNAME];
555 	size_t	i, n;
556 
557 	for (i = 0; i < MAXALIASES; i++)
558 		if (h->aliases[i] == NULL)
559 			break;
560 	if (i == MAXALIASES)
561 		return (-1);
562 
563 	if (isdname) {
564 		asr_strdname(name, buf, sizeof buf);
565 		buf[strlen(buf)-1] = '\0';
566 		name = buf;
567 	}
568 
569 	n = strlen(name) + 1;
570 	if (h->pos + n >= h->end)
571 		return (-1);
572 
573 	h->aliases[i] = h->pos;
574 	memmove(h->pos, name, n);
575 	h->pos += n;
576 	return (0);
577 }
578 
579 static int
580 hostent_add_addr(struct hostent_ext *h, const void *addr, size_t size)
581 {
582 	int	i;
583 
584 	for (i = 0; i < MAXADDRS; i++)
585 		if (h->addrs[i] == NULL)
586 			break;
587 	if (i == MAXADDRS)
588 		return (-1);
589 
590 	if (h->pos + size >= h->end)
591 		return (-1);
592 
593 	h->addrs[i] = h->pos;
594 	memmove(h->pos, addr, size);
595 	h->pos += size;
596 	return (0);
597 }
598 
599 ssize_t
600 addr_as_fqdn(const char *addr, int family, char *dst, size_t max)
601 {
602 	const struct in6_addr	*in6_addr;
603 	in_addr_t		 in_addr;
604 
605 	switch (family) {
606 	case AF_INET:
607 		in_addr = ntohl(*((const in_addr_t *)addr));
608 		snprintf(dst, max,
609 		    "%d.%d.%d.%d.in-addr.arpa.",
610 		    in_addr & 0xff,
611 		    (in_addr >> 8) & 0xff,
612 		    (in_addr >> 16) & 0xff,
613 		    (in_addr >> 24) & 0xff);
614 		break;
615 	case AF_INET6:
616 		in6_addr = (const struct in6_addr *)addr;
617 		snprintf(dst, max,
618 		    "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x."
619 		    "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x."
620 		    "ip6.arpa.",
621 		    in6_addr->s6_addr[15] & 0xf,
622 		    (in6_addr->s6_addr[15] >> 4) & 0xf,
623 		    in6_addr->s6_addr[14] & 0xf,
624 		    (in6_addr->s6_addr[14] >> 4) & 0xf,
625 		    in6_addr->s6_addr[13] & 0xf,
626 		    (in6_addr->s6_addr[13] >> 4) & 0xf,
627 		    in6_addr->s6_addr[12] & 0xf,
628 		    (in6_addr->s6_addr[12] >> 4) & 0xf,
629 		    in6_addr->s6_addr[11] & 0xf,
630 		    (in6_addr->s6_addr[11] >> 4) & 0xf,
631 		    in6_addr->s6_addr[10] & 0xf,
632 		    (in6_addr->s6_addr[10] >> 4) & 0xf,
633 		    in6_addr->s6_addr[9] & 0xf,
634 		    (in6_addr->s6_addr[9] >> 4) & 0xf,
635 		    in6_addr->s6_addr[8] & 0xf,
636 		    (in6_addr->s6_addr[8] >> 4) & 0xf,
637 		    in6_addr->s6_addr[7] & 0xf,
638 		    (in6_addr->s6_addr[7] >> 4) & 0xf,
639 		    in6_addr->s6_addr[6] & 0xf,
640 		    (in6_addr->s6_addr[6] >> 4) & 0xf,
641 		    in6_addr->s6_addr[5] & 0xf,
642 		    (in6_addr->s6_addr[5] >> 4) & 0xf,
643 		    in6_addr->s6_addr[4] & 0xf,
644 		    (in6_addr->s6_addr[4] >> 4) & 0xf,
645 		    in6_addr->s6_addr[3] & 0xf,
646 		    (in6_addr->s6_addr[3] >> 4) & 0xf,
647 		    in6_addr->s6_addr[2] & 0xf,
648 		    (in6_addr->s6_addr[2] >> 4) & 0xf,
649 		    in6_addr->s6_addr[1] & 0xf,
650 		    (in6_addr->s6_addr[1] >> 4) & 0xf,
651 		    in6_addr->s6_addr[0] & 0xf,
652 		    (in6_addr->s6_addr[0] >> 4) & 0xf);
653 		break;
654 	default:
655 		return (-1);
656 	}
657 	return (0);
658 }
659 
660 #ifdef YP
661 static struct hostent_ext *
662 _yp_gethostnamadr(int type, const void *data)
663 {
664 	static char		*domain = NULL;
665 	struct hostent_ext	*h = NULL;
666 	const char		*name;
667 	char			 buf[MAXHOSTNAMELEN];
668 	char			*res = NULL;
669 	int			 r, len;
670 
671 	if (!domain && _yp_check(&domain) == 0) {
672 		errno = 0; /* ignore yp_bind errors */
673 		return (NULL);
674 	}
675 
676 	if (type == ASR_GETHOSTBYNAME) {
677 		name = data;
678 		len = strlen(name);
679 		r = yp_match(domain, "hosts.byname", name, len, &res, &len);
680 	}
681 	else {
682 		if (inet_ntop(AF_INET, data, buf, sizeof buf) == NULL)
683 			return (NULL);
684 		len = strlen(buf);
685 		r = yp_match(domain, "hosts.byaddr", buf, len, &res, &len);
686 	}
687 	if (r == 0) {
688 		h = hostent_from_yp(AF_INET, res);
689 	} else {
690 		errno = 0; /* ignore error if not found */
691 	}
692 	if (res)
693 		free(res);
694 	return (h);
695 }
696 
697 static int
698 strsplit(char *line, char **tokens, int ntokens)
699 {
700 	int	ntok;
701 	char	*cp, **tp;
702 
703 	for (cp = line, tp = tokens, ntok = 0;
704 	    ntok < ntokens && (*tp = strsep(&cp, " \t")) != NULL; )
705 		if (**tp != '\0') {
706 			tp++;
707 			ntok++;
708 		}
709 
710 	return (ntok);
711 }
712 
713 static struct hostent_ext *
714 hostent_from_yp(int family, char *line)
715 {
716 	struct hostent_ext	*h;
717 	char			*next, *tokens[10], addr[IN6ADDRSZ];
718 	int			 i, ntok;
719 
720 	if ((h = hostent_alloc(family)) == NULL)
721 		return (NULL);
722 
723 	for (next = line; line; line = next) {
724 		if ((next = strchr(line, '\n'))) {
725 			*next = '\0';
726 			next += 1;
727 		}
728 		ntok = strsplit(line, tokens, 10);
729 		if (ntok < 2)
730 			continue;
731 		if (inet_pton(family, tokens[0], addr) == 1)
732 			hostent_add_addr(h, addr, family == AF_INET ?
733 			    INADDRSZ : IN6ADDRSZ);
734 		i = 2;
735 		if (h->h.h_name == NULL)
736 			hostent_set_cname(h, tokens[1], 0);
737 		else if (strcmp(h->h.h_name, tokens[1]))
738 			i = 1;
739 		for (; i < ntok; i++)
740 			hostent_add_alias(h, tokens[i], 0);
741 	}
742 
743 	if (h->h.h_name == NULL) {
744 		free(h);
745 		return (NULL);
746 	}
747 
748 	return (h);
749 }
750 #endif
751