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