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