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