1135446Strhodes/*
2193149Sdougb * Copyright (C) 2004, 2005, 2007  Internet Systems Consortium, Inc. ("ISC")
3135446Strhodes * Copyright (C) 2001, 2002  Internet Software Consortium.
4135446Strhodes *
5193149Sdougb * Permission to use, copy, modify, and/or distribute this software for any
6135446Strhodes * purpose with or without fee is hereby granted, provided that the above
7135446Strhodes * copyright notice and this permission notice appear in all copies.
8135446Strhodes *
9135446Strhodes * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10135446Strhodes * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11135446Strhodes * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12135446Strhodes * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13135446Strhodes * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14135446Strhodes * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15135446Strhodes * PERFORMANCE OF THIS SOFTWARE.
16135446Strhodes */
17135446Strhodes
18234010Sdougb/* $Id: getaddresses.c,v 1.22 2007/06/19 23:47:16 tbox Exp $ */
19135446Strhodes
20170222Sdougb/*! \file */
21170222Sdougb
22135446Strhodes#include <config.h>
23135446Strhodes#include <string.h>
24135446Strhodes
25135446Strhodes#include <isc/net.h>
26135446Strhodes#include <isc/netaddr.h>
27135446Strhodes#include <isc/netdb.h>
28135446Strhodes#include <isc/netscope.h>
29135446Strhodes#include <isc/result.h>
30135446Strhodes#include <isc/sockaddr.h>
31135446Strhodes#include <isc/util.h>
32135446Strhodes
33135446Strhodes#include <bind9/getaddresses.h>
34135446Strhodes
35135446Strhodes#ifdef HAVE_ADDRINFO
36135446Strhodes#ifdef HAVE_GETADDRINFO
37135446Strhodes#ifdef HAVE_GAISTRERROR
38135446Strhodes#define USE_GETADDRINFO
39135446Strhodes#endif
40135446Strhodes#endif
41135446Strhodes#endif
42135446Strhodes
43135446Strhodes#ifndef USE_GETADDRINFO
44135446Strhodes#ifndef ISC_PLATFORM_NONSTDHERRNO
45135446Strhodesextern int h_errno;
46135446Strhodes#endif
47135446Strhodes#endif
48135446Strhodes
49135446Strhodesisc_result_t
50135446Strhodesbind9_getaddresses(const char *hostname, in_port_t port,
51135446Strhodes		   isc_sockaddr_t *addrs, int addrsize, int *addrcount)
52135446Strhodes{
53135446Strhodes	struct in_addr in4;
54135446Strhodes	struct in6_addr in6;
55135446Strhodes	isc_boolean_t have_ipv4, have_ipv6;
56135446Strhodes	int i;
57135446Strhodes
58135446Strhodes#ifdef USE_GETADDRINFO
59135446Strhodes	struct addrinfo *ai = NULL, *tmpai, hints;
60135446Strhodes	int result;
61135446Strhodes#else
62135446Strhodes	struct hostent *he;
63135446Strhodes#endif
64135446Strhodes
65135446Strhodes	REQUIRE(hostname != NULL);
66135446Strhodes	REQUIRE(addrs != NULL);
67135446Strhodes	REQUIRE(addrcount != NULL);
68135446Strhodes	REQUIRE(addrsize > 0);
69135446Strhodes
70153816Sdougb	have_ipv4 = ISC_TF((isc_net_probeipv4() == ISC_R_SUCCESS));
71153816Sdougb	have_ipv6 = ISC_TF((isc_net_probeipv6() == ISC_R_SUCCESS));
72135446Strhodes
73135446Strhodes	/*
74135446Strhodes	 * Try IPv4, then IPv6.  In order to handle the extended format
75135446Strhodes	 * for IPv6 scoped addresses (address%scope_ID), we'll use a local
76135446Strhodes	 * working buffer of 128 bytes.  The length is an ad-hoc value, but
77135446Strhodes	 * should be enough for this purpose; the buffer can contain a string
78135446Strhodes	 * of at least 80 bytes for scope_ID in addition to any IPv6 numeric
79135446Strhodes	 * addresses (up to 46 bytes), the delimiter character and the
80135446Strhodes	 * terminating NULL character.
81135446Strhodes	 */
82135446Strhodes	if (inet_pton(AF_INET, hostname, &in4) == 1) {
83135446Strhodes		if (have_ipv4)
84135446Strhodes			isc_sockaddr_fromin(&addrs[0], &in4, port);
85135446Strhodes		else
86135446Strhodes			isc_sockaddr_v6fromin(&addrs[0], &in4, port);
87135446Strhodes		*addrcount = 1;
88135446Strhodes		return (ISC_R_SUCCESS);
89143731Sdougb	} else if (strlen(hostname) <= 127U) {
90135446Strhodes		char tmpbuf[128], *d;
91135446Strhodes		isc_uint32_t zone = 0;
92135446Strhodes
93135446Strhodes		strcpy(tmpbuf, hostname);
94135446Strhodes		d = strchr(tmpbuf, '%');
95135446Strhodes		if (d != NULL)
96135446Strhodes			*d = '\0';
97135446Strhodes
98135446Strhodes		if (inet_pton(AF_INET6, tmpbuf, &in6) == 1) {
99135446Strhodes			isc_netaddr_t na;
100135446Strhodes
101135446Strhodes			if (!have_ipv6)
102135446Strhodes				return (ISC_R_FAMILYNOSUPPORT);
103135446Strhodes
104135446Strhodes			if (d != NULL) {
105135446Strhodes#ifdef ISC_PLATFORM_HAVESCOPEID
106135446Strhodes				isc_result_t result;
107135446Strhodes
108135446Strhodes				result = isc_netscope_pton(AF_INET6, d + 1,
109135446Strhodes							   &in6, &zone);
110135446Strhodes
111135446Strhodes				if (result != ISC_R_SUCCESS)
112135446Strhodes					return (result);
113135446Strhodes#else
114135446Strhodes				/*
115135446Strhodes				 * The extended format is specified while the
116135446Strhodes				 * system does not provide the ability to use
117135446Strhodes				 * it.  Throw an explicit error instead of
118135446Strhodes				 * ignoring the specified value.
119135446Strhodes				 */
120135446Strhodes				return (ISC_R_BADADDRESSFORM);
121135446Strhodes#endif
122135446Strhodes			}
123135446Strhodes
124135446Strhodes			isc_netaddr_fromin6(&na, &in6);
125135446Strhodes			isc_netaddr_setzone(&na, zone);
126135446Strhodes			isc_sockaddr_fromnetaddr(&addrs[0],
127135446Strhodes						 (const isc_netaddr_t *)&na,
128135446Strhodes						 port);
129135446Strhodes
130135446Strhodes			*addrcount = 1;
131135446Strhodes			return (ISC_R_SUCCESS);
132135446Strhodes
133135446Strhodes		}
134135446Strhodes	}
135135446Strhodes#ifdef USE_GETADDRINFO
136135446Strhodes	memset(&hints, 0, sizeof(hints));
137135446Strhodes	if (!have_ipv6)
138135446Strhodes		hints.ai_family = PF_INET;
139135446Strhodes	else if (!have_ipv4)
140135446Strhodes		hints.ai_family = PF_INET6;
141135446Strhodes	else {
142135446Strhodes		hints.ai_family = PF_UNSPEC;
143135446Strhodes#ifdef AI_ADDRCONFIG
144135446Strhodes		hints.ai_flags = AI_ADDRCONFIG;
145135446Strhodes#endif
146135446Strhodes	}
147135446Strhodes	hints.ai_socktype = SOCK_STREAM;
148135446Strhodes#ifdef AI_ADDRCONFIG
149135446Strhodes again:
150135446Strhodes#endif
151135446Strhodes	result = getaddrinfo(hostname, NULL, &hints, &ai);
152135446Strhodes	switch (result) {
153135446Strhodes	case 0:
154135446Strhodes		break;
155135446Strhodes	case EAI_NONAME:
156135446Strhodes#if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
157135446Strhodes	case EAI_NODATA:
158135446Strhodes#endif
159135446Strhodes		return (ISC_R_NOTFOUND);
160135446Strhodes#ifdef AI_ADDRCONFIG
161135446Strhodes	case EAI_BADFLAGS:
162135446Strhodes		if ((hints.ai_flags & AI_ADDRCONFIG) != 0) {
163135446Strhodes			hints.ai_flags &= ~AI_ADDRCONFIG;
164135446Strhodes			goto again;
165135446Strhodes		}
166135446Strhodes#endif
167135446Strhodes	default:
168135446Strhodes		return (ISC_R_FAILURE);
169135446Strhodes	}
170135446Strhodes	for (tmpai = ai, i = 0;
171135446Strhodes	     tmpai != NULL && i < addrsize;
172135446Strhodes	     tmpai = tmpai->ai_next)
173135446Strhodes	{
174135446Strhodes		if (tmpai->ai_family != AF_INET &&
175135446Strhodes		    tmpai->ai_family != AF_INET6)
176135446Strhodes			continue;
177135446Strhodes		if (tmpai->ai_family == AF_INET) {
178135446Strhodes			struct sockaddr_in *sin;
179135446Strhodes			sin = (struct sockaddr_in *)tmpai->ai_addr;
180135446Strhodes			isc_sockaddr_fromin(&addrs[i], &sin->sin_addr, port);
181135446Strhodes		} else {
182135446Strhodes			struct sockaddr_in6 *sin6;
183135446Strhodes			sin6 = (struct sockaddr_in6 *)tmpai->ai_addr;
184135446Strhodes			isc_sockaddr_fromin6(&addrs[i], &sin6->sin6_addr,
185135446Strhodes					     port);
186135446Strhodes		}
187135446Strhodes		i++;
188135446Strhodes
189135446Strhodes	}
190135446Strhodes	freeaddrinfo(ai);
191135446Strhodes	*addrcount = i;
192135446Strhodes#else
193135446Strhodes	he = gethostbyname(hostname);
194135446Strhodes	if (he == NULL) {
195135446Strhodes		switch (h_errno) {
196135446Strhodes		case HOST_NOT_FOUND:
197135446Strhodes#ifdef NO_DATA
198135446Strhodes		case NO_DATA:
199135446Strhodes#endif
200135446Strhodes#if defined(NO_ADDRESS) && (!defined(NO_DATA) || (NO_DATA != NO_ADDRESS))
201135446Strhodes		case NO_ADDRESS:
202135446Strhodes#endif
203135446Strhodes			return (ISC_R_NOTFOUND);
204135446Strhodes		default:
205135446Strhodes			return (ISC_R_FAILURE);
206135446Strhodes		}
207135446Strhodes	}
208135446Strhodes	if (he->h_addrtype != AF_INET && he->h_addrtype != AF_INET6)
209135446Strhodes		return (ISC_R_NOTFOUND);
210135446Strhodes	for (i = 0; i < addrsize; i++) {
211135446Strhodes		if (he->h_addrtype == AF_INET) {
212135446Strhodes			struct in_addr *inp;
213135446Strhodes			inp = (struct in_addr *)(he->h_addr_list[i]);
214135446Strhodes			if (inp == NULL)
215135446Strhodes				break;
216135446Strhodes			isc_sockaddr_fromin(&addrs[i], inp, port);
217135446Strhodes		} else {
218135446Strhodes			struct in6_addr *in6p;
219135446Strhodes			in6p = (struct in6_addr *)(he->h_addr_list[i]);
220135446Strhodes			if (in6p == NULL)
221135446Strhodes				break;
222135446Strhodes			isc_sockaddr_fromin6(&addrs[i], in6p, port);
223135446Strhodes		}
224135446Strhodes	}
225135446Strhodes	*addrcount = i;
226135446Strhodes#endif
227135446Strhodes	if (*addrcount == 0)
228135446Strhodes		return (ISC_R_NOTFOUND);
229135446Strhodes	else
230135446Strhodes		return (ISC_R_SUCCESS);
231135446Strhodes}
232