xref: /illumos-gate/usr/src/lib/libresolv2/common/irs/gethostent.c (revision 07a48826732249fcd3aa8dd53c8389595e9f1fbc)
1 /*
2  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*
7  * Copyright (c) 1996-1999 by Internet Software Consortium.
8  *
9  * Permission to use, copy, modify, and distribute this software for any
10  * purpose with or without fee is hereby granted, provided that the above
11  * copyright notice and this permission notice appear in all copies.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
14  * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
15  * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
16  * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
17  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
18  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
19  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
20  * SOFTWARE.
21  */
22 
23 #if !defined(LINT) && !defined(CODECENTER)
24 static const char rcsid[] = "$Id: gethostent.c,v 1.34 2003/05/29 00:05:18 marka Exp $";
25 #endif
26 
27 /* Imports */
28 
29 #include "port_before.h"
30 
31 #if !defined(__BIND_NOSTATIC)
32 
33 #include <sys/types.h>
34 #include <sys/param.h>
35 #include <sys/socket.h>
36 #include <sys/ioctl.h>
37 #include <netinet/in.h>
38 #include <net/if.h>
39 #include <arpa/inet.h>
40 #include <arpa/nameser.h>
41 
42 #include <ctype.h>
43 #include <errno.h>
44 #include <stdlib.h>
45 #include <netdb.h>
46 #include <resolv.h>
47 #include <stdio.h>
48 #include <string.h>
49 #include <unistd.h>
50 
51 #include <irs.h>
52 #include <isc/memcluster.h>
53 
54 #include "port_after.h"
55 
56 #include "irs_p.h"
57 #include "irs_data.h"
58 
59 /* Definitions */
60 
61 struct pvt {
62 	char *		aliases[1];
63 	char *		addrs[2];
64 	char		addr[NS_IN6ADDRSZ];
65 	char		name[NS_MAXDNAME + 1];
66 	struct hostent	host;
67 };
68 
69 /* Forward */
70 
71 static struct net_data *init(void);
72 static void		freepvt(struct net_data *);
73 static struct hostent  *fakeaddr(const char *, int, struct net_data *);
74 
75 #ifdef	SUNW_OVERRIDE_RETRY
76 extern int	__res_retry(int);
77 extern int	__res_retry_reset(void);
78 #endif	/* SUNW_OVERRIDE_RETRY */
79 
80 /* Public */
81 
82 struct hostent *
83 gethostbyname(const char *name) {
84 	struct net_data *net_data = init();
85 
86 	return (gethostbyname_p(name, net_data));
87 }
88 
89 struct hostent *
90 gethostbyname2(const char *name, int af) {
91 	struct net_data *net_data = init();
92 
93 	return (gethostbyname2_p(name, af, net_data));
94 }
95 
96 struct hostent *
97 #ifdef ORIGINAL_ISC_CODE
98 gethostbyaddr(const char *addr, int len, int af) {
99 #else
100 gethostbyaddr(const void *addr, socklen_t len, int af) {
101 #endif /* ORIGINAL_ISC_CODE */
102 	struct net_data *net_data = init();
103 
104 	return (gethostbyaddr_p(addr, len, af, net_data));
105 }
106 
107 struct hostent *
108 gethostent() {
109 	struct net_data *net_data = init();
110 
111 	return (gethostent_p(net_data));
112 }
113 
114 #ifdef	ORIGINAL_ISC_CODE
115 void
116 #else
117 int
118 #endif
119 sethostent(int stayopen) {
120 	struct net_data *net_data = init();
121 	sethostent_p(stayopen, net_data);
122 #ifdef	ORIGINAL_ISC_CODE
123 #else
124 	return (0);
125 #endif
126 }
127 
128 
129 #ifdef	ORIGINAL_ISC_CODE
130 void
131 #else
132 int
133 #endif
134 endhostent() {
135 	struct net_data *net_data = init();
136 	endhostent_p(net_data);
137 #ifdef	ORIGINAL_ISC_CODE
138 #else
139 	return (0);
140 #endif
141 }
142 
143 /* Shared private. */
144 
145 struct hostent *
146 gethostbyname_p(const char *name, struct net_data *net_data) {
147 	struct hostent *hp;
148 
149 	if (!net_data)
150 #ifdef	SUNW_SETHERRNO
151 	{
152 		/*
153 		*  Should set the context h_errno, but since net_data
154 		 * is NULL, we don't have a context.
155 		 */
156 		h_errno = NETDB_INTERNAL;
157 		return (NULL);
158 	}
159 #else
160 		return (NULL);
161 #endif	/* SUNW_SETHERRNO */
162 
163 	if (net_data->res->options & RES_USE_INET6) {
164 		hp = gethostbyname2_p(name, AF_INET6, net_data);
165 		if (hp)
166 			return (hp);
167 	}
168 	return (gethostbyname2_p(name, AF_INET, net_data));
169 }
170 
171 struct hostent *
172 gethostbyname2_p(const char *name, int af, struct net_data *net_data) {
173 	struct irs_ho *ho;
174 	char tmp[NS_MAXDNAME];
175 	struct hostent *hp;
176 	const char *cp;
177 	char **hap;
178 
179 	if (!net_data || !(ho = net_data->ho))
180 #ifdef	SUNW_SETHERRNO
181 	{
182 		h_errno = NETDB_INTERNAL;
183 		return (NULL);
184 	}
185 #else
186 		return (NULL);
187 #endif	/* SUNW_SETHERRNO */
188 	if (net_data->ho_stayopen && net_data->ho_last &&
189 	net_data->ho_last->h_addrtype == af) {
190 		if (ns_samename(name, net_data->ho_last->h_name) == 1)
191 			return (net_data->ho_last);
192 		for (hap = net_data->ho_last->h_aliases; hap && *hap; hap++)
193 			if (ns_samename(name, *hap) == 1)
194 				return (net_data->ho_last);
195 	}
196 	if (!strchr(name, '.') && (cp = res_hostalias(net_data->res, name,
197 						      tmp, sizeof tmp)))
198 		name = cp;
199 	if ((hp = fakeaddr(name, af, net_data)) != NULL)
200 		return (hp);
201 #ifdef	SUNW_OVERRIDE_RETRY
202 	net_data->res->retry = __res_retry(net_data->res->retry);
203 #endif	/* SUNW_OVERRIDE_RETRY */
204 	net_data->ho_last = (*ho->byname2)(ho, name, af);
205 #ifdef	SUNW_OVERRIDE_RETRY
206 	net_data->res->retry = __res_retry_reset();
207 #endif	/* SUNW_OVERRIDE_RETRY */
208 	if (!net_data->ho_stayopen)
209 		endhostent();
210 	return (net_data->ho_last);
211 }
212 
213 struct hostent *
214 gethostbyaddr_p(const char *addr, int len, int af, struct net_data *net_data) {
215 	struct irs_ho *ho;
216 	char **hap;
217 
218 	if (!net_data || !(ho = net_data->ho))
219 #ifdef	SUNW_SETHERRNO
220 	{
221 		h_errno = NETDB_INTERNAL;
222 		return (NULL);
223 	}
224 #else
225 		return (NULL);
226 #endif	/* SUNW_SETHERRNO */
227 	if (net_data->ho_stayopen && net_data->ho_last &&
228 	    net_data->ho_last->h_length == len)
229 		for (hap = net_data->ho_last->h_addr_list;
230 		     hap && *hap;
231 		     hap++)
232 			if (!memcmp(addr, *hap, len))
233 				return (net_data->ho_last);
234 	net_data->ho_last = (*ho->byaddr)(ho, addr, len, af);
235 	if (!net_data->ho_stayopen)
236 		endhostent();
237 	return (net_data->ho_last);
238 }
239 
240 
241 struct hostent *
242 gethostent_p(struct net_data *net_data) {
243 	struct irs_ho *ho;
244 	struct hostent *hp;
245 
246 	if (!net_data || !(ho = net_data->ho))
247 #ifdef	SUNW_SETHERRNO
248 	{
249 		h_errno = NETDB_INTERNAL;
250 		return (NULL);
251 	}
252 #else
253 		return (NULL);
254 #endif	/* SUNW_SETHERRNO */
255 	while ((hp = (*ho->next)(ho)) != NULL &&
256 	       hp->h_addrtype == AF_INET6 &&
257 	       (net_data->res->options & RES_USE_INET6) == 0)
258 		continue;
259 	net_data->ho_last = hp;
260 	return (net_data->ho_last);
261 }
262 
263 
264 void
265 sethostent_p(int stayopen, struct net_data *net_data) {
266 	struct irs_ho *ho;
267 
268 	if (!net_data || !(ho = net_data->ho))
269 		return;
270 	freepvt(net_data);
271 	(*ho->rewind)(ho);
272 	net_data->ho_stayopen = (stayopen != 0);
273 	if (stayopen == 0)
274 		net_data_minimize(net_data);
275 }
276 
277 void
278 endhostent_p(struct net_data *net_data) {
279 	struct irs_ho *ho;
280 
281 	if ((net_data != NULL) && ((ho = net_data->ho) != NULL))
282 		(*ho->minimize)(ho);
283 }
284 
285 #ifndef IN6_IS_ADDR_V4COMPAT
286 static const unsigned char in6addr_compat[12] = {
287 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
288 #define IN6_IS_ADDR_V4COMPAT(x) (!memcmp((x)->s6_addr, in6addr_compat, 12) && \
289 				 ((x)->s6_addr[12] != 0 || \
290 				  (x)->s6_addr[13] != 0 || \
291 				  (x)->s6_addr[14] != 0 || \
292 				   ((x)->s6_addr[15] != 0 && \
293 				    (x)->s6_addr[15] != 1)))
294 #endif
295 #ifndef IN6_IS_ADDR_V4MAPPED
296 #define IN6_IS_ADDR_V4MAPPED(x) (!memcmp((x)->s6_addr, in6addr_mapped, 12))
297 #endif
298 
299 static const unsigned char in6addr_mapped[12] = {
300 	0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0xff, 0xff };
301 
302 static int scan_interfaces(int *, int *);
303 static struct hostent *copyandmerge(struct hostent *, struct hostent *, int, int *);
304 
305 /*
306  *	Public functions
307  */
308 
309 /*
310  *	AI_V4MAPPED + AF_INET6
311  *	If no IPv6 address then a query for IPv4 and map returned values.
312  *
313  *	AI_ALL + AI_V4MAPPED + AF_INET6
314  *	Return IPv6 and IPv4 mapped.
315  *
316  *	AI_ADDRCONFIG
317  *	Only return IPv6 / IPv4 address if there is an interface of that
318  *	type active.
319  */
320 
321 struct hostent *
322 getipnodebyname(const char *name, int af, int flags, int *error_num) {
323 	int have_v4 = 1, have_v6 = 1;
324 	struct in_addr in4;
325 	struct in6_addr in6;
326 	struct hostent he, *he1 = NULL, *he2 = NULL, *he3;
327 	int v4 = 0, v6 = 0;
328 	struct net_data *net_data = init();
329 	u_long options;
330 	int tmp_err;
331 
332 	if (net_data == NULL) {
333 		*error_num = NO_RECOVERY;
334 		return (NULL);
335 	}
336 
337 	/* If we care about active interfaces then check. */
338 	if ((flags & AI_ADDRCONFIG) != 0)
339 		if (scan_interfaces(&have_v4, &have_v6) == -1) {
340 			*error_num = NO_RECOVERY;
341 			return (NULL);
342 		}
343 
344 	/* Check for literal address. */
345 	if ((v4 = inet_pton(AF_INET, name, &in4)) != 1)
346 		v6 = inet_pton(AF_INET6, name, &in6);
347 
348 	/* Impossible combination? */
349 
350 	if ((af == AF_INET6 && (flags & AI_V4MAPPED) == 0 && v4 == 1) ||
351 	    (af == AF_INET && v6 == 1) ||
352 	    (have_v4 == 0 && v4 == 1) ||
353 	    (have_v6 == 0 && v6 == 1) ||
354 	    (have_v4 == 0 && af == AF_INET) ||
355 	    (have_v6 == 0 && af == AF_INET6)) {
356 		*error_num = HOST_NOT_FOUND;
357 		return (NULL);
358 	}
359 
360 	/* Literal address? */
361 	if (v4 == 1 || v6 == 1) {
362 		char *addr_list[2];
363 		char *aliases[1];
364 
365 		DE_CONST(name, he.h_name);
366 		he.h_addr_list = addr_list;
367 		he.h_addr_list[0] = (v4 == 1) ? (char *)&in4 : (char *)&in6;
368 		he.h_addr_list[1] = NULL;
369 		he.h_aliases = aliases;
370 		he.h_aliases[0] = NULL;
371 		he.h_length = (v4 == 1) ? INADDRSZ : IN6ADDRSZ;
372 		he.h_addrtype = (v4 == 1) ? AF_INET : AF_INET6;
373 		return (copyandmerge(&he, NULL, af, error_num));
374 	}
375 
376 	options = net_data->res->options;
377 	net_data->res->options &= ~RES_USE_INET6;
378 
379 	tmp_err = NO_RECOVERY;
380 	if (have_v6 && af == AF_INET6) {
381 		he2 = gethostbyname2_p(name, AF_INET6, net_data);
382 		if (he2 != NULL) {
383 			he1 = copyandmerge(he2, NULL, af, error_num);
384 			if (he1 == NULL)
385 				return (NULL);
386 			he2 = NULL;
387 		} else {
388 			tmp_err = net_data->res->res_h_errno;
389 		}
390 	}
391 
392 	if (have_v4 &&
393 	    ((af == AF_INET) ||
394 	     (af == AF_INET6 && (flags & AI_V4MAPPED) != 0 &&
395 	      (he1 == NULL || (flags & AI_ALL) != 0)))) {
396 		he2 = gethostbyname2_p(name, AF_INET, net_data);
397 		if (he1 == NULL && he2 == NULL) {
398 			*error_num = net_data->res->res_h_errno;
399 			return (NULL);
400 		}
401 	} else
402 		*error_num = tmp_err;
403 
404 	net_data->res->options = options;
405 
406 	he3 = copyandmerge(he1, he2, af, error_num);
407 
408 	if (he1 != NULL)
409 		freehostent(he1);
410 	return (he3);
411 }
412 
413 struct hostent *
414 getipnodebyaddr(const void *src, size_t len, int af, int *error_num) {
415 	struct hostent *he1, *he2;
416 	struct net_data *net_data = init();
417 
418 	/* Sanity Checks. */
419 	if (src == NULL || net_data == NULL) {
420 		*error_num = NO_RECOVERY;
421 		return (NULL);
422 	}
423 
424 	switch (af) {
425 	case AF_INET:
426 		if (len != INADDRSZ) {
427 			*error_num = NO_RECOVERY;
428 			return (NULL);
429 		}
430 		break;
431 	case AF_INET6:
432 		if (len != IN6ADDRSZ) {
433 			*error_num = NO_RECOVERY;
434 			return (NULL);
435 		}
436 		break;
437 	default:
438 		*error_num = NO_RECOVERY;
439 		return (NULL);
440 	}
441 
442 	/*
443 	 * Lookup IPv4 and IPv4 mapped/compatible addresses
444 	 */
445 	if ((af == AF_INET6 &&
446 	     IN6_IS_ADDR_V4COMPAT((const struct in6_addr *)src)) ||
447 	    (af == AF_INET6 &&
448 	     IN6_IS_ADDR_V4MAPPED((const struct in6_addr *)src)) ||
449 	    (af == AF_INET)) {
450 		const char *cp = src;
451 
452 		if (af == AF_INET6)
453 			cp += 12;
454 		he1 = gethostbyaddr_p(cp, 4, AF_INET, net_data);
455 		if (he1 == NULL) {
456 			*error_num = net_data->res->res_h_errno;
457 			return (NULL);
458 		}
459 		he2 = copyandmerge(he1, NULL, af, error_num);
460 		if (he2 == NULL)
461 			return (NULL);
462 		/*
463 		 * Restore original address if mapped/compatible.
464 		 */
465 		if (af == AF_INET6)
466 			memcpy(he1->h_addr, src, len);
467 		return (he2);
468 	}
469 
470 	/*
471 	 * Lookup IPv6 address.
472 	 */
473 	if (memcmp((const struct in6_addr *)src, &in6addr_any, 16) == 0) {
474 		*error_num = HOST_NOT_FOUND;
475 		return (NULL);
476 	}
477 
478 	he1 = gethostbyaddr_p(src, 16, AF_INET6, net_data);
479 	if (he1 == NULL) {
480 		*error_num = net_data->res->res_h_errno;
481 		return (NULL);
482 	}
483 	return (copyandmerge(he1, NULL, af, error_num));
484 }
485 
486 void
487 freehostent(struct hostent *he) {
488 	char **cpp;
489 	int names = 1;
490 	int addresses = 1;
491 
492 	memput(he->h_name, strlen(he->h_name) + 1);
493 
494 	cpp = he->h_addr_list;
495 	while (*cpp != NULL) {
496 		memput(*cpp, (he->h_addrtype == AF_INET) ?
497 			     INADDRSZ : IN6ADDRSZ);
498 		*cpp = NULL;
499 		cpp++;
500 		addresses++;
501 	}
502 
503 	cpp = he->h_aliases;
504 	while (*cpp != NULL) {
505 		memput(*cpp, strlen(*cpp) + 1);
506 		cpp++;
507 		names++;
508 	}
509 
510 	memput(he->h_aliases, sizeof(char *) * (names));
511 	memput(he->h_addr_list, sizeof(char *) * (addresses));
512 	memput(he, sizeof *he);
513 }
514 
515 /*
516  * Private
517  */
518 
519 /*
520  * Scan the interface table and set have_v4 and have_v6 depending
521  * upon whether there are IPv4 and IPv6 interface addresses.
522  *
523  * Returns:
524  *	0 on success
525  *	-1 on failure.
526  */
527 
528 #if defined(SIOCGLIFCONF) && defined(SIOCGLIFADDR) && \
529     !defined(IRIX_EMUL_IOCTL_SIOCGIFCONF)
530 
531 #define SETFAMILYFLAGS
532 #define LIFCONF lifconf
533 #define LIFREQ lifreq
534 
535 static void
536 scan_interfaces6(int *have_v4, int *have_v6) {
537 	struct LIFCONF lifc;
538 	struct LIFREQ lifreq;
539 	struct in_addr in4;
540 	struct in6_addr in6;
541 	char *buf = NULL, *cp, *cplim;
542 	static unsigned int bufsiz = 4095;
543 	int s, cpsize, n;
544 
545 	/* Get interface list from system. */
546 	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) == -1)
547 		goto cleanup;
548 
549 	/*
550 	 * Grow buffer until large enough to contain all interface
551 	 * descriptions.
552 	 */
553 	for (;;) {
554 		buf = memget(bufsiz);
555 		if (buf == NULL)
556 			goto cleanup;
557 #ifdef SETFAMILYFLAGS
558 		lifc.lifc_family = AF_UNSPEC;	/* request all families */
559 		lifc.lifc_flags = 0;
560 #endif
561 		lifc.lifc_len = bufsiz;
562 		lifc.lifc_buf = buf;
563 		if ((n = ioctl(s, SIOCGLIFCONF, (char *)&lifc)) != -1) {
564 			/*
565 			 * Some OS's just return what will fit rather
566 			 * than set EINVAL if the buffer is too small
567 			 * to fit all the interfaces in.  If
568 			 * lifc.lifc_len is too near to the end of the
569 			 * buffer we will grow it just in case and
570 			 * retry.
571 			 */
572 			if (lifc.lifc_len + 2 * sizeof(lifreq) < bufsiz)
573 				break;
574 		}
575 		if ((n == -1) && errno != EINVAL)
576 			goto cleanup;
577 
578 		if (bufsiz > 1000000)
579 			goto cleanup;
580 
581 		memput(buf, bufsiz);
582 		bufsiz += 4096;
583 	}
584 
585 	/* Parse system's interface list. */
586 	cplim = buf + lifc.lifc_len;    /* skip over if's with big ifr_addr's */
587 	for (cp = buf;
588 	     (*have_v4 == 0 || *have_v6 == 0) && cp < cplim;
589 	     cp += cpsize) {
590 		memcpy(&lifreq, cp, sizeof lifreq);
591 #ifdef HAVE_SA_LEN
592 #ifdef FIX_ZERO_SA_LEN
593 		if (lifreq.lifr_addr.sa_len == 0)
594 			lifreq.lifr_addr.sa_len = 16;
595 #endif
596 #ifdef HAVE_MINIMUM_IFREQ
597 		cpsize = sizeof lifreq;
598 		if (lifreq.lifr_addr.sa_len > sizeof (struct sockaddr))
599 			cpsize += (int)lifreq.lifr_addr.sa_len -
600 				(int)(sizeof (struct sockaddr));
601 #else
602 		cpsize = sizeof lifreq.lifr_name + lifreq.lifr_addr.sa_len;
603 #endif /* HAVE_MINIMUM_IFREQ */
604 #elif defined SIOCGIFCONF_ADDR
605 		cpsize = sizeof lifreq;
606 #else
607 		cpsize = sizeof lifreq.lifr_name;
608 		/* XXX maybe this should be a hard error? */
609 		if (ioctl(s, SIOCGLIFADDR, (char *)&lifreq) < 0)
610 			continue;
611 #endif
612 		switch (lifreq.lifr_addr.ss_family) {
613 		case AF_INET:
614 			if (*have_v4 == 0) {
615 				memcpy(&in4,
616 				       &((struct sockaddr_in *)
617 				       &lifreq.lifr_addr)->sin_addr,
618 				       sizeof in4);
619 				if (in4.s_addr == INADDR_ANY)
620 					break;
621 				n = ioctl(s, SIOCGLIFFLAGS, (char *)&lifreq);
622 				if (n < 0)
623 					break;
624 				if ((lifreq.lifr_flags & IFF_UP) == 0)
625 					break;
626 				*have_v4 = 1;
627 			}
628 			break;
629 		case AF_INET6:
630 			if (*have_v6 == 0) {
631 				memcpy(&in6,
632 				       &((struct sockaddr_in6 *)
633 				       &lifreq.lifr_addr)->sin6_addr, sizeof in6);
634 				if (memcmp(&in6, &in6addr_any, sizeof in6) == 0)
635 					break;
636 				n = ioctl(s, SIOCGLIFFLAGS, (char *)&lifreq);
637 				if (n < 0)
638 					break;
639 				if ((lifreq.lifr_flags & IFF_UP) == 0)
640 					break;
641 				*have_v6 = 1;
642 			}
643 			break;
644 		}
645 	}
646 	if (buf != NULL)
647 		memput(buf, bufsiz);
648 	close(s);
649 	/* printf("scan interface -> 4=%d 6=%d\n", *have_v4, *have_v6); */
650 	return;
651  cleanup:
652 	if (buf != NULL)
653 		memput(buf, bufsiz);
654 	if (s != -1)
655 		close(s);
656 	/* printf("scan interface -> 4=%d 6=%d\n", *have_v4, *have_v6); */
657 	return;
658 }
659 #endif
660 
661 static int
662 scan_interfaces(int *have_v4, int *have_v6) {
663 	struct ifconf ifc;
664 	union {
665 		char _pad[256];		/* leave space for IPv6 addresses */
666 		struct ifreq ifreq;
667 	} u;
668 	struct in_addr in4;
669 	struct in6_addr in6;
670 	char *buf = NULL, *cp, *cplim;
671 	static unsigned int bufsiz = 4095;
672 	int s, n;
673 	size_t cpsize;
674 
675 	/* Set to zero.  Used as loop terminators below. */
676 	*have_v4 = *have_v6 = 0;
677 
678 #if defined(SIOCGLIFCONF) && defined(SIOCGLIFADDR) && \
679     !defined(IRIX_EMUL_IOCTL_SIOCGIFCONF)
680 	/*
681 	 * Try to scan the interfaces using IPv6 ioctls().
682 	 */
683 	scan_interfaces6(have_v4, have_v6);
684 	if (*have_v4 != 0 && *have_v6 != 0)
685 		return (0);
686 #endif
687 
688 	/* Get interface list from system. */
689 	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
690 		goto err_ret;
691 
692 	/*
693 	 * Grow buffer until large enough to contain all interface
694 	 * descriptions.
695 	 */
696 	for (;;) {
697 		buf = memget(bufsiz);
698 		if (buf == NULL)
699 			goto err_ret;
700 		ifc.ifc_len = bufsiz;
701 		ifc.ifc_buf = buf;
702 #ifdef IRIX_EMUL_IOCTL_SIOCGIFCONF
703 		/*
704 		 * This is a fix for IRIX OS in which the call to ioctl with
705 		 * the flag SIOCGIFCONF may not return an entry for all the
706 		 * interfaces like most flavors of Unix.
707 		 */
708 		if (emul_ioctl(&ifc) >= 0)
709 			break;
710 #else
711 		if ((n = ioctl(s, SIOCGIFCONF, (char *)&ifc)) != -1) {
712 			/*
713 			 * Some OS's just return what will fit rather
714 			 * than set EINVAL if the buffer is too small
715 			 * to fit all the interfaces in.  If
716 			 * ifc.ifc_len is too near to the end of the
717 			 * buffer we will grow it just in case and
718 			 * retry.
719 			 */
720 			if (ifc.ifc_len + 2 * sizeof(u.ifreq) < bufsiz)
721 				break;
722 		}
723 #endif
724 		if ((n == -1) && errno != EINVAL)
725 			goto err_ret;
726 
727 		if (bufsiz > 1000000)
728 			goto err_ret;
729 
730 		memput(buf, bufsiz);
731 		bufsiz += 4096;
732 	}
733 
734 	/* Parse system's interface list. */
735 	cplim = buf + ifc.ifc_len;    /* skip over if's with big ifr_addr's */
736 	for (cp = buf;
737 	     (*have_v4 == 0 || *have_v6 == 0) && cp < cplim;
738 	     cp += cpsize) {
739 		memcpy(&u.ifreq, cp, sizeof u.ifreq);
740 #ifdef HAVE_SA_LEN
741 #ifdef FIX_ZERO_SA_LEN
742 		if (u.ifreq.ifr_addr.sa_len == 0)
743 			u.ifreq.ifr_addr.sa_len = 16;
744 #endif
745 #ifdef HAVE_MINIMUM_IFREQ
746 		cpsize = sizeof u.ifreq;
747 		if (u.ifreq.ifr_addr.sa_len > sizeof (struct sockaddr))
748 			cpsize += (int)u.ifreq.ifr_addr.sa_len -
749 				(int)(sizeof (struct sockaddr));
750 #else
751 		cpsize = sizeof u.ifreq.ifr_name + u.ifreq.ifr_addr.sa_len;
752 #endif /* HAVE_MINIMUM_IFREQ */
753 		if (cpsize > sizeof u.ifreq && cpsize <= sizeof u)
754 			memcpy(&u.ifreq, cp, cpsize);
755 #elif defined SIOCGIFCONF_ADDR
756 		cpsize = sizeof u.ifreq;
757 #else
758 		cpsize = sizeof u.ifreq.ifr_name;
759 		/* XXX maybe this should be a hard error? */
760 		if (ioctl(s, SIOCGIFADDR, (char *)&u.ifreq) < 0)
761 			continue;
762 #endif
763 		switch (u.ifreq.ifr_addr.sa_family) {
764 		case AF_INET:
765 			if (*have_v4 == 0) {
766 				memcpy(&in4,
767 				       &((struct sockaddr_in *)
768 				       &u.ifreq.ifr_addr)->sin_addr,
769 				       sizeof in4);
770 				if (in4.s_addr == INADDR_ANY)
771 					break;
772 				n = ioctl(s, SIOCGIFFLAGS, (char *)&u.ifreq);
773 				if (n < 0)
774 					break;
775 				if ((u.ifreq.ifr_flags & IFF_UP) == 0)
776 					break;
777 				*have_v4 = 1;
778 			}
779 			break;
780 		case AF_INET6:
781 			if (*have_v6 == 0) {
782 				memcpy(&in6,
783 				       &((struct sockaddr_in6 *)
784 				       &u.ifreq.ifr_addr)->sin6_addr,
785 				       sizeof in6);
786 				if (memcmp(&in6, &in6addr_any, sizeof in6) == 0)
787 					break;
788 				n = ioctl(s, SIOCGIFFLAGS, (char *)&u.ifreq);
789 				if (n < 0)
790 					break;
791 				if ((u.ifreq.ifr_flags & IFF_UP) == 0)
792 					break;
793 				*have_v6 = 1;
794 			}
795 			break;
796 		}
797 	}
798 	if (buf != NULL)
799 		memput(buf, bufsiz);
800 	close(s);
801 	/* printf("scan interface -> 4=%d 6=%d\n", *have_v4, *have_v6); */
802 	return (0);
803  err_ret:
804 	if (buf != NULL)
805 		memput(buf, bufsiz);
806 	if (s != -1)
807 		close(s);
808 	/* printf("scan interface -> 4=%d 6=%d\n", *have_v4, *have_v6); */
809 	return (-1);
810 }
811 
812 static struct hostent *
813 copyandmerge(struct hostent *he1, struct hostent *he2, int af, int *error_num) {
814 	struct hostent *he = NULL;
815 	int addresses = 1;	/* NULL terminator */
816 	int names = 1;		/* NULL terminator */
817 	int len = 0;
818 	char **cpp, **npp;
819 
820 	/*
821 	 * Work out array sizes;
822 	 */
823 	if (he1 != NULL) {
824 		cpp = he1->h_addr_list;
825 		while (*cpp != NULL) {
826 			addresses++;
827 			cpp++;
828 		}
829 		cpp = he1->h_aliases;
830 		while (*cpp != NULL) {
831 			names++;
832 			cpp++;
833 		}
834 	}
835 
836 	if (he2 != NULL) {
837 		cpp = he2->h_addr_list;
838 		while (*cpp != NULL) {
839 			addresses++;
840 			cpp++;
841 		}
842 		if (he1 == NULL) {
843 			cpp = he2->h_aliases;
844 			while (*cpp != NULL) {
845 				names++;
846 				cpp++;
847 			}
848 		}
849 	}
850 
851 	if (addresses == 1) {
852 		*error_num = NO_ADDRESS;
853 		return (NULL);
854 	}
855 
856 	he = memget(sizeof *he);
857 	if (he == NULL)
858 		goto no_recovery;
859 
860 	he->h_addr_list = memget(sizeof(char *) * (addresses));
861 	if (he->h_addr_list == NULL)
862 		goto cleanup0;
863 	memset(he->h_addr_list, 0, sizeof(char *) * (addresses));
864 
865 	/* copy addresses */
866 	npp = he->h_addr_list;
867 	if (he1 != NULL) {
868 		cpp = he1->h_addr_list;
869 		while (*cpp != NULL) {
870 			*npp = memget((af == AF_INET) ? INADDRSZ : IN6ADDRSZ);
871 			if (*npp == NULL)
872 				goto cleanup1;
873 			/* convert to mapped if required */
874 			if (af == AF_INET6 && he1->h_addrtype == AF_INET) {
875 				memcpy(*npp, in6addr_mapped,
876 				       sizeof in6addr_mapped);
877 				memcpy(*npp + sizeof in6addr_mapped, *cpp,
878 				       INADDRSZ);
879 			} else {
880 				memcpy(*npp, *cpp,
881 				       (af == AF_INET) ? INADDRSZ : IN6ADDRSZ);
882 			}
883 			cpp++;
884 			npp++;
885 		}
886 	}
887 
888 	if (he2 != NULL) {
889 		cpp = he2->h_addr_list;
890 		while (*cpp != NULL) {
891 			*npp = memget((af == AF_INET) ? INADDRSZ : IN6ADDRSZ);
892 			if (*npp == NULL)
893 				goto cleanup1;
894 			/* convert to mapped if required */
895 			if (af == AF_INET6 && he2->h_addrtype == AF_INET) {
896 				memcpy(*npp, in6addr_mapped,
897 				       sizeof in6addr_mapped);
898 				memcpy(*npp + sizeof in6addr_mapped, *cpp,
899 				       INADDRSZ);
900 			} else {
901 				memcpy(*npp, *cpp,
902 				       (af == AF_INET) ? INADDRSZ : IN6ADDRSZ);
903 			}
904 			cpp++;
905 			npp++;
906 		}
907 	}
908 
909 	he->h_aliases = memget(sizeof(char *) * (names));
910 	if (he->h_aliases == NULL)
911 		goto cleanup1;
912 	memset(he->h_aliases, 0, sizeof(char *) * (names));
913 
914 	/* copy aliases */
915 	npp = he->h_aliases;
916 	cpp = (he1 != NULL) ? he1->h_aliases : he2->h_aliases;
917 	while (*cpp != NULL) {
918 		len = strlen (*cpp) + 1;
919 		*npp = memget(len);
920 		if (*npp == NULL)
921 			goto cleanup2;
922 		strcpy(*npp, *cpp);
923 		npp++;
924 		cpp++;
925 	}
926 
927 	/* copy hostname */
928 	he->h_name = memget(strlen((he1 != NULL) ?
929 			    he1->h_name : he2->h_name) + 1);
930 	if (he->h_name == NULL)
931 		goto cleanup2;
932 	strcpy(he->h_name, (he1 != NULL) ? he1->h_name : he2->h_name);
933 
934 	/* set address type and length */
935 	he->h_addrtype = af;
936 	he->h_length = (af == AF_INET) ? INADDRSZ : IN6ADDRSZ;
937 	return(he);
938 
939  cleanup2:
940 	cpp = he->h_aliases;
941 	while (*cpp != NULL) {
942 		memput(*cpp, strlen(*cpp) + 1);
943 		cpp++;
944 	}
945 	memput(he->h_aliases, sizeof(char *) * (names));
946 
947  cleanup1:
948 	cpp = he->h_addr_list;
949 	while (*cpp != NULL) {
950 		memput(*cpp, (af == AF_INET) ? INADDRSZ : IN6ADDRSZ);
951 		*cpp = NULL;
952 		cpp++;
953 	}
954 	memput(he->h_addr_list, sizeof(char *) * (addresses));
955 
956  cleanup0:
957 	memput(he, sizeof *he);
958 
959  no_recovery:
960 	*error_num = NO_RECOVERY;
961 	return (NULL);
962 }
963 
964 static struct net_data *
965 init() {
966 	struct net_data *net_data;
967 
968 	if (!(net_data = net_data_init(NULL)))
969 		goto error;
970 	if (!net_data->ho) {
971 		net_data->ho = (*net_data->irs->ho_map)(net_data->irs);
972 		if (!net_data->ho || !net_data->res) {
973   error:
974 			errno = EIO;
975 			if (net_data && net_data->res)
976 				RES_SET_H_ERRNO(net_data->res, NETDB_INTERNAL);
977 			return (NULL);
978 		}
979 
980 		(*net_data->ho->res_set)(net_data->ho, net_data->res, NULL);
981 	}
982 
983 	return (net_data);
984 }
985 
986 static void
987 freepvt(struct net_data *net_data) {
988 	if (net_data->ho_data) {
989 		free(net_data->ho_data);
990 		net_data->ho_data = NULL;
991 	}
992 }
993 
994 static struct hostent *
995 fakeaddr(const char *name, int af, struct net_data *net_data) {
996 	struct pvt *pvt;
997 
998 	freepvt(net_data);
999 	net_data->ho_data = malloc(sizeof (struct pvt));
1000 	if (!net_data->ho_data) {
1001 		errno = ENOMEM;
1002 		RES_SET_H_ERRNO(net_data->res, NETDB_INTERNAL);
1003 		return (NULL);
1004 	}
1005 	pvt = net_data->ho_data;
1006 #ifndef __bsdi__
1007 	/*
1008 	 * Unlike its forebear(inet_aton), our friendly inet_pton() is strict
1009 	 * in its interpretation of its input, and it will only return "1" if
1010 	 * the input string is a formally valid(and thus unambiguous with
1011 	 * respect to host names) internet address specification for this AF.
1012 	 *
1013 	 * This means "telnet 0xdeadbeef" and "telnet 127.1" are dead now.
1014 	 */
1015 	if (inet_pton(af, name, pvt->addr) != 1) {
1016 #else
1017 	/* BSDI XXX
1018 	 * We put this back to inet_aton -- we really want the old behavior
1019 	 * Long live 127.1...
1020 	 */
1021 	if ((af != AF_INET ||
1022 	    inet_aton(name, (struct in_addr *)pvt->addr) != 1) &&
1023 	    inet_pton(af, name, pvt->addr) != 1) {
1024 #endif
1025 		RES_SET_H_ERRNO(net_data->res, HOST_NOT_FOUND);
1026 		return (NULL);
1027 	}
1028 	strncpy(pvt->name, name, NS_MAXDNAME);
1029 	pvt->name[NS_MAXDNAME] = '\0';
1030 	if (af == AF_INET && (net_data->res->options & RES_USE_INET6) != 0) {
1031 		map_v4v6_address(pvt->addr, pvt->addr);
1032 		af = AF_INET6;
1033 	}
1034 	pvt->host.h_addrtype = af;
1035 	switch(af) {
1036 	case AF_INET:
1037 		pvt->host.h_length = NS_INADDRSZ;
1038 		break;
1039 	case AF_INET6:
1040 		pvt->host.h_length = NS_IN6ADDRSZ;
1041 		break;
1042 	default:
1043 		errno = EAFNOSUPPORT;
1044 		RES_SET_H_ERRNO(net_data->res, NETDB_INTERNAL);
1045 		return (NULL);
1046 	}
1047 	pvt->host.h_name = pvt->name;
1048 	pvt->host.h_aliases = pvt->aliases;
1049 	pvt->aliases[0] = NULL;
1050 	pvt->addrs[0] = (char *)pvt->addr;
1051 	pvt->addrs[1] = NULL;
1052 	pvt->host.h_addr_list = pvt->addrs;
1053 	RES_SET_H_ERRNO(net_data->res, NETDB_SUCCESS);
1054 	return (&pvt->host);
1055 }
1056 
1057 #ifdef grot	/* for future use in gethostbyaddr(), for "SUNSECURITY" */
1058 	struct hostent *rhp;
1059 	char **haddr;
1060 	u_long old_options;
1061 	char hname2[MAXDNAME+1];
1062 
1063 	if (af == AF_INET) {
1064 	    /*
1065 	     * turn off search as the name should be absolute,
1066 	     * 'localhost' should be matched by defnames
1067 	     */
1068 	    strncpy(hname2, hp->h_name, MAXDNAME);
1069 	    hname2[MAXDNAME] = '\0';
1070 	    old_options = net_data->res->options;
1071 	    net_data->res->options &= ~RES_DNSRCH;
1072 	    net_data->res->options |= RES_DEFNAMES;
1073 	    if (!(rhp = gethostbyname(hname2))) {
1074 		net_data->res->options = old_options;
1075 		RES_SET_H_ERRNO(net_data->res, HOST_NOT_FOUND);
1076 		return (NULL);
1077 	    }
1078 	    net_data->res->options = old_options;
1079 	    for (haddr = rhp->h_addr_list; *haddr; haddr++)
1080 		if (!memcmp(*haddr, addr, INADDRSZ))
1081 			break;
1082 	    if (!*haddr) {
1083 		RES_SET_H_ERRNO(net_data->res, HOST_NOT_FOUND);
1084 		return (NULL);
1085 	    }
1086 	}
1087 #endif /* grot */
1088 
1089 #endif /*__BIND_NOSTATIC*/
1090