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