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