xref: /openbsd-src/lib/libc/asr/gethostnamadr_async.c (revision 6a7a3d640d39dae50928bded135d5992d31570e3)
1 /*	$OpenBSD: gethostnamadr_async.c,v 1.20 2013/06/01 14:34:34 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			 name[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 				break;
196 			}
197 		}
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 			async_set_state(as, ASR_STATE_NOT_FOUND);
205 			break;
206 		}
207 
208 		switch (AS_DB(as)) {
209 
210 		case ASR_DB_DNS:
211 
212 			/* Create a subquery to do the DNS lookup */
213 
214 			if (as->as_type == ASR_GETHOSTBYNAME) {
215 				type = (as->as.hostnamadr.family == AF_INET) ?
216 				    T_A : T_AAAA;
217 				as->as.hostnamadr.subq = res_search_async_ctx(
218 				    as->as.hostnamadr.name,
219 				    C_IN, type, as->as_ctx);
220 			} else {
221 				addr_as_fqdn(as->as.hostnamadr.addr,
222 				    as->as.hostnamadr.family,
223 				    name, sizeof(name));
224 				as->as.hostnamadr.subq = res_query_async_ctx(
225 				    name, C_IN, T_PTR, as->as_ctx);
226 			}
227 
228 			if (as->as.hostnamadr.subq == NULL) {
229 				ar->ar_errno = errno;
230 				ar->ar_h_errno = NETDB_INTERNAL;
231 				async_set_state(as, ASR_STATE_HALT);
232 				break;
233 			}
234 
235 			async_set_state(as, ASR_STATE_SUBQUERY);
236 			break;
237 
238 		case ASR_DB_FILE:
239 
240 			/* Try to find a match in the host file */
241 
242 			if ((f = fopen(as->as_ctx->ac_hostfile, "r")) == NULL)
243 				break;
244 
245 			if (as->as_type == ASR_GETHOSTBYNAME) {
246 				data = asr_hostalias(as->as_ctx,
247 				    as->as.hostnamadr.name, name, sizeof(name));
248 				if (data == NULL)
249 					data = as->as.hostnamadr.name;
250 			}
251 			else
252 				data = as->as.hostnamadr.addr;
253 
254 			h = hostent_file_match(f, as->as_type,
255 			    as->as.hostnamadr.family, data,
256 			    as->as.hostnamadr.addrlen);
257 			saved_errno = errno;
258 			fclose(f);
259 			errno = saved_errno;
260 
261 			if (h == NULL) {
262 				if (errno) {
263 					ar->ar_errno = errno;
264 					ar->ar_h_errno = NETDB_INTERNAL;
265 					async_set_state(as, ASR_STATE_HALT);
266 				}
267 				/* otherwise not found */
268 				break;
269 			}
270 			ar->ar_hostent = &h->h;
271 			ar->ar_h_errno = NETDB_SUCCESS;
272 			async_set_state(as, ASR_STATE_HALT);
273 			break;
274 #ifdef YP
275 		case ASR_DB_YP:
276 			/* IPv4 only */
277 			if (as->as.hostnamadr.family != AF_INET)
278 				break;
279 			if (as->as_type == ASR_GETHOSTBYNAME) {
280 				data = asr_hostalias(as->as_ctx,
281 				    as->as.hostnamadr.name, name, sizeof(name));
282 				if (data == NULL)
283 					data = as->as.hostnamadr.name;
284 			}
285 			else
286 				data = as->as.hostnamadr.addr;
287 			h = _yp_gethostnamadr(as->as_type, data);
288 			if (h == NULL) {
289 				if (errno) {
290 					ar->ar_errno = errno;
291 					ar->ar_h_errno = NETDB_INTERNAL;
292 					async_set_state(as, ASR_STATE_HALT);
293 				}
294 				/* otherwise not found */
295 				break;
296 			}
297 			ar->ar_hostent = &h->h;
298 			ar->ar_h_errno = NETDB_SUCCESS;
299 			async_set_state(as, ASR_STATE_HALT);
300 			break;
301 #endif
302 		}
303 		break;
304 
305 	case ASR_STATE_SUBQUERY:
306 
307 		/* Run the DNS subquery. */
308 
309 		if ((r = async_run(as->as.hostnamadr.subq, ar)) == ASYNC_COND)
310 			return (ASYNC_COND);
311 
312 		/* Done. */
313 		as->as.hostnamadr.subq = NULL;
314 
315 		if (ar->ar_datalen == -1) {
316 			async_set_state(as, ASR_STATE_NEXT_DB);
317 			break;
318 		}
319 
320 		/* If we got a packet but no anwser, use the next DB. */
321 		if (ar->ar_count == 0) {
322 			free(ar->ar_data);
323 			as->as.hostnamadr.subq_h_errno = ar->ar_h_errno;
324 			async_set_state(as, ASR_STATE_NEXT_DB);
325 			break;
326 		}
327 
328 		/* Read the hostent from the packet. */
329 
330 		h = hostent_from_packet(as->as_type,
331 		    as->as.hostnamadr.family, ar->ar_data, ar->ar_datalen);
332 		free(ar->ar_data);
333 		if (h == NULL) {
334 			ar->ar_errno = errno;
335 			ar->ar_h_errno = NETDB_INTERNAL;
336 			async_set_state(as, ASR_STATE_HALT);
337 			break;
338 		}
339 
340 		if (as->as_type == ASR_GETHOSTBYADDR) {
341 			if (hostent_add_addr(h, as->as.hostnamadr.addr,
342 			    as->as.hostnamadr.addrlen) == -1) {
343 				free(h);
344 				ar->ar_errno = errno;
345 				ar->ar_h_errno = NETDB_INTERNAL;
346 				async_set_state(as, ASR_STATE_HALT);
347 				break;
348 			}
349 		}
350 
351 		/*
352 		 * No address found in the dns packet. The blocking version
353 		 * reports this as an error.
354 		 */
355 		if ((as->as_type == ASR_GETHOSTBYNAME &&
356 		     h->h.h_addr_list[0] == NULL) ||
357 		    (as->as_type == ASR_GETHOSTBYADDR &&
358 		     h->h.h_name == NULL)) {
359 			free(h);
360 			async_set_state(as, ASR_STATE_NEXT_DB);
361 			break;
362 		}
363 
364 		ar->ar_hostent = &h->h;
365 		ar->ar_h_errno = NETDB_SUCCESS;
366 		async_set_state(as, ASR_STATE_HALT);
367 		break;
368 
369 	case ASR_STATE_NOT_FOUND:
370 		ar->ar_errno = 0;
371 		if (as->as.hostnamadr.subq_h_errno)
372 			ar->ar_h_errno = as->as.hostnamadr.subq_h_errno;
373 		else
374 			ar->ar_h_errno = HOST_NOT_FOUND;
375 		async_set_state(as, ASR_STATE_HALT);
376 		break;
377 
378 	case ASR_STATE_HALT:
379 		if (ar->ar_h_errno)
380 			ar->ar_hostent = NULL;
381 		else
382 			ar->ar_errno = 0;
383 		return (ASYNC_DONE);
384 
385 	default:
386 		ar->ar_errno = EOPNOTSUPP;
387 		ar->ar_h_errno = NETDB_INTERNAL;
388 		ar->ar_gai_errno = EAI_SYSTEM;
389 		async_set_state(as, ASR_STATE_HALT);
390 		break;
391 	}
392 	goto next;
393 }
394 
395 /*
396  * Create a hostent from a numeric address string.
397  */
398 static struct hostent_ext *
399 hostent_from_addr(int family, const char *name, const char *addr)
400 {
401 	struct	 hostent_ext *h;
402 
403 	if ((h = hostent_alloc(family)) == NULL)
404 		return (NULL);
405 	if (hostent_set_cname(h, name, 0) == -1)
406 		goto fail;
407 	if (hostent_add_addr(h, addr, h->h.h_length) == -1)
408 		goto fail;
409 	return (h);
410 fail:
411 	free(h);
412 	return (NULL);
413 }
414 
415 /*
416  * Lookup the first matching entry in the hostfile, either by address or by
417  * name depending on reqtype, and build a hostent from the line.
418  */
419 static struct hostent_ext *
420 hostent_file_match(FILE *f, int reqtype, int family, const char *data,
421     int datalen)
422 {
423 	char	*tokens[MAXTOKEN], addr[16];
424 	struct	 hostent_ext *h;
425 	int	 n, i;
426 
427 	for (;;) {
428 		n = asr_parse_namedb_line(f, tokens, MAXTOKEN);
429 		if (n == -1) {
430 			errno = 0; /* ignore errors reading the file */
431 			return (NULL);
432 		}
433 
434 		if (reqtype == ASR_GETHOSTBYNAME) {
435 			for (i = 1; i < n; i++) {
436 				if (strcasecmp(data, tokens[i]))
437 					continue;
438 				if (inet_pton(family, tokens[0], addr) == 1)
439 					goto found;
440 			}
441 		} else {
442 			if (inet_pton(family, tokens[0], addr) == 1 &&
443 			    memcmp(addr, data, datalen) == 0)
444 				goto found;
445 		}
446 	}
447 
448 found:
449 	if ((h = hostent_alloc(family)) == NULL)
450 		return (NULL);
451 	if (hostent_set_cname(h, tokens[1], 0) == -1)
452 		goto fail;
453 	for (i = 2; i < n; i ++)
454 		if (hostent_add_alias(h, tokens[i], 0) == -1)
455 			goto fail;
456 	if (hostent_add_addr(h, addr, h->h.h_length) == -1)
457 		goto fail;
458 	return (h);
459 fail:
460 	free(h);
461 	return (NULL);
462 }
463 
464 /*
465  * Fill the hostent from the given DNS packet.
466  */
467 static struct hostent_ext *
468 hostent_from_packet(int reqtype, int family, char *pkt, size_t pktlen)
469 {
470 	struct hostent_ext *h;
471 	struct unpack	 p;
472 	struct header	 hdr;
473 	struct query	 q;
474 	struct rr	 rr;
475 	char		 dname[MAXDNAME];
476 
477 	if ((h = hostent_alloc(family)) == NULL)
478 		return (NULL);
479 
480 	unpack_init(&p, pkt, pktlen);
481 	unpack_header(&p, &hdr);
482 	for (; hdr.qdcount; hdr.qdcount--)
483 		unpack_query(&p, &q);
484 	strlcpy(dname, q.q_dname, sizeof(dname));
485 
486 	for (; hdr.ancount; hdr.ancount--) {
487 		unpack_rr(&p, &rr);
488 		if (rr.rr_class != C_IN)
489 			continue;
490 		switch (rr.rr_type) {
491 
492 		case T_CNAME:
493 			if (reqtype == ASR_GETHOSTBYNAME) {
494 				if (hostent_add_alias(h, rr.rr_dname, 1) == -1)
495 					goto fail;
496 			} else {
497 				if (strcasecmp(rr.rr_dname, dname) == 0)
498 					strlcpy(dname, rr.rr.cname.cname,
499 					    sizeof(dname));
500 			}
501 			break;
502 
503 		case T_PTR:
504 			if (reqtype != ASR_GETHOSTBYADDR)
505 				break;
506 			if (strcasecmp(rr.rr_dname, dname) != 0)
507 				continue;
508 			if (hostent_set_cname(h, rr.rr.ptr.ptrname, 1) == -1)
509 				goto fail;
510 			/* XXX See if we need MULTI_PTRS_ARE_ALIASES */
511 			break;
512 
513 		case T_A:
514 			if (reqtype != ASR_GETHOSTBYNAME)
515 				break;
516 			if (family != AF_INET)
517 				break;
518 			if (hostent_set_cname(h, rr.rr_dname, 1) == -1)
519 				;
520 			if (hostent_add_addr(h, &rr.rr.in_a.addr, 4) == -1)
521 				goto fail;
522 			break;
523 
524 		case T_AAAA:
525 			if (reqtype != ASR_GETHOSTBYNAME)
526 				break;
527 			if (family != AF_INET6)
528 				break;
529 			if (hostent_set_cname(h, rr.rr_dname, 1) == -1)
530 				;
531 			if (hostent_add_addr(h, &rr.rr.in_aaaa.addr6, 16) == -1)
532 				goto fail;
533 			break;
534 		}
535 	}
536 
537 	return (h);
538 fail:
539 	free(h);
540 	return (NULL);
541 }
542 
543 static struct hostent_ext *
544 hostent_alloc(int family)
545 {
546 	struct hostent_ext	*h;
547 	size_t			alloc;
548 
549 	alloc = sizeof(*h) + 1024;
550 	if ((h = calloc(1, alloc)) == NULL)
551 		return (NULL);
552 
553 	h->h.h_addrtype = family;
554 	h->h.h_length = (family == AF_INET) ? 4 : 16;
555 	h->h.h_aliases = h->aliases;
556 	h->h.h_addr_list = h->addrs;
557 	h->pos = (char *)(h) + sizeof(*h);
558 	h->end = h->pos + 1024;
559 
560 	return (h);
561 }
562 
563 static int
564 hostent_set_cname(struct hostent_ext *h, const char *name, int isdname)
565 {
566 	char	buf[MAXDNAME];
567 	size_t	n;
568 
569 	if (h->h.h_name)
570 		return (-1);
571 
572 	if (isdname) {
573 		asr_strdname(name, buf, sizeof buf);
574 		buf[strlen(buf) - 1] = '\0';
575 		name = buf;
576 	}
577 
578 	n = strlen(name) + 1;
579 	if (h->pos + n >= h->end)
580 		return (-1);
581 
582 	h->h.h_name = h->pos;
583 	memmove(h->pos, name, n);
584 	h->pos += n;
585 	return (0);
586 }
587 
588 static int
589 hostent_add_alias(struct hostent_ext *h, const char *name, int isdname)
590 {
591 	char	buf[MAXDNAME];
592 	size_t	i, n;
593 
594 	for (i = 0; i < MAXALIASES; i++)
595 		if (h->aliases[i] == NULL)
596 			break;
597 	if (i == MAXALIASES)
598 		return (-1);
599 
600 	if (isdname) {
601 		asr_strdname(name, buf, sizeof buf);
602 		buf[strlen(buf)-1] = '\0';
603 		name = buf;
604 	}
605 
606 	n = strlen(name) + 1;
607 	if (h->pos + n >= h->end)
608 		return (-1);
609 
610 	h->aliases[i] = h->pos;
611 	memmove(h->pos, name, n);
612 	h->pos += n;
613 	return (0);
614 }
615 
616 static int
617 hostent_add_addr(struct hostent_ext *h, const void *addr, size_t size)
618 {
619 	int	i;
620 
621 	for (i = 0; i < MAXADDRS; i++)
622 		if (h->addrs[i] == NULL)
623 			break;
624 	if (i == MAXADDRS)
625 		return (-1);
626 
627 	if (h->pos + size >= h->end)
628 		return (-1);
629 
630 	h->addrs[i] = h->pos;
631 	memmove(h->pos, addr, size);
632 	h->pos += size;
633 	return (0);
634 }
635 
636 ssize_t
637 addr_as_fqdn(const char *addr, int family, char *dst, size_t max)
638 {
639 	const struct in6_addr	*in6_addr;
640 	in_addr_t		 in_addr;
641 
642 	switch (family) {
643 	case AF_INET:
644 		in_addr = ntohl(*((const in_addr_t *)addr));
645 		snprintf(dst, max,
646 		    "%d.%d.%d.%d.in-addr.arpa.",
647 		    in_addr & 0xff,
648 		    (in_addr >> 8) & 0xff,
649 		    (in_addr >> 16) & 0xff,
650 		    (in_addr >> 24) & 0xff);
651 		break;
652 	case AF_INET6:
653 		in6_addr = (const struct in6_addr *)addr;
654 		snprintf(dst, max,
655 		    "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x."
656 		    "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x."
657 		    "ip6.arpa.",
658 		    in6_addr->s6_addr[15] & 0xf,
659 		    (in6_addr->s6_addr[15] >> 4) & 0xf,
660 		    in6_addr->s6_addr[14] & 0xf,
661 		    (in6_addr->s6_addr[14] >> 4) & 0xf,
662 		    in6_addr->s6_addr[13] & 0xf,
663 		    (in6_addr->s6_addr[13] >> 4) & 0xf,
664 		    in6_addr->s6_addr[12] & 0xf,
665 		    (in6_addr->s6_addr[12] >> 4) & 0xf,
666 		    in6_addr->s6_addr[11] & 0xf,
667 		    (in6_addr->s6_addr[11] >> 4) & 0xf,
668 		    in6_addr->s6_addr[10] & 0xf,
669 		    (in6_addr->s6_addr[10] >> 4) & 0xf,
670 		    in6_addr->s6_addr[9] & 0xf,
671 		    (in6_addr->s6_addr[9] >> 4) & 0xf,
672 		    in6_addr->s6_addr[8] & 0xf,
673 		    (in6_addr->s6_addr[8] >> 4) & 0xf,
674 		    in6_addr->s6_addr[7] & 0xf,
675 		    (in6_addr->s6_addr[7] >> 4) & 0xf,
676 		    in6_addr->s6_addr[6] & 0xf,
677 		    (in6_addr->s6_addr[6] >> 4) & 0xf,
678 		    in6_addr->s6_addr[5] & 0xf,
679 		    (in6_addr->s6_addr[5] >> 4) & 0xf,
680 		    in6_addr->s6_addr[4] & 0xf,
681 		    (in6_addr->s6_addr[4] >> 4) & 0xf,
682 		    in6_addr->s6_addr[3] & 0xf,
683 		    (in6_addr->s6_addr[3] >> 4) & 0xf,
684 		    in6_addr->s6_addr[2] & 0xf,
685 		    (in6_addr->s6_addr[2] >> 4) & 0xf,
686 		    in6_addr->s6_addr[1] & 0xf,
687 		    (in6_addr->s6_addr[1] >> 4) & 0xf,
688 		    in6_addr->s6_addr[0] & 0xf,
689 		    (in6_addr->s6_addr[0] >> 4) & 0xf);
690 		break;
691 	default:
692 		return (-1);
693 	}
694 	return (0);
695 }
696 
697 #ifdef YP
698 static struct hostent_ext *
699 _yp_gethostnamadr(int type, const void *data)
700 {
701 	static char		*domain = NULL;
702 	struct hostent_ext	*h = NULL;
703 	const char		*name;
704 	char			 buf[MAXHOSTNAMELEN];
705 	char			*res = NULL;
706 	int			 r, len;
707 
708 	if (!domain && _yp_check(&domain) == 0) {
709 		errno = 0; /* ignore yp_bind errors */
710 		return (NULL);
711 	}
712 
713 	if (type == ASR_GETHOSTBYNAME) {
714 		name = data;
715 		len = strlen(name);
716 		r = yp_match(domain, "hosts.byname", name, len, &res, &len);
717 	}
718 	else {
719 		if (inet_ntop(AF_INET, data, buf, sizeof buf) == NULL)
720 			return (NULL);
721 		len = strlen(buf);
722 		r = yp_match(domain, "hosts.byaddr", buf, len, &res, &len);
723 	}
724 	if (r == 0) {
725 		h = hostent_from_yp(AF_INET, res);
726 	} else {
727 		errno = 0; /* ignore error if not found */
728 	}
729 	if (res)
730 		free(res);
731 	return (h);
732 }
733 
734 static int
735 strsplit(char *line, char **tokens, int ntokens)
736 {
737 	int	ntok;
738 	char	*cp, **tp;
739 
740 	for (cp = line, tp = tokens, ntok = 0;
741 	    ntok < ntokens && (*tp = strsep(&cp, " \t")) != NULL; )
742 		if (**tp != '\0') {
743 			tp++;
744 			ntok++;
745 		}
746 
747 	return (ntok);
748 }
749 
750 static struct hostent_ext *
751 hostent_from_yp(int family, char *line)
752 {
753 	struct hostent_ext	*h;
754 	char			*next, *tokens[10], addr[IN6ADDRSZ];
755 	int			 i, ntok;
756 
757 	if ((h = hostent_alloc(family)) == NULL)
758 		return (NULL);
759 
760 	for (next = line; line; line = next) {
761 		if ((next = strchr(line, '\n'))) {
762 			*next = '\0';
763 			next += 1;
764 		}
765 		ntok = strsplit(line, tokens, 10);
766 		if (ntok < 2)
767 			continue;
768 		if (inet_pton(family, tokens[0], addr) == 1)
769 			hostent_add_addr(h, addr, family == AF_INET ?
770 			    INADDRSZ : IN6ADDRSZ);
771 		i = 2;
772 		if (h->h.h_name == NULL)
773 			hostent_set_cname(h, tokens[1], 0);
774 		else if (strcmp(h->h.h_name, tokens[1]))
775 			i = 1;
776 		for (; i < ntok; i++)
777 			hostent_add_alias(h, tokens[i], 0);
778 	}
779 
780 	if (h->h.h_name == NULL) {
781 		free(h);
782 		return (NULL);
783 	}
784 
785 	return (h);
786 }
787 #endif
788