1258945Sroberto/*
2280849Scy * Copyright (C) 2004, 2005, 2007-2009, 2011, 2012  Internet Systems Consortium, Inc. ("ISC")
3258945Sroberto * Copyright (C) 1999-2003  Internet Software Consortium.
4258945Sroberto *
5258945Sroberto * Permission to use, copy, modify, and/or distribute this software for any
6258945Sroberto * purpose with or without fee is hereby granted, provided that the above
7258945Sroberto * copyright notice and this permission notice appear in all copies.
8258945Sroberto *
9258945Sroberto * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10258945Sroberto * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11258945Sroberto * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12258945Sroberto * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13258945Sroberto * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14258945Sroberto * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15258945Sroberto * PERFORMANCE OF THIS SOFTWARE.
16258945Sroberto */
17258945Sroberto
18280849Scy/* $Id$ */
19258945Sroberto
20258945Sroberto#include <config.h>
21258945Sroberto
22258945Sroberto#include <errno.h>
23258945Sroberto#include <unistd.h>
24258945Sroberto
25258945Sroberto#include <isc/log.h>
26258945Sroberto#include <isc/msgs.h>
27258945Sroberto#include <isc/net.h>
28258945Sroberto#include <isc/once.h>
29258945Sroberto#include <isc/strerror.h>
30258945Sroberto#include <isc/string.h>
31258945Sroberto#include <isc/util.h>
32258945Sroberto
33258945Sroberto/*%
34258945Sroberto * Definitions about UDP port range specification.  This is a total mess of
35258945Sroberto * portability variants: some use sysctl (but the sysctl names vary), some use
36258945Sroberto * system-specific interfaces, some have the same interface for IPv4 and IPv6,
37258945Sroberto * some separate them, etc...
38258945Sroberto */
39258945Sroberto
40258945Sroberto/*%
41258945Sroberto * The last resort defaults: use all non well known port space
42258945Sroberto */
43258945Sroberto#ifndef ISC_NET_PORTRANGELOW
44258945Sroberto#define ISC_NET_PORTRANGELOW 1024
45258945Sroberto#endif	/* ISC_NET_PORTRANGELOW */
46258945Sroberto#ifndef ISC_NET_PORTRANGEHIGH
47258945Sroberto#define ISC_NET_PORTRANGEHIGH 65535
48258945Sroberto#endif	/* ISC_NET_PORTRANGEHIGH */
49258945Sroberto
50258945Sroberto#if defined(ISC_PLATFORM_NEEDIN6ADDRANY)
51258945Srobertoconst struct in6_addr isc_net_in6addrany = IN6ADDR_ANY_INIT;
52258945Sroberto#endif
53258945Sroberto
54258945Sroberto#if defined(ISC_PLATFORM_NEEDIN6ADDRLOOPBACK)
55258945Srobertoconst struct in6_addr isc_net_in6addrloop = IN6ADDR_LOOPBACK_INIT;
56258945Sroberto#endif
57258945Sroberto
58258945Sroberto
59258945Srobertostatic isc_once_t 	once = ISC_ONCE_INIT;
60258945Srobertostatic isc_once_t 	once_ipv6only = ISC_ONCE_INIT;
61258945Srobertostatic isc_once_t 	once_ipv6pktinfo = ISC_ONCE_INIT;
62258945Srobertostatic isc_result_t	ipv4_result = ISC_R_NOTFOUND;
63258945Srobertostatic isc_result_t	ipv6_result = ISC_R_NOTFOUND;
64258945Srobertostatic isc_result_t	ipv6only_result = ISC_R_NOTFOUND;
65258945Srobertostatic isc_result_t	ipv6pktinfo_result = ISC_R_NOTFOUND;
66258945Sroberto
67258945Srobertovoid InitSockets(void);
68258945Sroberto
69258945Srobertostatic isc_result_t
70258945Srobertotry_proto(int domain) {
71258945Sroberto	SOCKET s;
72258945Sroberto	char strbuf[ISC_STRERRORSIZE];
73258945Sroberto	int errval;
74258945Sroberto
75258945Sroberto	s = socket(domain, SOCK_STREAM, IPPROTO_TCP);
76258945Sroberto	if (s == INVALID_SOCKET) {
77258945Sroberto		errval = WSAGetLastError();
78258945Sroberto		switch (errval) {
79258945Sroberto		case WSAEAFNOSUPPORT:
80258945Sroberto		case WSAEPROTONOSUPPORT:
81258945Sroberto		case WSAEINVAL:
82258945Sroberto			return (ISC_R_NOTFOUND);
83258945Sroberto		default:
84258945Sroberto			isc__strerror(errval, strbuf, sizeof(strbuf));
85258945Sroberto			UNEXPECTED_ERROR(__FILE__, __LINE__,
86258945Sroberto					 "socket() %s: %s",
87258945Sroberto					 isc_msgcat_get(isc_msgcat,
88258945Sroberto							ISC_MSGSET_GENERAL,
89258945Sroberto							ISC_MSG_FAILED,
90258945Sroberto							"failed"),
91258945Sroberto					 strbuf);
92258945Sroberto			return (ISC_R_UNEXPECTED);
93258945Sroberto		}
94258945Sroberto	}
95258945Sroberto
96258945Sroberto	closesocket(s);
97258945Sroberto
98258945Sroberto	return (ISC_R_SUCCESS);
99258945Sroberto}
100258945Sroberto
101258945Srobertostatic void
102258945Srobertoinitialize_action(void) {
103258945Sroberto	InitSockets();
104258945Sroberto	ipv4_result = try_proto(PF_INET);
105258945Sroberto#ifdef ISC_PLATFORM_HAVEIPV6
106258945Sroberto#ifdef WANT_IPV6
107258945Sroberto#ifdef ISC_PLATFORM_HAVEIN6PKTINFO
108258945Sroberto	ipv6_result = try_proto(PF_INET6);
109258945Sroberto#endif
110258945Sroberto#endif
111258945Sroberto#endif
112258945Sroberto}
113258945Sroberto
114258945Srobertostatic void
115258945Srobertoinitialize(void) {
116258945Sroberto	RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS);
117258945Sroberto}
118258945Sroberto
119258945Srobertoisc_result_t
120258945Srobertoisc_net_probeipv4(void) {
121258945Sroberto	initialize();
122258945Sroberto	return (ipv4_result);
123258945Sroberto}
124258945Sroberto
125258945Srobertoisc_result_t
126258945Srobertoisc_net_probeipv6(void) {
127258945Sroberto	initialize();
128258945Sroberto	return (ipv6_result);
129258945Sroberto}
130258945Sroberto
131258945Srobertoisc_result_t
132258945Srobertoisc_net_probeunix(void) {
133258945Sroberto	return (ISC_R_NOTFOUND);
134258945Sroberto}
135258945Sroberto
136258945Sroberto#ifdef ISC_PLATFORM_HAVEIPV6
137258945Sroberto#ifdef WANT_IPV6
138258945Srobertostatic void
139258945Srobertotry_ipv6only(void) {
140258945Sroberto#ifdef IPV6_V6ONLY
141258945Sroberto	SOCKET s;
142258945Sroberto	int on;
143258945Sroberto	char strbuf[ISC_STRERRORSIZE];
144258945Sroberto#endif
145258945Sroberto	isc_result_t result;
146258945Sroberto
147258945Sroberto	result = isc_net_probeipv6();
148258945Sroberto	if (result != ISC_R_SUCCESS) {
149258945Sroberto		ipv6only_result = result;
150258945Sroberto		return;
151258945Sroberto	}
152258945Sroberto
153258945Sroberto#ifndef IPV6_V6ONLY
154258945Sroberto	ipv6only_result = ISC_R_NOTFOUND;
155258945Sroberto	return;
156258945Sroberto#else
157258945Sroberto	/* check for TCP sockets */
158258945Sroberto	s = socket(PF_INET6, SOCK_STREAM, 0);
159258945Sroberto	if (s == INVALID_SOCKET) {
160258945Sroberto		isc__strerror(errno, strbuf, sizeof(strbuf));
161258945Sroberto		UNEXPECTED_ERROR(__FILE__, __LINE__,
162258945Sroberto				 "socket() %s: %s",
163258945Sroberto				 isc_msgcat_get(isc_msgcat,
164258945Sroberto						ISC_MSGSET_GENERAL,
165258945Sroberto						ISC_MSG_FAILED,
166258945Sroberto						"failed"),
167258945Sroberto				 strbuf);
168258945Sroberto		ipv6only_result = ISC_R_UNEXPECTED;
169258945Sroberto		return;
170258945Sroberto	}
171258945Sroberto
172258945Sroberto	on = 1;
173280849Scy	if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, (const char *)&on,
174280849Scy		       sizeof(on)) < 0) {
175258945Sroberto		ipv6only_result = ISC_R_NOTFOUND;
176258945Sroberto		goto close;
177258945Sroberto	}
178258945Sroberto
179258945Sroberto	closesocket(s);
180258945Sroberto
181258945Sroberto	/* check for UDP sockets */
182258945Sroberto	s = socket(PF_INET6, SOCK_DGRAM, 0);
183258945Sroberto	if (s == INVALID_SOCKET) {
184258945Sroberto		isc__strerror(errno, strbuf, sizeof(strbuf));
185258945Sroberto		UNEXPECTED_ERROR(__FILE__, __LINE__,
186258945Sroberto				 "socket() %s: %s",
187258945Sroberto				 isc_msgcat_get(isc_msgcat,
188258945Sroberto						ISC_MSGSET_GENERAL,
189258945Sroberto						ISC_MSG_FAILED,
190258945Sroberto						"failed"),
191258945Sroberto				 strbuf);
192258945Sroberto		ipv6only_result = ISC_R_UNEXPECTED;
193258945Sroberto		return;
194258945Sroberto	}
195258945Sroberto
196258945Sroberto	on = 1;
197280849Scy	if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, (const char *)&on,
198280849Scy		       sizeof(on)) < 0) {
199258945Sroberto		ipv6only_result = ISC_R_NOTFOUND;
200258945Sroberto		goto close;
201258945Sroberto	}
202258945Sroberto
203258945Sroberto	ipv6only_result = ISC_R_SUCCESS;
204258945Sroberto
205258945Srobertoclose:
206258945Sroberto	closesocket(s);
207258945Sroberto	return;
208258945Sroberto#endif /* IPV6_V6ONLY */
209258945Sroberto}
210258945Sroberto
211258945Srobertostatic void
212258945Srobertoinitialize_ipv6only(void) {
213258945Sroberto	RUNTIME_CHECK(isc_once_do(&once_ipv6only,
214258945Sroberto				  try_ipv6only) == ISC_R_SUCCESS);
215258945Sroberto}
216258945Sroberto
217258945Srobertostatic void
218258945Srobertotry_ipv6pktinfo(void) {
219293650Sglebius	SOCKET s;
220293650Sglebius	int on;
221258945Sroberto	char strbuf[ISC_STRERRORSIZE];
222258945Sroberto	isc_result_t result;
223258945Sroberto	int optname;
224258945Sroberto
225258945Sroberto	result = isc_net_probeipv6();
226258945Sroberto	if (result != ISC_R_SUCCESS) {
227258945Sroberto		ipv6pktinfo_result = result;
228258945Sroberto		return;
229258945Sroberto	}
230258945Sroberto
231258945Sroberto	/* we only use this for UDP sockets */
232258945Sroberto	s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
233258945Sroberto	if (s == INVALID_SOCKET) {
234258945Sroberto		isc__strerror(errno, strbuf, sizeof(strbuf));
235258945Sroberto		UNEXPECTED_ERROR(__FILE__, __LINE__,
236258945Sroberto				 "socket() %s: %s",
237258945Sroberto				 isc_msgcat_get(isc_msgcat,
238258945Sroberto						ISC_MSGSET_GENERAL,
239258945Sroberto						ISC_MSG_FAILED,
240258945Sroberto						"failed"),
241258945Sroberto				 strbuf);
242258945Sroberto		ipv6pktinfo_result = ISC_R_UNEXPECTED;
243258945Sroberto		return;
244258945Sroberto	}
245258945Sroberto
246258945Sroberto#ifdef IPV6_RECVPKTINFO
247258945Sroberto	optname = IPV6_RECVPKTINFO;
248258945Sroberto#else
249258945Sroberto	optname = IPV6_PKTINFO;
250258945Sroberto#endif
251258945Sroberto	on = 1;
252258945Sroberto	if (setsockopt(s, IPPROTO_IPV6, optname, (const char *) &on,
253258945Sroberto		       sizeof(on)) < 0) {
254258945Sroberto		ipv6pktinfo_result = ISC_R_NOTFOUND;
255258945Sroberto		goto close;
256258945Sroberto	}
257258945Sroberto
258258945Sroberto	ipv6pktinfo_result = ISC_R_SUCCESS;
259258945Sroberto
260258945Srobertoclose:
261258945Sroberto	closesocket(s);
262258945Sroberto	return;
263258945Sroberto}
264258945Sroberto
265258945Srobertostatic void
266258945Srobertoinitialize_ipv6pktinfo(void) {
267258945Sroberto	RUNTIME_CHECK(isc_once_do(&once_ipv6pktinfo,
268258945Sroberto				  try_ipv6pktinfo) == ISC_R_SUCCESS);
269258945Sroberto}
270258945Sroberto#endif /* WANT_IPV6 */
271258945Sroberto#endif /* ISC_PLATFORM_HAVEIPV6 */
272258945Sroberto
273258945Srobertoisc_result_t
274258945Srobertoisc_net_probe_ipv6only(void) {
275258945Sroberto#ifdef ISC_PLATFORM_HAVEIPV6
276258945Sroberto#ifdef WANT_IPV6
277258945Sroberto	initialize_ipv6only();
278258945Sroberto#else
279258945Sroberto	ipv6only_result = ISC_R_NOTFOUND;
280258945Sroberto#endif
281258945Sroberto#endif
282258945Sroberto	return (ipv6only_result);
283258945Sroberto}
284258945Sroberto
285258945Srobertoisc_result_t
286258945Srobertoisc_net_probe_ipv6pktinfo(void) {
287258945Sroberto#ifdef ISC_PLATFORM_HAVEIPV6
288258945Sroberto#ifdef WANT_IPV6
289258945Sroberto	initialize_ipv6pktinfo();
290258945Sroberto#else
291258945Sroberto	ipv6pktinfo_result = ISC_R_NOTFOUND;
292258945Sroberto#endif
293258945Sroberto#endif
294258945Sroberto	return (ipv6pktinfo_result);
295258945Sroberto}
296258945Sroberto
297258945Srobertoisc_result_t
298258945Srobertoisc_net_getudpportrange(int af, in_port_t *low, in_port_t *high) {
299258945Sroberto	int result = ISC_R_FAILURE;
300258945Sroberto
301258945Sroberto	REQUIRE(low != NULL && high != NULL);
302258945Sroberto
303258945Sroberto	UNUSED(af);
304258945Sroberto
305258945Sroberto	if (result != ISC_R_SUCCESS) {
306258945Sroberto		*low = ISC_NET_PORTRANGELOW;
307258945Sroberto		*high = ISC_NET_PORTRANGEHIGH;
308258945Sroberto	}
309258945Sroberto
310258945Sroberto	return (ISC_R_SUCCESS);	/* we currently never fail in this function */
311258945Sroberto}
312258945Sroberto
313258945Srobertovoid
314258945Srobertoisc_net_disableipv4(void) {
315258945Sroberto	initialize();
316258945Sroberto	if (ipv4_result == ISC_R_SUCCESS)
317258945Sroberto		ipv4_result = ISC_R_DISABLED;
318258945Sroberto}
319258945Sroberto
320258945Srobertovoid
321258945Srobertoisc_net_disableipv6(void) {
322258945Sroberto	initialize();
323258945Sroberto	if (ipv6_result == ISC_R_SUCCESS)
324258945Sroberto		ipv6_result = ISC_R_DISABLED;
325258945Sroberto}
326258945Sroberto
327258945Srobertovoid
328258945Srobertoisc_net_enableipv4(void) {
329258945Sroberto	initialize();
330258945Sroberto	if (ipv4_result == ISC_R_DISABLED)
331258945Sroberto		ipv4_result = ISC_R_SUCCESS;
332258945Sroberto}
333258945Sroberto
334258945Srobertovoid
335258945Srobertoisc_net_enableipv6(void) {
336258945Sroberto	initialize();
337258945Sroberto	if (ipv6_result == ISC_R_DISABLED)
338258945Sroberto		ipv6_result = ISC_R_SUCCESS;
339258945Sroberto}
340