xref: /netbsd/external/bsd/ntp/dist/lib/isc/win32/net.c (revision 6550d01e)
1 /*	$NetBSD: net.c,v 1.1.1.1 2009/12/13 16:54:41 kardel Exp $	*/
2 
3 /*
4  * Copyright (C) 2004, 2005, 2007, 2008  Internet Systems Consortium, Inc. ("ISC")
5  * Copyright (C) 1999-2003  Internet Software Consortium.
6  *
7  * Permission to use, copy, modify, and/or distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
12  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
14  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
16  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17  * PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 /* Id: net.c,v 1.18 2008/08/08 05:06:49 marka Exp */
21 
22 #include <config.h>
23 
24 #include <errno.h>
25 #include <unistd.h>
26 
27 #include <isc/log.h>
28 #include <isc/msgs.h>
29 #include <isc/net.h>
30 #include <isc/once.h>
31 #include <isc/strerror.h>
32 #include <isc/string.h>
33 #include <isc/util.h>
34 
35 /*%
36  * Definitions about UDP port range specification.  This is a total mess of
37  * portability variants: some use sysctl (but the sysctl names vary), some use
38  * system-specific interfaces, some have the same interface for IPv4 and IPv6,
39  * some separate them, etc...
40  */
41 
42 /*%
43  * The last resort defaults: use all non well known port space
44  */
45 #ifndef ISC_NET_PORTRANGELOW
46 #define ISC_NET_PORTRANGELOW 1024
47 #endif	/* ISC_NET_PORTRANGELOW */
48 #ifndef ISC_NET_PORTRANGEHIGH
49 #define ISC_NET_PORTRANGEHIGH 65535
50 #endif	/* ISC_NET_PORTRANGEHIGH */
51 
52 #if defined(ISC_PLATFORM_HAVEIPV6) && defined(ISC_PLATFORM_NEEDIN6ADDRANY)
53 const struct in6_addr isc_net_in6addrany = IN6ADDR_ANY_INIT;
54 #endif
55 
56 #if defined(ISC_PLATFORM_HAVEIPV6) && defined(ISC_PLATFORM_NEEDIN6ADDRLOOPBACK)
57 const struct in6_addr isc_net_in6addrloop = IN6ADDR_LOOPBACK_INIT;
58 #endif
59 
60 
61 static isc_once_t 	once = ISC_ONCE_INIT;
62 static isc_once_t 	once_ipv6only = ISC_ONCE_INIT;
63 static isc_once_t 	once_ipv6pktinfo = ISC_ONCE_INIT;
64 static isc_result_t	ipv4_result = ISC_R_NOTFOUND;
65 static isc_result_t	ipv6_result = ISC_R_NOTFOUND;
66 static isc_result_t	ipv6only_result = ISC_R_NOTFOUND;
67 static isc_result_t	ipv6pktinfo_result = ISC_R_NOTFOUND;
68 
69 void InitSockets(void);
70 
71 static isc_result_t
72 try_proto(int domain) {
73 	SOCKET s;
74 	isc_result_t result = ISC_R_SUCCESS;
75 	char strbuf[ISC_STRERRORSIZE];
76 	int errval;
77 
78 	s = socket(domain, SOCK_STREAM, IPPROTO_TCP);
79 	if (s == INVALID_SOCKET) {
80 		errval = WSAGetLastError();
81 		switch (errval) {
82 		case WSAEAFNOSUPPORT:
83 		case WSAEPROTONOSUPPORT:
84 		case WSAEINVAL:
85 			return (ISC_R_NOTFOUND);
86 		default:
87 			isc__strerror(errval, strbuf, sizeof(strbuf));
88 			UNEXPECTED_ERROR(__FILE__, __LINE__,
89 					 "socket() %s: %s",
90 					 isc_msgcat_get(isc_msgcat,
91 							ISC_MSGSET_GENERAL,
92 							ISC_MSG_FAILED,
93 							"failed"),
94 					 strbuf);
95 			return (ISC_R_UNEXPECTED);
96 		}
97 	}
98 
99 	closesocket(s);
100 
101 	return (ISC_R_SUCCESS);
102 }
103 
104 static void
105 initialize_action(void) {
106 	InitSockets();
107 	ipv4_result = try_proto(PF_INET);
108 #ifdef ISC_PLATFORM_HAVEIPV6
109 #ifdef WANT_IPV6
110 #ifdef ISC_PLATFORM_HAVEIN6PKTINFO
111 	ipv6_result = try_proto(PF_INET6);
112 #endif
113 #endif
114 #endif
115 }
116 
117 static void
118 initialize(void) {
119 	RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS);
120 }
121 
122 isc_result_t
123 isc_net_probeipv4(void) {
124 	initialize();
125 	return (ipv4_result);
126 }
127 
128 isc_result_t
129 isc_net_probeipv6(void) {
130 	initialize();
131 	return (ipv6_result);
132 }
133 
134 isc_result_t
135 isc_net_probeunix(void) {
136 	return (ISC_R_NOTFOUND);
137 }
138 
139 #ifdef ISC_PLATFORM_HAVEIPV6
140 #ifdef WANT_IPV6
141 static void
142 try_ipv6only(void) {
143 #ifdef IPV6_V6ONLY
144 	SOCKET s;
145 	int on;
146 	char strbuf[ISC_STRERRORSIZE];
147 #endif
148 	isc_result_t result;
149 
150 	result = isc_net_probeipv6();
151 	if (result != ISC_R_SUCCESS) {
152 		ipv6only_result = result;
153 		return;
154 	}
155 
156 #ifndef IPV6_V6ONLY
157 	ipv6only_result = ISC_R_NOTFOUND;
158 	return;
159 #else
160 	/* check for TCP sockets */
161 	s = socket(PF_INET6, SOCK_STREAM, 0);
162 	if (s == INVALID_SOCKET) {
163 		isc__strerror(errno, strbuf, sizeof(strbuf));
164 		UNEXPECTED_ERROR(__FILE__, __LINE__,
165 				 "socket() %s: %s",
166 				 isc_msgcat_get(isc_msgcat,
167 						ISC_MSGSET_GENERAL,
168 						ISC_MSG_FAILED,
169 						"failed"),
170 				 strbuf);
171 		ipv6only_result = ISC_R_UNEXPECTED;
172 		return;
173 	}
174 
175 	on = 1;
176 	if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&on, sizeof(on)) < 0) {
177 		ipv6only_result = ISC_R_NOTFOUND;
178 		goto close;
179 	}
180 
181 	closesocket(s);
182 
183 	/* check for UDP sockets */
184 	s = socket(PF_INET6, SOCK_DGRAM, 0);
185 	if (s == INVALID_SOCKET) {
186 		isc__strerror(errno, strbuf, sizeof(strbuf));
187 		UNEXPECTED_ERROR(__FILE__, __LINE__,
188 				 "socket() %s: %s",
189 				 isc_msgcat_get(isc_msgcat,
190 						ISC_MSGSET_GENERAL,
191 						ISC_MSG_FAILED,
192 						"failed"),
193 				 strbuf);
194 		ipv6only_result = ISC_R_UNEXPECTED;
195 		return;
196 	}
197 
198 	on = 1;
199 	if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&on, sizeof(on)) < 0) {
200 		ipv6only_result = ISC_R_NOTFOUND;
201 		goto close;
202 	}
203 
204 	ipv6only_result = ISC_R_SUCCESS;
205 
206 close:
207 	closesocket(s);
208 	return;
209 #endif /* IPV6_V6ONLY */
210 }
211 
212 static void
213 initialize_ipv6only(void) {
214 	RUNTIME_CHECK(isc_once_do(&once_ipv6only,
215 				  try_ipv6only) == ISC_R_SUCCESS);
216 }
217 
218 static void
219 try_ipv6pktinfo(void) {
220 	int s, on;
221 	char strbuf[ISC_STRERRORSIZE];
222 	isc_result_t result;
223 	int optname;
224 
225 	result = isc_net_probeipv6();
226 	if (result != ISC_R_SUCCESS) {
227 		ipv6pktinfo_result = result;
228 		return;
229 	}
230 
231 	/* we only use this for UDP sockets */
232 	s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
233 	if (s == INVALID_SOCKET) {
234 		isc__strerror(errno, strbuf, sizeof(strbuf));
235 		UNEXPECTED_ERROR(__FILE__, __LINE__,
236 				 "socket() %s: %s",
237 				 isc_msgcat_get(isc_msgcat,
238 						ISC_MSGSET_GENERAL,
239 						ISC_MSG_FAILED,
240 						"failed"),
241 				 strbuf);
242 		ipv6pktinfo_result = ISC_R_UNEXPECTED;
243 		return;
244 	}
245 
246 #ifdef IPV6_RECVPKTINFO
247 	optname = IPV6_RECVPKTINFO;
248 #else
249 	optname = IPV6_PKTINFO;
250 #endif
251 	on = 1;
252 	if (setsockopt(s, IPPROTO_IPV6, optname, (const char *) &on,
253 		       sizeof(on)) < 0) {
254 		ipv6pktinfo_result = ISC_R_NOTFOUND;
255 		goto close;
256 	}
257 
258 	ipv6pktinfo_result = ISC_R_SUCCESS;
259 
260 close:
261 	closesocket(s);
262 	return;
263 }
264 
265 static void
266 initialize_ipv6pktinfo(void) {
267 	RUNTIME_CHECK(isc_once_do(&once_ipv6pktinfo,
268 				  try_ipv6pktinfo) == ISC_R_SUCCESS);
269 }
270 #endif /* WANT_IPV6 */
271 #endif /* ISC_PLATFORM_HAVEIPV6 */
272 
273 isc_result_t
274 isc_net_probe_ipv6only(void) {
275 #ifdef ISC_PLATFORM_HAVEIPV6
276 #ifdef WANT_IPV6
277 	initialize_ipv6only();
278 #else
279 	ipv6only_result = ISC_R_NOTFOUND;
280 #endif
281 #endif
282 	return (ipv6only_result);
283 }
284 
285 isc_result_t
286 isc_net_probe_ipv6pktinfo(void) {
287 #ifdef ISC_PLATFORM_HAVEIPV6
288 #ifdef WANT_IPV6
289 	initialize_ipv6pktinfo();
290 #else
291 	ipv6pktinfo_result = ISC_R_NOTFOUND;
292 #endif
293 #endif
294 	return (ipv6pktinfo_result);
295 }
296 
297 isc_result_t
298 isc_net_getudpportrange(int af, in_port_t *low, in_port_t *high) {
299 	int result = ISC_R_FAILURE;
300 
301 	REQUIRE(low != NULL && high != NULL);
302 
303 	UNUSED(af);
304 
305 	if (result != ISC_R_SUCCESS) {
306 		*low = ISC_NET_PORTRANGELOW;
307 		*high = ISC_NET_PORTRANGEHIGH;
308 	}
309 
310 	return (ISC_R_SUCCESS);	/* we currently never fail in this function */
311 }
312 
313 void
314 isc_net_disableipv4(void) {
315 	initialize();
316 	if (ipv4_result == ISC_R_SUCCESS)
317 		ipv4_result = ISC_R_DISABLED;
318 }
319 
320 void
321 isc_net_disableipv6(void) {
322 	initialize();
323 	if (ipv6_result == ISC_R_SUCCESS)
324 		ipv6_result = ISC_R_DISABLED;
325 }
326 
327 void
328 isc_net_enableipv4(void) {
329 	initialize();
330 	if (ipv4_result == ISC_R_DISABLED)
331 		ipv4_result = ISC_R_SUCCESS;
332 }
333 
334 void
335 isc_net_enableipv6(void) {
336 	initialize();
337 	if (ipv6_result == ISC_R_DISABLED)
338 		ipv6_result = ISC_R_SUCCESS;
339 }
340