1/*
2 * Copyright (C) 2004, 2005, 2007, 2009  Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 1999-2003  Internet Software Consortium.
4 *
5 * Permission to use, copy, modify, and/or 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 WITH
10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15 * PERFORMANCE OF THIS SOFTWARE.
16 */
17
18/* $Id: getipnode.c,v 1.47 2009/09/01 23:47:45 tbox Exp $ */
19
20/*! \file */
21
22/**
23 *    These functions perform thread safe, protocol independent
24 *    nodename-to-address and address-to-nodename translation as defined in
25 *    RFC2553.  This use a struct hostent which is defined in namedb.h:
26 *
27 * \code
28 * struct  hostent {
29 *         char    *h_name;        // official name of host
30 *         char    **h_aliases;    // alias list
31 *         int     h_addrtype;     // host address type
32 *         int     h_length;       // length of address
33 *         char    **h_addr_list;  // list of addresses from name server
34 * };
35 * #define h_addr  h_addr_list[0]  // address, for backward compatibility
36 * \endcode
37 *
38 *    The members of this structure are:
39 *
40 * \li   h_name:
41 *           The official (canonical) name of the host.
42 *
43 * \li   h_aliases:
44 *           A NULL-terminated array of alternate names (nicknames) for the
45 *           host.
46 *
47 * \li   h_addrtype:
48 *           The type of address being returned - usually PF_INET or
49 *           PF_INET6.
50 *
51 * \li   h_length:
52 *           The length of the address in bytes.
53 *
54 * \li   h_addr_list:
55 *           A NULL terminated array of network addresses for the host. Host
56 *           addresses are returned in network byte order.
57 *
58 *    lwres_getipnodebyname() looks up addresses of protocol family af for
59 *    the hostname name. The flags parameter contains ORed flag bits to
60 *    specify the types of addresses that are searched for, and the types of
61 *    addresses that are returned. The flag bits are:
62 *
63 * \li   #AI_V4MAPPED:
64 *           This is used with an af of #AF_INET6, and causes IPv4 addresses
65 *           to be returned as IPv4-mapped IPv6 addresses.
66 *
67 * \li   #AI_ALL:
68 *           This is used with an af of #AF_INET6, and causes all known
69 *           addresses (IPv6 and IPv4) to be returned. If #AI_V4MAPPED is
70 *           also set, the IPv4 addresses are return as mapped IPv6
71 *           addresses.
72 *
73 * \li   #AI_ADDRCONFIG:
74 *           Only return an IPv6 or IPv4 address if here is an active
75 *           network interface of that type. This is not currently
76 *           implemented in the BIND 9 lightweight resolver, and the flag is
77 *           ignored.
78 *
79 * \li   #AI_DEFAULT:
80 *           This default sets the #AI_V4MAPPED and #AI_ADDRCONFIG flag bits.
81 *
82 *    lwres_getipnodebyaddr() performs a reverse lookup of address src which
83 *    is len bytes long. af denotes the protocol family, typically PF_INET
84 *    or PF_INET6.
85 *
86 *    lwres_freehostent() releases all the memory associated with the struct
87 *    hostent pointer. Any memory allocated for the h_name, h_addr_list
88 *    and h_aliases is freed, as is the memory for the hostent structure
89 *    itself.
90 *
91 * \section getipnode_return Return Values
92 *
93 *    If an error occurs, lwres_getipnodebyname() and
94 *    lwres_getipnodebyaddr() set *error_num to an appropriate error code
95 *    and the function returns a NULL pointer. The error codes and their
96 *    meanings are defined in \link netdb.h <lwres/netdb.h>\endlink:
97 *
98 * \li   #HOST_NOT_FOUND:
99 *           No such host is known.
100 *
101 * \li   #NO_ADDRESS:
102 *           The server recognised the request and the name but no address
103 *           is available. Another type of request to the name server for
104 *           the domain might return an answer.
105 *
106 * \li   #TRY_AGAIN:
107 *           A temporary and possibly transient error occurred, such as a
108 *           failure of a server to respond. The request may succeed if
109 *           retried.
110 *
111 * \li   #NO_RECOVERY:
112 *           An unexpected failure occurred, and retrying the request is
113 *           pointless.
114 *
115 *    lwres_hstrerror() translates these error codes to suitable error
116 *    messages.
117 *
118 * \section getipnode_see See Also
119 *
120 * getaddrinfo.c, gethost.c, getnameinfo.c, herror.c, RFC2553
121 */
122
123#include <config.h>
124
125#include <stdio.h>
126#include <stdlib.h>
127#include <string.h>
128#include <errno.h>
129
130#include <lwres/lwres.h>
131#include <lwres/net.h>
132#include <lwres/netdb.h>	/* XXX #include <netdb.h> */
133
134#include "assert_p.h"
135
136#ifndef INADDRSZ
137#define INADDRSZ 4
138#endif
139#ifndef IN6ADDRSZ
140#define IN6ADDRSZ 16
141#endif
142
143#ifdef LWRES_PLATFORM_NEEDIN6ADDRANY
144LIBLWRES_EXTERNAL_DATA const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
145#endif
146
147#ifndef IN6_IS_ADDR_V4COMPAT
148static const unsigned char in6addr_compat[12] = {
149	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
150};
151#define IN6_IS_ADDR_V4COMPAT(x) (!memcmp((x)->s6_addr, in6addr_compat, 12) && \
152				 ((x)->s6_addr[12] != 0 || \
153				  (x)->s6_addr[13] != 0 || \
154				  (x)->s6_addr[14] != 0 || \
155				   ((x)->s6_addr[15] != 0 && \
156				    (x)->s6_addr[15] != 1)))
157#endif
158#ifndef IN6_IS_ADDR_V4MAPPED
159#define IN6_IS_ADDR_V4MAPPED(x) (!memcmp((x)->s6_addr, in6addr_mapped, 12))
160#endif
161
162static const unsigned char in6addr_mapped[12] = {
163	0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0xff, 0xff
164};
165
166/***
167 ***	Forward declarations.
168 ***/
169
170static int
171scan_interfaces(int *, int *);
172
173static struct hostent *
174copyandmerge(struct hostent *, struct hostent *, int, int *);
175
176static struct hostent *
177hostfromaddr(lwres_gnbaresponse_t *addr, int af, const void *src);
178
179static struct hostent *
180hostfromname(lwres_gabnresponse_t *name, int af);
181
182/***
183 ***	Public functions.
184 ***/
185
186/*!
187 *	AI_V4MAPPED + AF_INET6
188 *	If no IPv6 address then a query for IPv4 and map returned values.
189 *
190 *	AI_ALL + AI_V4MAPPED + AF_INET6
191 *	Return IPv6 and IPv4 mapped.
192 *
193 *	AI_ADDRCONFIG
194 *	Only return IPv6 / IPv4 address if there is an interface of that
195 *	type active.
196 */
197
198struct hostent *
199lwres_getipnodebyname(const char *name, int af, int flags, int *error_num) {
200	int have_v4 = 1, have_v6 = 1;
201	struct in_addr in4;
202	struct in6_addr in6;
203	struct hostent he, *he1 = NULL, *he2 = NULL, *he3 = NULL;
204	int v4 = 0, v6 = 0;
205	int tmp_err = 0;
206	lwres_context_t *lwrctx = NULL;
207	lwres_gabnresponse_t *by = NULL;
208	int n;
209
210	/*
211	 * If we care about active interfaces then check.
212	 */
213	if ((flags & AI_ADDRCONFIG) != 0)
214		if (scan_interfaces(&have_v4, &have_v6) == -1) {
215			*error_num = NO_RECOVERY;
216			return (NULL);
217		}
218
219	/* Check for literal address. */
220	if ((v4 = lwres_net_pton(AF_INET, name, &in4)) != 1)
221		v6 = lwres_net_pton(AF_INET6, name, &in6);
222
223	/*
224	 * Impossible combination?
225	 */
226	if ((af == AF_INET6 && (flags & AI_V4MAPPED) == 0 && v4 == 1) ||
227	    (af == AF_INET && v6 == 1) ||
228	    (have_v4 == 0 && v4 == 1) ||
229	    (have_v6 == 0 && v6 == 1) ||
230	    (have_v4 == 0 && af == AF_INET) ||
231	    (have_v6 == 0 && af == AF_INET6 &&
232	     (((flags & AI_V4MAPPED) != 0 && have_v4) ||
233	      (flags & AI_V4MAPPED) == 0))) {
234		*error_num = HOST_NOT_FOUND;
235		return (NULL);
236	}
237
238	/*
239	 * Literal address?
240	 */
241	if (v4 == 1 || v6 == 1) {
242		char *addr_list[2];
243		char *aliases[1];
244		char mappedname[sizeof("::ffff:123.123.123.123")];
245		union {
246			const char *const_name;
247			char *deconst_name;
248		} u;
249
250		u.const_name = name;
251		if (v4 == 1 && af == AF_INET6) {
252			strcpy(mappedname, "::ffff:");
253			lwres_net_ntop(AF_INET, (char *)&in4,
254				       mappedname + sizeof("::ffff:") - 1,
255				       sizeof(mappedname) - sizeof("::ffff:")
256				       + 1);
257			he.h_name = mappedname;
258		} else
259			he.h_name = u.deconst_name;
260		he.h_addr_list = addr_list;
261		he.h_addr_list[0] = (v4 == 1) ? (char *)&in4 : (char *)&in6;
262		he.h_addr_list[1] = NULL;
263		he.h_aliases = aliases;
264		he.h_aliases[0] = NULL;
265		he.h_length = (v4 == 1) ? INADDRSZ : IN6ADDRSZ;
266		he.h_addrtype = (v4 == 1) ? AF_INET : AF_INET6;
267		return (copyandmerge(&he, NULL, af, error_num));
268	}
269
270	n = lwres_context_create(&lwrctx, NULL, NULL, NULL, 0);
271	if (n != 0) {
272		*error_num = NO_RECOVERY;
273		goto cleanup;
274	}
275	(void) lwres_conf_parse(lwrctx, lwres_resolv_conf);
276	tmp_err = NO_RECOVERY;
277	if (have_v6 && af == AF_INET6) {
278		n = lwres_getaddrsbyname(lwrctx, name, LWRES_ADDRTYPE_V6, &by);
279		if (n == 0) {
280			he1 = hostfromname(by, AF_INET6);
281			lwres_gabnresponse_free(lwrctx, &by);
282			if (he1 == NULL) {
283				*error_num = NO_RECOVERY;
284				goto cleanup;
285			}
286		} else {
287			if (n == LWRES_R_NOTFOUND)
288				tmp_err = HOST_NOT_FOUND;
289			else {
290				*error_num = NO_RECOVERY;
291				goto cleanup;
292			}
293		}
294	}
295
296	if (have_v4 &&
297	    ((af == AF_INET) ||
298	     (af == AF_INET6 && (flags & AI_V4MAPPED) != 0 &&
299	      (he1 == NULL || (flags & AI_ALL) != 0)))) {
300		n = lwres_getaddrsbyname(lwrctx, name, LWRES_ADDRTYPE_V4, &by);
301		if (n == 0) {
302			he2 = hostfromname(by, AF_INET);
303			lwres_gabnresponse_free(lwrctx, &by);
304			if (he2 == NULL) {
305				*error_num = NO_RECOVERY;
306				goto cleanup;
307			}
308		} else if (he1 == NULL) {
309			if (n == LWRES_R_NOTFOUND)
310				*error_num = HOST_NOT_FOUND;
311			else
312				*error_num = NO_RECOVERY;
313			goto cleanup;
314		}
315	} else
316		*error_num = tmp_err;
317
318	he3 = copyandmerge(he1, he2, af, error_num);
319
320 cleanup:
321	if (he1 != NULL)
322		lwres_freehostent(he1);
323	if (he2 != NULL)
324		lwres_freehostent(he2);
325	if (lwrctx != NULL) {
326		lwres_conf_clear(lwrctx);
327		lwres_context_destroy(&lwrctx);
328	}
329	return (he3);
330}
331
332/*% performs a reverse lookup of address src which is len bytes long. af denotes the protocol family, typically #PF_INET or PF_INET6. */
333struct hostent *
334lwres_getipnodebyaddr(const void *src, size_t len, int af, int *error_num) {
335	struct hostent *he1, *he2;
336	lwres_context_t *lwrctx = NULL;
337	lwres_gnbaresponse_t *by = NULL;
338	lwres_result_t n;
339	union {
340		const void *konst;
341		struct in6_addr *in6;
342	} u;
343
344	/*
345	 * Sanity checks.
346	 */
347	if (src == NULL) {
348		*error_num = NO_RECOVERY;
349		return (NULL);
350	}
351
352	switch (af) {
353	case AF_INET:
354		if (len != (unsigned int)INADDRSZ) {
355			*error_num = NO_RECOVERY;
356			return (NULL);
357		}
358		break;
359	case AF_INET6:
360		if (len != (unsigned int)IN6ADDRSZ) {
361			*error_num = NO_RECOVERY;
362			return (NULL);
363		}
364		break;
365	default:
366		*error_num = NO_RECOVERY;
367		return (NULL);
368	}
369
370	/*
371	 * The de-"const"-ing game is done because at least one
372	 * vendor's system (RedHat 6.0) defines the IN6_IS_ADDR_*
373	 * macros in such a way that they discard the const with
374	 * internal casting, and gcc ends up complaining.  Rather
375	 * than replacing their own (possibly optimized) definitions
376	 * with our own, cleanly discarding the const is the easiest
377	 * thing to do.
378	 */
379	u.konst = src;
380
381	/*
382	 * Look up IPv4 and IPv4 mapped/compatible addresses.
383	 */
384	if ((af == AF_INET6 && IN6_IS_ADDR_V4COMPAT(u.in6)) ||
385	    (af == AF_INET6 && IN6_IS_ADDR_V4MAPPED(u.in6)) ||
386	    (af == AF_INET)) {
387		const unsigned char *cp = src;
388
389		if (af == AF_INET6)
390			cp += 12;
391		n = lwres_context_create(&lwrctx, NULL, NULL, NULL, 0);
392		if (n == LWRES_R_SUCCESS)
393			(void) lwres_conf_parse(lwrctx, lwres_resolv_conf);
394		if (n == LWRES_R_SUCCESS)
395			n = lwres_getnamebyaddr(lwrctx, LWRES_ADDRTYPE_V4,
396						INADDRSZ, cp, &by);
397		if (n != LWRES_R_SUCCESS) {
398			lwres_conf_clear(lwrctx);
399			lwres_context_destroy(&lwrctx);
400			if (n == LWRES_R_NOTFOUND)
401				*error_num = HOST_NOT_FOUND;
402			else
403				*error_num = NO_RECOVERY;
404			return (NULL);
405		}
406		he1 = hostfromaddr(by, AF_INET, cp);
407		lwres_gnbaresponse_free(lwrctx, &by);
408		lwres_conf_clear(lwrctx);
409		lwres_context_destroy(&lwrctx);
410		if (af != AF_INET6)
411			return (he1);
412
413		/*
414		 * Convert from AF_INET to AF_INET6.
415		 */
416		he2 = copyandmerge(he1, NULL, af, error_num);
417		lwres_freehostent(he1);
418		if (he2 == NULL)
419			return (NULL);
420		/*
421		 * Restore original address.
422		 */
423		memcpy(he2->h_addr, src, len);
424		return (he2);
425	}
426
427	/*
428	 * Lookup IPv6 address.
429	 */
430	if (memcmp(src, &in6addr_any, IN6ADDRSZ) == 0) {
431		*error_num = HOST_NOT_FOUND;
432		return (NULL);
433	}
434
435	n = lwres_context_create(&lwrctx, NULL, NULL, NULL, 0);
436	if (n == LWRES_R_SUCCESS)
437		(void) lwres_conf_parse(lwrctx, lwres_resolv_conf);
438	if (n == LWRES_R_SUCCESS)
439		n = lwres_getnamebyaddr(lwrctx, LWRES_ADDRTYPE_V6, IN6ADDRSZ,
440					src, &by);
441	if (n != 0) {
442		lwres_conf_clear(lwrctx);
443		lwres_context_destroy(&lwrctx);
444
445		if (n == LWRES_R_NOTFOUND)
446		       *error_num = HOST_NOT_FOUND;
447		else
448		       *error_num = NO_RECOVERY;
449
450		return (NULL);
451	}
452
453	he1 = hostfromaddr(by, AF_INET6, src);
454	lwres_gnbaresponse_free(lwrctx, &by);
455	if (he1 == NULL)
456		*error_num = NO_RECOVERY;
457	lwres_conf_clear(lwrctx);
458	lwres_context_destroy(&lwrctx);
459	return (he1);
460}
461
462/*% releases all the memory associated with the struct hostent pointer */
463void
464lwres_freehostent(struct hostent *he) {
465	char **cpp;
466	int names = 1;
467	int addresses = 1;
468
469	free(he->h_name);
470
471	cpp = he->h_addr_list;
472	while (*cpp != NULL) {
473		free(*cpp);
474		*cpp = NULL;
475		cpp++;
476		addresses++;
477	}
478
479	cpp = he->h_aliases;
480	while (*cpp != NULL) {
481		free(*cpp);
482		cpp++;
483		names++;
484	}
485
486	free(he->h_aliases);
487	free(he->h_addr_list);
488	free(he);
489}
490
491/*
492 * Private
493 */
494
495/*
496 * Scan the interface table and set have_v4 and have_v6 depending
497 * upon whether there are IPv4 and IPv6 interface addresses.
498 *
499 * Returns:
500 *	0 on success
501 *	-1 on failure.
502 */
503
504#if defined(SIOCGLIFCONF) && defined(SIOCGLIFADDR) && \
505    !defined(IRIX_EMUL_IOCTL_SIOCGIFCONF)
506
507#ifdef __hpux
508#define lifc_len iflc_len
509#define lifc_buf iflc_buf
510#define lifc_req iflc_req
511#define LIFCONF if_laddrconf
512#else
513#define ISC_HAVE_LIFC_FAMILY 1
514#define ISC_HAVE_LIFC_FLAGS 1
515#define LIFCONF lifconf
516#endif
517
518#ifdef __hpux
519#define lifr_addr iflr_addr
520#define lifr_name iflr_name
521#define lifr_dstaddr iflr_dstaddr
522#define lifr_flags iflr_flags
523#define ss_family sa_family
524#define LIFREQ if_laddrreq
525#else
526#define LIFREQ lifreq
527#endif
528
529static int
530scan_interfaces6(int *have_v4, int *have_v6) {
531	struct LIFCONF lifc;
532	struct LIFREQ lifreq;
533	struct in_addr in4;
534	struct in6_addr in6;
535	char *buf = NULL, *cp, *cplim;
536	static unsigned int bufsiz = 4095;
537	int s, cpsize, n;
538
539	/*
540	 * Set to zero.  Used as loop terminators below.
541	 */
542	*have_v4 = *have_v6 = 0;
543
544	/*
545	 * Get interface list from system.
546	 */
547	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) == -1)
548		goto err_ret;
549
550	/*
551	 * Grow buffer until large enough to contain all interface
552	 * descriptions.
553	 */
554	for (;;) {
555		buf = malloc(bufsiz);
556		if (buf == NULL)
557			goto err_ret;
558#ifdef ISC_HAVE_LIFC_FAMILY
559		lifc.lifc_family = AF_UNSPEC;	/* request all families */
560#endif
561#ifdef ISC_HAVE_LIFC_FLAGS
562		lifc.lifc_flags = 0;
563#endif
564		lifc.lifc_len = bufsiz;
565		lifc.lifc_buf = buf;
566		if ((n = ioctl(s, SIOCGLIFCONF, (char *)&lifc)) != -1) {
567			/*
568			 * Some OS's just return what will fit rather
569			 * than set EINVAL if the buffer is too small
570			 * to fit all the interfaces in.  If
571			 * lifc.lifc_len is too near to the end of the
572			 * buffer we will grow it just in case and
573			 * retry.
574			 */
575			if (lifc.lifc_len + 2 * sizeof(lifreq) < bufsiz)
576				break;
577		}
578		if ((n == -1) && errno != EINVAL)
579			goto err_ret;
580
581		if (bufsiz > 1000000)
582			goto err_ret;
583
584		free(buf);
585		bufsiz += 4096;
586	}
587
588	/*
589	 * Parse system's interface list.
590	 */
591	cplim = buf + lifc.lifc_len;    /* skip over if's with big ifr_addr's */
592	for (cp = buf;
593	     (*have_v4 == 0 || *have_v6 == 0) && cp < cplim;
594	     cp += cpsize) {
595		memcpy(&lifreq, cp, sizeof(lifreq));
596#ifdef LWRES_PLATFORM_HAVESALEN
597#ifdef FIX_ZERO_SA_LEN
598		if (lifreq.lifr_addr.sa_len == 0)
599			lifreq.lifr_addr.sa_len = 16;
600#endif
601#ifdef HAVE_MINIMUM_IFREQ
602		cpsize = sizeof(lifreq);
603		if (lifreq.lifr_addr.sa_len > sizeof(struct sockaddr))
604			cpsize += (int)lifreq.lifr_addr.sa_len -
605				(int)(sizeof(struct sockaddr));
606#else
607		cpsize = sizeof(lifreq.lifr_name) + lifreq.lifr_addr.sa_len;
608#endif /* HAVE_MINIMUM_IFREQ */
609#elif defined SIOCGIFCONF_ADDR
610		cpsize = sizeof(lifreq);
611#else
612		cpsize = sizeof(lifreq.lifr_name);
613		/* XXX maybe this should be a hard error? */
614		if (ioctl(s, SIOCGLIFADDR, (char *)&lifreq) < 0)
615			continue;
616#endif
617		switch (lifreq.lifr_addr.ss_family) {
618		case AF_INET:
619			if (*have_v4 == 0) {
620				memcpy(&in4,
621				       &((struct sockaddr_in *)
622				       &lifreq.lifr_addr)->sin_addr,
623				       sizeof(in4));
624				if (in4.s_addr == INADDR_ANY)
625					break;
626				n = ioctl(s, SIOCGLIFFLAGS, (char *)&lifreq);
627				if (n < 0)
628					break;
629				if ((lifreq.lifr_flags & IFF_UP) == 0)
630					break;
631				*have_v4 = 1;
632			}
633			break;
634		case AF_INET6:
635			if (*have_v6 == 0) {
636				memcpy(&in6,
637				       &((struct sockaddr_in6 *)
638				       &lifreq.lifr_addr)->sin6_addr,
639				       sizeof(in6));
640				if (memcmp(&in6, &in6addr_any,
641					   sizeof(in6)) == 0)
642					break;
643				n = ioctl(s, SIOCGLIFFLAGS, (char *)&lifreq);
644				if (n < 0)
645					break;
646				if ((lifreq.lifr_flags & IFF_UP) == 0)
647					break;
648				*have_v6 = 1;
649			}
650			break;
651		}
652	}
653	if (buf != NULL)
654		free(buf);
655	close(s);
656	return (0);
657 err_ret:
658	if (buf != NULL)
659		free(buf);
660	if (s != -1)
661		close(s);
662	return (-1);
663}
664#endif
665
666static int
667scan_interfaces(int *have_v4, int *have_v6) {
668#if !defined(SIOCGIFCONF) || !defined(SIOCGIFADDR)
669	*have_v4 = *have_v6 = 1;
670	return (0);
671#else
672	struct ifconf ifc;
673	union {
674		char _pad[256];		/* leave space for IPv6 addresses */
675		struct ifreq ifreq;
676	} u;
677	struct in_addr in4;
678	struct in6_addr in6;
679	char *buf = NULL, *cp, *cplim;
680	static unsigned int bufsiz = 4095;
681	int s, n;
682	size_t cpsize;
683
684#ifdef WIN32
685	InitSockets();
686#endif
687#if defined(SIOCGLIFCONF) && defined(SIOCGLIFADDR) && \
688    !defined(IRIX_EMUL_IOCTL_SIOCGIFCONF)
689	/*
690	 * Try to scan the interfaces using IPv6 ioctls().
691	 */
692	if (!scan_interfaces6(have_v4, have_v6)) {
693#ifdef WIN32
694		DestroySockets();
695#endif
696		return (0);
697	}
698#endif
699
700	/*
701	 * Set to zero.  Used as loop terminators below.
702	 */
703	*have_v4 = *have_v6 = 0;
704
705	/*
706	 * Get interface list from system.
707	 */
708	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
709		goto err_ret;
710
711	/*
712	 * Grow buffer until large enough to contain all interface
713	 * descriptions.
714	 */
715	for (;;) {
716		buf = malloc(bufsiz);
717		if (buf == NULL)
718			goto err_ret;
719		ifc.ifc_len = bufsiz;
720		ifc.ifc_buf = buf;
721#ifdef IRIX_EMUL_IOCTL_SIOCGIFCONF
722		/*
723		 * This is a fix for IRIX OS in which the call to ioctl with
724		 * the flag SIOCGIFCONF may not return an entry for all the
725		 * interfaces like most flavors of Unix.
726		 */
727		if (emul_ioctl(&ifc) >= 0)
728			break;
729#else
730		if ((n = ioctl(s, SIOCGIFCONF, (char *)&ifc)) != -1) {
731			/*
732			 * Some OS's just return what will fit rather
733			 * than set EINVAL if the buffer is too small
734			 * to fit all the interfaces in.  If
735			 * ifc.ifc_len is too near to the end of the
736			 * buffer we will grow it just in case and
737			 * retry.
738			 */
739			if (ifc.ifc_len + 2 * sizeof(u.ifreq) < bufsiz)
740				break;
741		}
742#endif
743		if ((n == -1) && errno != EINVAL)
744			goto err_ret;
745
746		if (bufsiz > 1000000)
747			goto err_ret;
748
749		free(buf);
750		bufsiz += 4096;
751	}
752
753	/*
754	 * Parse system's interface list.
755	 */
756	cplim = buf + ifc.ifc_len;    /* skip over if's with big ifr_addr's */
757	for (cp = buf;
758	     (*have_v4 == 0 || *have_v6 == 0) && cp < cplim;
759	     cp += cpsize) {
760		memcpy(&u.ifreq, cp, sizeof(u.ifreq));
761#ifdef LWRES_PLATFORM_HAVESALEN
762#ifdef FIX_ZERO_SA_LEN
763		if (u.ifreq.ifr_addr.sa_len == 0)
764			u.ifreq.ifr_addr.sa_len = 16;
765#endif
766#ifdef HAVE_MINIMUM_IFREQ
767		cpsize = sizeof(u.ifreq);
768		if (u.ifreq.ifr_addr.sa_len > sizeof(struct sockaddr))
769			cpsize += (int)u.ifreq.ifr_addr.sa_len -
770				(int)(sizeof(struct sockaddr));
771#else
772		cpsize = sizeof(u.ifreq.ifr_name) + u.ifreq.ifr_addr.sa_len;
773#endif /* HAVE_MINIMUM_IFREQ */
774		if (cpsize > sizeof(u.ifreq) && cpsize <= sizeof(u))
775			memcpy(&u.ifreq, cp, cpsize);
776#elif defined SIOCGIFCONF_ADDR
777		cpsize = sizeof(u.ifreq);
778#else
779		cpsize = sizeof(u.ifreq.ifr_name);
780		/* XXX maybe this should be a hard error? */
781		if (ioctl(s, SIOCGIFADDR, (char *)&u.ifreq) < 0)
782			continue;
783#endif
784		switch (u.ifreq.ifr_addr.sa_family) {
785		case AF_INET:
786			if (*have_v4 == 0) {
787				memcpy(&in4,
788				       &((struct sockaddr_in *)
789				       &u.ifreq.ifr_addr)->sin_addr,
790				       sizeof(in4));
791				if (in4.s_addr == INADDR_ANY)
792					break;
793				n = ioctl(s, SIOCGIFFLAGS, (char *)&u.ifreq);
794				if (n < 0)
795					break;
796				if ((u.ifreq.ifr_flags & IFF_UP) == 0)
797					break;
798				*have_v4 = 1;
799			}
800			break;
801		case AF_INET6:
802			if (*have_v6 == 0) {
803				memcpy(&in6,
804				       &((struct sockaddr_in6 *)
805				       &u.ifreq.ifr_addr)->sin6_addr,
806				       sizeof(in6));
807				if (memcmp(&in6, &in6addr_any,
808					   sizeof(in6)) == 0)
809					break;
810				n = ioctl(s, SIOCGIFFLAGS, (char *)&u.ifreq);
811				if (n < 0)
812					break;
813				if ((u.ifreq.ifr_flags & IFF_UP) == 0)
814					break;
815				*have_v6 = 1;
816			}
817			break;
818		}
819	}
820	if (buf != NULL)
821		free(buf);
822#ifdef WIN32
823	DestroySockets();
824#endif
825	close(s);
826	return (0);
827
828 err_ret:
829	if (buf != NULL)
830		free(buf);
831	if (s != -1)
832		close(s);
833#ifdef WIN32
834	DestroySockets();
835#endif
836	return (-1);
837#endif
838}
839
840static struct hostent *
841copyandmerge(struct hostent *he1, struct hostent *he2, int af, int *error_num)
842{
843	struct hostent *he = NULL;
844	int addresses = 1;	/* NULL terminator */
845	int names = 1;		/* NULL terminator */
846	int len = 0;
847	char **cpp, **npp;
848
849	/*
850	 * Work out array sizes.
851	 */
852	if (he1 != NULL) {
853		cpp = he1->h_addr_list;
854		while (*cpp != NULL) {
855			addresses++;
856			cpp++;
857		}
858		cpp = he1->h_aliases;
859		while (*cpp != NULL) {
860			names++;
861			cpp++;
862		}
863	}
864
865	if (he2 != NULL) {
866		cpp = he2->h_addr_list;
867		while (*cpp != NULL) {
868			addresses++;
869			cpp++;
870		}
871		if (he1 == NULL) {
872			cpp = he2->h_aliases;
873			while (*cpp != NULL) {
874				names++;
875				cpp++;
876			}
877		}
878	}
879
880	if (addresses == 1) {
881		*error_num = NO_ADDRESS;
882		return (NULL);
883	}
884
885	he = malloc(sizeof(*he));
886	if (he == NULL)
887		goto no_recovery;
888
889	he->h_addr_list = malloc(sizeof(char *) * (addresses));
890	if (he->h_addr_list == NULL)
891		goto cleanup0;
892	memset(he->h_addr_list, 0, sizeof(char *) * (addresses));
893
894	/*
895	 * Copy addresses.
896	 */
897	npp = he->h_addr_list;
898	if (he1 != NULL) {
899		cpp = he1->h_addr_list;
900		while (*cpp != NULL) {
901			*npp = malloc((af == AF_INET) ? INADDRSZ : IN6ADDRSZ);
902			if (*npp == NULL)
903				goto cleanup1;
904			/*
905			 * Convert to mapped if required.
906			 */
907			if (af == AF_INET6 && he1->h_addrtype == AF_INET) {
908				memcpy(*npp, in6addr_mapped,
909				       sizeof(in6addr_mapped));
910				memcpy(*npp + sizeof(in6addr_mapped), *cpp,
911				       INADDRSZ);
912			} else {
913				memcpy(*npp, *cpp,
914				       (af == AF_INET) ? INADDRSZ : IN6ADDRSZ);
915			}
916			cpp++;
917			npp++;
918		}
919	}
920
921	if (he2 != NULL) {
922		cpp = he2->h_addr_list;
923		while (*cpp != NULL) {
924			*npp = malloc((af == AF_INET) ? INADDRSZ : IN6ADDRSZ);
925			if (*npp == NULL)
926				goto cleanup1;
927			/*
928			 * Convert to mapped if required.
929			 */
930			if (af == AF_INET6 && he2->h_addrtype == AF_INET) {
931				memcpy(*npp, in6addr_mapped,
932				       sizeof(in6addr_mapped));
933				memcpy(*npp + sizeof(in6addr_mapped), *cpp,
934				       INADDRSZ);
935			} else {
936				memcpy(*npp, *cpp,
937				       (af == AF_INET) ? INADDRSZ : IN6ADDRSZ);
938			}
939			cpp++;
940			npp++;
941		}
942	}
943
944	he->h_aliases = malloc(sizeof(char *) * (names));
945	if (he->h_aliases == NULL)
946		goto cleanup1;
947	memset(he->h_aliases, 0, sizeof(char *) * (names));
948
949	/*
950	 * Copy aliases.
951	 */
952	npp = he->h_aliases;
953	cpp = (he1 != NULL) ? he1->h_aliases : he2->h_aliases;
954	while (*cpp != NULL) {
955		len = strlen (*cpp) + 1;
956		*npp = malloc(len);
957		if (*npp == NULL)
958			goto cleanup2;
959		strcpy(*npp, *cpp);
960		npp++;
961		cpp++;
962	}
963
964	/*
965	 * Copy hostname.
966	 */
967	he->h_name = malloc(strlen((he1 != NULL) ?
968			    he1->h_name : he2->h_name) + 1);
969	if (he->h_name == NULL)
970		goto cleanup2;
971	strcpy(he->h_name, (he1 != NULL) ? he1->h_name : he2->h_name);
972
973	/*
974	 * Set address type and length.
975	 */
976	he->h_addrtype = af;
977	he->h_length = (af == AF_INET) ? INADDRSZ : IN6ADDRSZ;
978	return (he);
979
980 cleanup2:
981	cpp = he->h_aliases;
982	while (*cpp != NULL) {
983		free(*cpp);
984		cpp++;
985	}
986	free(he->h_aliases);
987
988 cleanup1:
989	cpp = he->h_addr_list;
990	while (*cpp != NULL) {
991		free(*cpp);
992		*cpp = NULL;
993		cpp++;
994	}
995	free(he->h_addr_list);
996
997 cleanup0:
998	free(he);
999
1000 no_recovery:
1001	*error_num = NO_RECOVERY;
1002	return (NULL);
1003}
1004
1005static struct hostent *
1006hostfromaddr(lwres_gnbaresponse_t *addr, int af, const void *src) {
1007	struct hostent *he;
1008	int i;
1009
1010	he = malloc(sizeof(*he));
1011	if (he == NULL)
1012		goto cleanup;
1013	memset(he, 0, sizeof(*he));
1014
1015	/*
1016	 * Set family and length.
1017	 */
1018	he->h_addrtype = af;
1019	switch (af) {
1020	case AF_INET:
1021		he->h_length = INADDRSZ;
1022		break;
1023	case AF_INET6:
1024		he->h_length = IN6ADDRSZ;
1025		break;
1026	default:
1027		INSIST(0);
1028	}
1029
1030	/*
1031	 * Copy name.
1032	 */
1033	he->h_name = strdup(addr->realname);
1034	if (he->h_name == NULL)
1035		goto cleanup;
1036
1037	/*
1038	 * Copy aliases.
1039	 */
1040	he->h_aliases = malloc(sizeof(char *) * (addr->naliases + 1));
1041	if (he->h_aliases == NULL)
1042		goto cleanup;
1043	for (i = 0; i < addr->naliases; i++) {
1044		he->h_aliases[i] = strdup(addr->aliases[i]);
1045		if (he->h_aliases[i] == NULL)
1046			goto cleanup;
1047	}
1048	he->h_aliases[i] = NULL;
1049
1050	/*
1051	 * Copy address.
1052	 */
1053	he->h_addr_list = malloc(sizeof(char *) * 2);
1054	if (he->h_addr_list == NULL)
1055		goto cleanup;
1056	he->h_addr_list[0] = malloc(he->h_length);
1057	if (he->h_addr_list[0] == NULL)
1058		goto cleanup;
1059	memcpy(he->h_addr_list[0], src, he->h_length);
1060	he->h_addr_list[1] = NULL;
1061	return (he);
1062
1063 cleanup:
1064	if (he != NULL && he->h_addr_list != NULL) {
1065		for (i = 0; he->h_addr_list[i] != NULL; i++)
1066			free(he->h_addr_list[i]);
1067		free(he->h_addr_list);
1068	}
1069	if (he != NULL && he->h_aliases != NULL) {
1070		for (i = 0; he->h_aliases[i] != NULL; i++)
1071			free(he->h_aliases[i]);
1072		free(he->h_aliases);
1073	}
1074	if (he != NULL && he->h_name != NULL)
1075		free(he->h_name);
1076	if (he != NULL)
1077		free(he);
1078	return (NULL);
1079}
1080
1081static struct hostent *
1082hostfromname(lwres_gabnresponse_t *name, int af) {
1083	struct hostent *he;
1084	int i;
1085	lwres_addr_t *addr;
1086
1087	he = malloc(sizeof(*he));
1088	if (he == NULL)
1089		goto cleanup;
1090	memset(he, 0, sizeof(*he));
1091
1092	/*
1093	 * Set family and length.
1094	 */
1095	he->h_addrtype = af;
1096	switch (af) {
1097	case AF_INET:
1098		he->h_length = INADDRSZ;
1099		break;
1100	case AF_INET6:
1101		he->h_length = IN6ADDRSZ;
1102		break;
1103	default:
1104		INSIST(0);
1105	}
1106
1107	/*
1108	 * Copy name.
1109	 */
1110	he->h_name = strdup(name->realname);
1111	if (he->h_name == NULL)
1112		goto cleanup;
1113
1114	/*
1115	 * Copy aliases.
1116	 */
1117	he->h_aliases = malloc(sizeof(char *) * (name->naliases + 1));
1118	for (i = 0; i < name->naliases; i++) {
1119		he->h_aliases[i] = strdup(name->aliases[i]);
1120		if (he->h_aliases[i] == NULL)
1121			goto cleanup;
1122	}
1123	he->h_aliases[i] = NULL;
1124
1125	/*
1126	 * Copy addresses.
1127	 */
1128	he->h_addr_list = malloc(sizeof(char *) * (name->naddrs + 1));
1129	addr = LWRES_LIST_HEAD(name->addrs);
1130	i = 0;
1131	while (addr != NULL) {
1132		he->h_addr_list[i] = malloc(he->h_length);
1133		if (he->h_addr_list[i] == NULL)
1134			goto cleanup;
1135		memcpy(he->h_addr_list[i], addr->address, he->h_length);
1136		addr = LWRES_LIST_NEXT(addr, link);
1137		i++;
1138	}
1139	he->h_addr_list[i] = NULL;
1140	return (he);
1141
1142 cleanup:
1143	if (he != NULL && he->h_addr_list != NULL) {
1144		for (i = 0; he->h_addr_list[i] != NULL; i++)
1145			free(he->h_addr_list[i]);
1146		free(he->h_addr_list);
1147	}
1148	if (he != NULL && he->h_aliases != NULL) {
1149		for (i = 0; he->h_aliases[i] != NULL; i++)
1150			free(he->h_aliases[i]);
1151		free(he->h_aliases);
1152	}
1153	if (he != NULL && he->h_name != NULL)
1154		free(he->h_name);
1155	if (he != NULL)
1156		free(he);
1157	return (NULL);
1158}
1159