xref: /openbsd-src/lib/libc/asr/gethostnamadr_async.c (revision 0d7b84a898f082aed9d1f2ec3ae7c476483a8f40)
1 /*	$OpenBSD: gethostnamadr_async.c,v 1.32 2014/11/02 13:59: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 #include <netdb.h>
30 
31 #include <asr.h>
32 #include <ctype.h>
33 #include <err.h>
34 #include <errno.h>
35 #include <resolv.h> /* for res_hnok */
36 #include <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
39 
40 #include "asr_private.h"
41 
42 #define MAXALIASES	16
43 #define MAXADDRS	16
44 
45 struct hostent_ext {
46 	struct hostent	 h;
47 	char		*aliases[MAXALIASES + 1];
48 	char		*addrs[MAXADDRS + 1];
49 	char		*end;
50 	char		*pos;
51 };
52 
53 static int gethostnamadr_async_run(struct asr_query *, struct asr_result *);
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 asr_query *
68 gethostbyname_async(const char *name, void *asr)
69 {
70 	return gethostbyname2_async(name, AF_INET, asr);
71 }
72 
73 struct asr_query *
74 gethostbyname2_async(const char *name, int af, void *asr)
75 {
76 	struct asr_ctx	 *ac;
77 	struct asr_query *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 = asr_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 		asr_async_free(as);
105 	asr_ctx_unref(ac);
106 	return (NULL);
107 }
108 
109 struct asr_query *
110 gethostbyaddr_async(const void *addr, socklen_t len, int af, void *asr)
111 {
112 	struct asr_ctx	 *ac;
113 	struct asr_query *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 asr_query *
123 gethostbyaddr_async_ctx(const void *addr, socklen_t len, int af,
124     struct asr_ctx *ac)
125 {
126 	struct asr_query *as;
127 
128 	if ((as = asr_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 		asr_async_free(as);
142 	return (NULL);
143 }
144 
145 static int
146 gethostnamadr_async_run(struct asr_query *as, struct asr_result *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 		if (as->as_type == ASR_GETHOSTBYNAME) {
177 
178 			if (as->as.hostnamadr.name[0] == '\0') {
179 				ar->ar_h_errno = NO_DATA;
180 				async_set_state(as, ASR_STATE_HALT);
181 				break;
182 			}
183 
184 			/* Name might be an IP address string */
185 			for (c = as->as.hostnamadr.name; *c; c++)
186 				if (!isdigit((unsigned char)*c) &&
187 				     *c != '.' && *c != ':')
188 					break;
189 			if (*c == 0 &&
190 			    inet_pton(as->as.hostnamadr.family,
191 			    as->as.hostnamadr.name, addr) == 1) {
192 				h = hostent_from_addr(as->as.hostnamadr.family,
193 				    as->as.hostnamadr.name, addr);
194 				if (h == NULL) {
195 					ar->ar_errno = errno;
196 					ar->ar_h_errno = NETDB_INTERNAL;
197 				}
198 				else {
199 					ar->ar_hostent = &h->h;
200 					ar->ar_h_errno = NETDB_SUCCESS;
201 				}
202 				async_set_state(as, ASR_STATE_HALT);
203 				break;
204 			}
205 		}
206 		async_set_state(as, ASR_STATE_NEXT_DB);
207 		break;
208 
209 	case ASR_STATE_NEXT_DB:
210 
211 		if (asr_iter_db(as) == -1) {
212 			async_set_state(as, ASR_STATE_NOT_FOUND);
213 			break;
214 		}
215 
216 		switch (AS_DB(as)) {
217 
218 		case ASR_DB_DNS:
219 
220 			/* Create a subquery to do the DNS lookup */
221 
222 			if (as->as_type == ASR_GETHOSTBYNAME) {
223 				type = (as->as.hostnamadr.family == AF_INET) ?
224 				    T_A : T_AAAA;
225 				as->as.hostnamadr.subq = res_search_async_ctx(
226 				    as->as.hostnamadr.name,
227 				    C_IN, type, as->as_ctx);
228 			} else {
229 				asr_addr_as_fqdn(as->as.hostnamadr.addr,
230 				    as->as.hostnamadr.family,
231 				    name, sizeof(name));
232 				as->as.hostnamadr.subq = res_query_async_ctx(
233 				    name, C_IN, T_PTR, as->as_ctx);
234 			}
235 
236 			if (as->as.hostnamadr.subq == NULL) {
237 				ar->ar_errno = errno;
238 				ar->ar_h_errno = NETDB_INTERNAL;
239 				async_set_state(as, ASR_STATE_HALT);
240 				break;
241 			}
242 
243 			async_set_state(as, ASR_STATE_SUBQUERY);
244 			break;
245 
246 		case ASR_DB_FILE:
247 
248 			/* Try to find a match in the host file */
249 
250 			if ((f = fopen(as->as_ctx->ac_hostfile, "re")) == NULL)
251 				break;
252 
253 			if (as->as_type == ASR_GETHOSTBYNAME) {
254 				data = asr_hostalias(as->as_ctx,
255 				    as->as.hostnamadr.name, name, sizeof(name));
256 				if (data == NULL)
257 					data = as->as.hostnamadr.name;
258 			}
259 			else
260 				data = as->as.hostnamadr.addr;
261 
262 			h = hostent_file_match(f, as->as_type,
263 			    as->as.hostnamadr.family, data,
264 			    as->as.hostnamadr.addrlen);
265 			saved_errno = errno;
266 			fclose(f);
267 			errno = saved_errno;
268 
269 			if (h == NULL) {
270 				if (errno) {
271 					ar->ar_errno = errno;
272 					ar->ar_h_errno = NETDB_INTERNAL;
273 					async_set_state(as, ASR_STATE_HALT);
274 				}
275 				/* otherwise not found */
276 				break;
277 			}
278 			ar->ar_hostent = &h->h;
279 			ar->ar_h_errno = NETDB_SUCCESS;
280 			async_set_state(as, ASR_STATE_HALT);
281 			break;
282 #ifdef YP
283 		case ASR_DB_YP:
284 			/* IPv4 only */
285 			if (as->as.hostnamadr.family != AF_INET)
286 				break;
287 			if (as->as_type == ASR_GETHOSTBYNAME) {
288 				data = asr_hostalias(as->as_ctx,
289 				    as->as.hostnamadr.name, name, sizeof(name));
290 				if (data == NULL)
291 					data = as->as.hostnamadr.name;
292 			}
293 			else
294 				data = as->as.hostnamadr.addr;
295 			h = _yp_gethostnamadr(as->as_type, data);
296 			if (h == NULL) {
297 				if (errno) {
298 					ar->ar_errno = errno;
299 					ar->ar_h_errno = NETDB_INTERNAL;
300 					async_set_state(as, ASR_STATE_HALT);
301 				}
302 				/* otherwise not found */
303 				break;
304 			}
305 			ar->ar_hostent = &h->h;
306 			ar->ar_h_errno = NETDB_SUCCESS;
307 			async_set_state(as, ASR_STATE_HALT);
308 			break;
309 #endif
310 		}
311 		break;
312 
313 	case ASR_STATE_SUBQUERY:
314 
315 		/* Run the DNS subquery. */
316 
317 		if ((r = asr_run(as->as.hostnamadr.subq, ar)) == ASYNC_COND)
318 			return (ASYNC_COND);
319 
320 		/* Done. */
321 		as->as.hostnamadr.subq = NULL;
322 
323 		if (ar->ar_datalen == -1) {
324 			async_set_state(as, ASR_STATE_NEXT_DB);
325 			break;
326 		}
327 
328 		/* If we got a packet but no anwser, use the next DB. */
329 		if (ar->ar_count == 0) {
330 			free(ar->ar_data);
331 			as->as.hostnamadr.subq_h_errno = ar->ar_h_errno;
332 			async_set_state(as, ASR_STATE_NEXT_DB);
333 			break;
334 		}
335 
336 		/* Read the hostent from the packet. */
337 
338 		h = hostent_from_packet(as->as_type,
339 		    as->as.hostnamadr.family, ar->ar_data, ar->ar_datalen);
340 		free(ar->ar_data);
341 		if (h == NULL) {
342 			ar->ar_errno = errno;
343 			ar->ar_h_errno = NETDB_INTERNAL;
344 			async_set_state(as, ASR_STATE_HALT);
345 			break;
346 		}
347 
348 		if (as->as_type == ASR_GETHOSTBYADDR) {
349 			if (hostent_add_addr(h, as->as.hostnamadr.addr,
350 			    as->as.hostnamadr.addrlen) == -1) {
351 				free(h);
352 				ar->ar_errno = errno;
353 				ar->ar_h_errno = NETDB_INTERNAL;
354 				async_set_state(as, ASR_STATE_HALT);
355 				break;
356 			}
357 		}
358 
359 		/*
360 		 * No valid hostname or address found in the dns packet.
361 		 * Ignore it.
362 		 */
363 		if ((as->as_type == ASR_GETHOSTBYNAME &&
364 		     h->h.h_addr_list[0] == NULL) ||
365 		    h->h.h_name == NULL) {
366 			free(h);
367 			async_set_state(as, ASR_STATE_NEXT_DB);
368 			break;
369 		}
370 
371 		ar->ar_hostent = &h->h;
372 		ar->ar_h_errno = NETDB_SUCCESS;
373 		async_set_state(as, ASR_STATE_HALT);
374 		break;
375 
376 	case ASR_STATE_NOT_FOUND:
377 		ar->ar_errno = 0;
378 		if (as->as.hostnamadr.subq_h_errno)
379 			ar->ar_h_errno = as->as.hostnamadr.subq_h_errno;
380 		else
381 			ar->ar_h_errno = HOST_NOT_FOUND;
382 		async_set_state(as, ASR_STATE_HALT);
383 		break;
384 
385 	case ASR_STATE_HALT:
386 		if (ar->ar_h_errno)
387 			ar->ar_hostent = NULL;
388 		else
389 			ar->ar_errno = 0;
390 		return (ASYNC_DONE);
391 
392 	default:
393 		ar->ar_errno = EOPNOTSUPP;
394 		ar->ar_h_errno = NETDB_INTERNAL;
395 		ar->ar_gai_errno = EAI_SYSTEM;
396 		async_set_state(as, ASR_STATE_HALT);
397 		break;
398 	}
399 	goto next;
400 }
401 
402 /*
403  * Create a hostent from a numeric address string.
404  */
405 static struct hostent_ext *
406 hostent_from_addr(int family, const char *name, const char *addr)
407 {
408 	struct	 hostent_ext *h;
409 
410 	if ((h = hostent_alloc(family)) == NULL)
411 		return (NULL);
412 	if (hostent_set_cname(h, name, 0) == -1)
413 		goto fail;
414 	if (hostent_add_addr(h, addr, h->h.h_length) == -1)
415 		goto fail;
416 	return (h);
417 fail:
418 	free(h);
419 	return (NULL);
420 }
421 
422 /*
423  * Lookup the first matching entry in the hostfile, either by address or by
424  * name depending on reqtype, and build a hostent from the line.
425  */
426 static struct hostent_ext *
427 hostent_file_match(FILE *f, int reqtype, int family, const char *data,
428     int datalen)
429 {
430 	char	*tokens[MAXTOKEN], addr[16];
431 	struct	 hostent_ext *h;
432 	int	 n, i;
433 
434 	for (;;) {
435 		n = asr_parse_namedb_line(f, tokens, MAXTOKEN);
436 		if (n == -1) {
437 			errno = 0; /* ignore errors reading the file */
438 			return (NULL);
439 		}
440 
441 		/* there must be an address and at least one name */
442 		if (n < 2)
443 			continue;
444 
445 		if (reqtype == ASR_GETHOSTBYNAME) {
446 			for (i = 1; i < n; i++) {
447 				if (strcasecmp(data, tokens[i]))
448 					continue;
449 				if (inet_pton(family, tokens[0], addr) == 1)
450 					goto found;
451 			}
452 		} else {
453 			if (inet_pton(family, tokens[0], addr) == 1 &&
454 			    memcmp(addr, data, datalen) == 0)
455 				goto found;
456 		}
457 	}
458 
459 found:
460 	if ((h = hostent_alloc(family)) == NULL)
461 		return (NULL);
462 	if (hostent_set_cname(h, tokens[1], 0) == -1)
463 		goto fail;
464 	for (i = 2; i < n; i ++)
465 		if (hostent_add_alias(h, tokens[i], 0) == -1)
466 			goto fail;
467 	if (hostent_add_addr(h, addr, h->h.h_length) == -1)
468 		goto fail;
469 	return (h);
470 fail:
471 	free(h);
472 	return (NULL);
473 }
474 
475 /*
476  * Fill the hostent from the given DNS packet.
477  */
478 static struct hostent_ext *
479 hostent_from_packet(int reqtype, int family, char *pkt, size_t pktlen)
480 {
481 	struct hostent_ext	*h;
482 	struct asr_unpack	 p;
483 	struct asr_dns_header	 hdr;
484 	struct asr_dns_query	 q;
485 	struct asr_dns_rr	 rr;
486 	char			 dname[MAXDNAME];
487 
488 	if ((h = hostent_alloc(family)) == NULL)
489 		return (NULL);
490 
491 	asr_unpack_init(&p, pkt, pktlen);
492 	asr_unpack_header(&p, &hdr);
493 	for (; hdr.qdcount; hdr.qdcount--)
494 		asr_unpack_query(&p, &q);
495 	strlcpy(dname, q.q_dname, sizeof(dname));
496 
497 	for (; hdr.ancount; hdr.ancount--) {
498 		asr_unpack_rr(&p, &rr);
499 		if (rr.rr_class != C_IN)
500 			continue;
501 		switch (rr.rr_type) {
502 
503 		case T_CNAME:
504 			if (reqtype == ASR_GETHOSTBYNAME) {
505 				if (hostent_add_alias(h, rr.rr_dname, 1) == -1)
506 					goto fail;
507 			} else {
508 				if (strcasecmp(rr.rr_dname, dname) == 0)
509 					strlcpy(dname, rr.rr.cname.cname,
510 					    sizeof(dname));
511 			}
512 			break;
513 
514 		case T_PTR:
515 			if (reqtype != ASR_GETHOSTBYADDR)
516 				break;
517 			if (strcasecmp(rr.rr_dname, dname) != 0)
518 				continue;
519 			if (hostent_set_cname(h, rr.rr.ptr.ptrname, 1) == -1)
520 				hostent_add_alias(h, rr.rr.ptr.ptrname, 1);
521 			break;
522 
523 		case T_A:
524 			if (reqtype != ASR_GETHOSTBYNAME)
525 				break;
526 			if (family != AF_INET)
527 				break;
528 			if (hostent_set_cname(h, rr.rr_dname, 1) == -1)
529 				;
530 			if (hostent_add_addr(h, &rr.rr.in_a.addr, 4) == -1)
531 				goto fail;
532 			break;
533 
534 		case T_AAAA:
535 			if (reqtype != ASR_GETHOSTBYNAME)
536 				break;
537 			if (family != AF_INET6)
538 				break;
539 			if (hostent_set_cname(h, rr.rr_dname, 1) == -1)
540 				;
541 			if (hostent_add_addr(h, &rr.rr.in_aaaa.addr6, 16) == -1)
542 				goto fail;
543 			break;
544 		}
545 	}
546 
547 	return (h);
548 fail:
549 	free(h);
550 	return (NULL);
551 }
552 
553 static struct hostent_ext *
554 hostent_alloc(int family)
555 {
556 	struct hostent_ext	*h;
557 	size_t			alloc;
558 
559 	alloc = sizeof(*h) + 1024;
560 	if ((h = calloc(1, alloc)) == NULL)
561 		return (NULL);
562 
563 	h->h.h_addrtype = family;
564 	h->h.h_length = (family == AF_INET) ? 4 : 16;
565 	h->h.h_aliases = h->aliases;
566 	h->h.h_addr_list = h->addrs;
567 	h->pos = (char *)(h) + sizeof(*h);
568 	h->end = h->pos + 1024;
569 
570 	return (h);
571 }
572 
573 static int
574 hostent_set_cname(struct hostent_ext *h, const char *name, int isdname)
575 {
576 	char	buf[MAXDNAME];
577 	size_t	n;
578 
579 	if (h->h.h_name)
580 		return (-1);
581 
582 	if (isdname) {
583 		asr_strdname(name, buf, sizeof buf);
584 		buf[strlen(buf) - 1] = '\0';
585 		if (!res_hnok(buf))
586 			return (-1);
587 		name = buf;
588 	}
589 
590 	n = strlen(name) + 1;
591 	if (h->pos + n >= h->end)
592 		return (-1);
593 
594 	h->h.h_name = h->pos;
595 	memmove(h->pos, name, n);
596 	h->pos += n;
597 	return (0);
598 }
599 
600 static int
601 hostent_add_alias(struct hostent_ext *h, const char *name, int isdname)
602 {
603 	char	buf[MAXDNAME];
604 	size_t	i, n;
605 
606 	for (i = 0; i < MAXALIASES; i++)
607 		if (h->aliases[i] == NULL)
608 			break;
609 	if (i == MAXALIASES)
610 		return (-1);
611 
612 	if (isdname) {
613 		asr_strdname(name, buf, sizeof buf);
614 		buf[strlen(buf)-1] = '\0';
615 		if (!res_hnok(buf))
616 			return (-1);
617 		name = buf;
618 	}
619 
620 	n = strlen(name) + 1;
621 	if (h->pos + n >= h->end)
622 		return (-1);
623 
624 	h->aliases[i] = h->pos;
625 	memmove(h->pos, name, n);
626 	h->pos += n;
627 	return (0);
628 }
629 
630 static int
631 hostent_add_addr(struct hostent_ext *h, const void *addr, size_t size)
632 {
633 	int	i;
634 
635 	for (i = 0; i < MAXADDRS; i++)
636 		if (h->addrs[i] == NULL)
637 			break;
638 	if (i == MAXADDRS)
639 		return (-1);
640 
641 	if (h->pos + size >= h->end)
642 		return (-1);
643 
644 	h->addrs[i] = h->pos;
645 	memmove(h->pos, addr, size);
646 	h->pos += size;
647 	return (0);
648 }
649 
650 #ifdef YP
651 static struct hostent_ext *
652 _yp_gethostnamadr(int type, const void *data)
653 {
654 	static char		*domain = NULL;
655 	struct hostent_ext	*h = NULL;
656 	const char		*name;
657 	char			 buf[MAXHOSTNAMELEN];
658 	char			*res = NULL;
659 	int			 r, len;
660 
661 	if (!domain && _yp_check(&domain) == 0) {
662 		errno = 0; /* ignore yp_bind errors */
663 		return (NULL);
664 	}
665 
666 	if (type == ASR_GETHOSTBYNAME) {
667 		name = data;
668 		len = strlen(name);
669 		r = yp_match(domain, "hosts.byname", name, len, &res, &len);
670 	}
671 	else {
672 		if (inet_ntop(AF_INET, data, buf, sizeof buf) == NULL)
673 			return (NULL);
674 		len = strlen(buf);
675 		r = yp_match(domain, "hosts.byaddr", buf, len, &res, &len);
676 	}
677 	if (r == 0) {
678 		h = hostent_from_yp(AF_INET, res);
679 	} else {
680 		errno = 0; /* ignore error if not found */
681 	}
682 	if (res)
683 		free(res);
684 	return (h);
685 }
686 
687 static int
688 strsplit(char *line, char **tokens, int ntokens)
689 {
690 	int	ntok;
691 	char	*cp, **tp;
692 
693 	for (cp = line, tp = tokens, ntok = 0;
694 	    ntok < ntokens && (*tp = strsep(&cp, " \t")) != NULL; )
695 		if (**tp != '\0') {
696 			tp++;
697 			ntok++;
698 		}
699 
700 	return (ntok);
701 }
702 
703 static struct hostent_ext *
704 hostent_from_yp(int family, char *line)
705 {
706 	struct hostent_ext	*h;
707 	char			*next, *tokens[10], addr[IN6ADDRSZ];
708 	int			 i, ntok;
709 
710 	if ((h = hostent_alloc(family)) == NULL)
711 		return (NULL);
712 
713 	for (next = line; line; line = next) {
714 		if ((next = strchr(line, '\n'))) {
715 			*next = '\0';
716 			next += 1;
717 		}
718 		ntok = strsplit(line, tokens, 10);
719 		if (ntok < 2)
720 			continue;
721 		if (inet_pton(family, tokens[0], addr) == 1)
722 			hostent_add_addr(h, addr, family == AF_INET ?
723 			    INADDRSZ : IN6ADDRSZ);
724 		i = 2;
725 		if (h->h.h_name == NULL)
726 			hostent_set_cname(h, tokens[1], 0);
727 		else if (strcmp(h->h.h_name, tokens[1]))
728 			i = 1;
729 		for (; i < ntok; i++)
730 			hostent_add_alias(h, tokens[i], 0);
731 	}
732 
733 	if (h->h.h_name == NULL) {
734 		free(h);
735 		return (NULL);
736 	}
737 
738 	return (h);
739 }
740 #endif
741