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