1/*
2 * Copyright (C) 2004, 2005, 2007  Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 2001, 2002  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: getaddresses.c,v 1.22 2007/06/19 23:47:16 tbox Exp $ */
19
20/*! \file */
21
22#include <config.h>
23#include <string.h>
24
25#include <isc/net.h>
26#include <isc/netaddr.h>
27#include <isc/netdb.h>
28#include <isc/netscope.h>
29#include <isc/result.h>
30#include <isc/sockaddr.h>
31#include <isc/util.h>
32
33#include <bind9/getaddresses.h>
34
35#ifdef HAVE_ADDRINFO
36#ifdef HAVE_GETADDRINFO
37#ifdef HAVE_GAISTRERROR
38#define USE_GETADDRINFO
39#endif
40#endif
41#endif
42
43#ifndef USE_GETADDRINFO
44#ifndef ISC_PLATFORM_NONSTDHERRNO
45extern int h_errno;
46#endif
47#endif
48
49isc_result_t
50bind9_getaddresses(const char *hostname, in_port_t port,
51		   isc_sockaddr_t *addrs, int addrsize, int *addrcount)
52{
53	struct in_addr in4;
54	struct in6_addr in6;
55	isc_boolean_t have_ipv4, have_ipv6;
56	int i;
57
58#ifdef USE_GETADDRINFO
59	struct addrinfo *ai = NULL, *tmpai, hints;
60	int result;
61#else
62	struct hostent *he;
63#endif
64
65	REQUIRE(hostname != NULL);
66	REQUIRE(addrs != NULL);
67	REQUIRE(addrcount != NULL);
68	REQUIRE(addrsize > 0);
69
70	have_ipv4 = ISC_TF((isc_net_probeipv4() == ISC_R_SUCCESS));
71	have_ipv6 = ISC_TF((isc_net_probeipv6() == ISC_R_SUCCESS));
72
73	/*
74	 * Try IPv4, then IPv6.  In order to handle the extended format
75	 * for IPv6 scoped addresses (address%scope_ID), we'll use a local
76	 * working buffer of 128 bytes.  The length is an ad-hoc value, but
77	 * should be enough for this purpose; the buffer can contain a string
78	 * of at least 80 bytes for scope_ID in addition to any IPv6 numeric
79	 * addresses (up to 46 bytes), the delimiter character and the
80	 * terminating NULL character.
81	 */
82	if (inet_pton(AF_INET, hostname, &in4) == 1) {
83		if (have_ipv4)
84			isc_sockaddr_fromin(&addrs[0], &in4, port);
85		else
86			isc_sockaddr_v6fromin(&addrs[0], &in4, port);
87		*addrcount = 1;
88		return (ISC_R_SUCCESS);
89	} else if (strlen(hostname) <= 127U) {
90		char tmpbuf[128], *d;
91		isc_uint32_t zone = 0;
92
93		strcpy(tmpbuf, hostname);
94		d = strchr(tmpbuf, '%');
95		if (d != NULL)
96			*d = '\0';
97
98		if (inet_pton(AF_INET6, tmpbuf, &in6) == 1) {
99			isc_netaddr_t na;
100
101			if (!have_ipv6)
102				return (ISC_R_FAMILYNOSUPPORT);
103
104			if (d != NULL) {
105#ifdef ISC_PLATFORM_HAVESCOPEID
106				isc_result_t result;
107
108				result = isc_netscope_pton(AF_INET6, d + 1,
109							   &in6, &zone);
110
111				if (result != ISC_R_SUCCESS)
112					return (result);
113#else
114				/*
115				 * The extended format is specified while the
116				 * system does not provide the ability to use
117				 * it.  Throw an explicit error instead of
118				 * ignoring the specified value.
119				 */
120				return (ISC_R_BADADDRESSFORM);
121#endif
122			}
123
124			isc_netaddr_fromin6(&na, &in6);
125			isc_netaddr_setzone(&na, zone);
126			isc_sockaddr_fromnetaddr(&addrs[0],
127						 (const isc_netaddr_t *)&na,
128						 port);
129
130			*addrcount = 1;
131			return (ISC_R_SUCCESS);
132
133		}
134	}
135#ifdef USE_GETADDRINFO
136	memset(&hints, 0, sizeof(hints));
137	if (!have_ipv6)
138		hints.ai_family = PF_INET;
139	else if (!have_ipv4)
140		hints.ai_family = PF_INET6;
141	else {
142		hints.ai_family = PF_UNSPEC;
143#ifdef AI_ADDRCONFIG
144		hints.ai_flags = AI_ADDRCONFIG;
145#endif
146	}
147	hints.ai_socktype = SOCK_STREAM;
148#ifdef AI_ADDRCONFIG
149 again:
150#endif
151	result = getaddrinfo(hostname, NULL, &hints, &ai);
152	switch (result) {
153	case 0:
154		break;
155	case EAI_NONAME:
156#if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
157	case EAI_NODATA:
158#endif
159		return (ISC_R_NOTFOUND);
160#ifdef AI_ADDRCONFIG
161	case EAI_BADFLAGS:
162		if ((hints.ai_flags & AI_ADDRCONFIG) != 0) {
163			hints.ai_flags &= ~AI_ADDRCONFIG;
164			goto again;
165		}
166#endif
167	default:
168		return (ISC_R_FAILURE);
169	}
170	for (tmpai = ai, i = 0;
171	     tmpai != NULL && i < addrsize;
172	     tmpai = tmpai->ai_next)
173	{
174		if (tmpai->ai_family != AF_INET &&
175		    tmpai->ai_family != AF_INET6)
176			continue;
177		if (tmpai->ai_family == AF_INET) {
178			struct sockaddr_in *sin;
179			sin = (struct sockaddr_in *)tmpai->ai_addr;
180			isc_sockaddr_fromin(&addrs[i], &sin->sin_addr, port);
181		} else {
182			struct sockaddr_in6 *sin6;
183			sin6 = (struct sockaddr_in6 *)tmpai->ai_addr;
184			isc_sockaddr_fromin6(&addrs[i], &sin6->sin6_addr,
185					     port);
186		}
187		i++;
188
189	}
190	freeaddrinfo(ai);
191	*addrcount = i;
192#else
193	he = gethostbyname(hostname);
194	if (he == NULL) {
195		switch (h_errno) {
196		case HOST_NOT_FOUND:
197#ifdef NO_DATA
198		case NO_DATA:
199#endif
200#if defined(NO_ADDRESS) && (!defined(NO_DATA) || (NO_DATA != NO_ADDRESS))
201		case NO_ADDRESS:
202#endif
203			return (ISC_R_NOTFOUND);
204		default:
205			return (ISC_R_FAILURE);
206		}
207	}
208	if (he->h_addrtype != AF_INET && he->h_addrtype != AF_INET6)
209		return (ISC_R_NOTFOUND);
210	for (i = 0; i < addrsize; i++) {
211		if (he->h_addrtype == AF_INET) {
212			struct in_addr *inp;
213			inp = (struct in_addr *)(he->h_addr_list[i]);
214			if (inp == NULL)
215				break;
216			isc_sockaddr_fromin(&addrs[i], inp, port);
217		} else {
218			struct in6_addr *in6p;
219			in6p = (struct in6_addr *)(he->h_addr_list[i]);
220			if (in6p == NULL)
221				break;
222			isc_sockaddr_fromin6(&addrs[i], in6p, port);
223		}
224	}
225	*addrcount = i;
226#endif
227	if (*addrcount == 0)
228		return (ISC_R_NOTFOUND);
229	else
230		return (ISC_R_SUCCESS);
231}
232