xref: /freebsd/contrib/ntp/libntp/lib/isc/win32/net.c (revision 02e9120893770924227138ba49df1edb3896112a)
1 /*
2  * Copyright (C) 2004, 2005, 2007-2009, 2011, 2012  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$ */
19 
20 #include <config.h>
21 
22 #include <errno.h>
23 #include <unistd.h>
24 
25 #include <isc/log.h>
26 #include <isc/msgs.h>
27 #include <isc/net.h>
28 #include <isc/once.h>
29 #include <isc/strerror.h>
30 #include <isc/string.h>
31 #include <isc/util.h>
32 
33 /*%
34  * Definitions about UDP port range specification.  This is a total mess of
35  * portability variants: some use sysctl (but the sysctl names vary), some use
36  * system-specific interfaces, some have the same interface for IPv4 and IPv6,
37  * some separate them, etc...
38  */
39 
40 /*%
41  * The last resort defaults: use all non well known port space
42  */
43 #ifndef ISC_NET_PORTRANGELOW
44 #define ISC_NET_PORTRANGELOW 1024
45 #endif	/* ISC_NET_PORTRANGELOW */
46 #ifndef ISC_NET_PORTRANGEHIGH
47 #define ISC_NET_PORTRANGEHIGH 65535
48 #endif	/* ISC_NET_PORTRANGEHIGH */
49 
50 #if defined(ISC_PLATFORM_NEEDIN6ADDRANY)
51 const struct in6_addr isc_net_in6addrany = IN6ADDR_ANY_INIT;
52 #endif
53 
54 #if defined(ISC_PLATFORM_NEEDIN6ADDRLOOPBACK)
55 const struct in6_addr isc_net_in6addrloop = IN6ADDR_LOOPBACK_INIT;
56 #endif
57 
58 
59 static isc_once_t 	once = ISC_ONCE_INIT;
60 static isc_once_t 	once_ipv6only = ISC_ONCE_INIT;
61 static isc_once_t 	once_ipv6pktinfo = ISC_ONCE_INIT;
62 static isc_result_t	ipv4_result = ISC_R_NOTFOUND;
63 static isc_result_t	ipv6_result = ISC_R_NOTFOUND;
64 static isc_result_t	ipv6only_result = ISC_R_NOTFOUND;
65 static isc_result_t	ipv6pktinfo_result = ISC_R_NOTFOUND;
66 
67 void InitSockets(void);
68 
69 static isc_result_t
70 try_proto(int domain) {
71 	SOCKET s;
72 	char strbuf[ISC_STRERRORSIZE];
73 	int errval;
74 
75 	s = socket(domain, SOCK_STREAM, IPPROTO_TCP);
76 	if (s == INVALID_SOCKET) {
77 		errval = WSAGetLastError();
78 		switch (errval) {
79 		case WSAEAFNOSUPPORT:
80 		case WSAEPROTONOSUPPORT:
81 		case WSAEINVAL:
82 			return (ISC_R_NOTFOUND);
83 		default:
84 			isc__strerror(errval, strbuf, sizeof(strbuf));
85 			UNEXPECTED_ERROR(__FILE__, __LINE__,
86 					 "socket() %s: %s",
87 					 isc_msgcat_get(isc_msgcat,
88 							ISC_MSGSET_GENERAL,
89 							ISC_MSG_FAILED,
90 							"failed"),
91 					 strbuf);
92 			return (ISC_R_UNEXPECTED);
93 		}
94 	}
95 
96 	closesocket(s);
97 
98 	return (ISC_R_SUCCESS);
99 }
100 
101 static void
102 initialize_action(void) {
103 	InitSockets();
104 	ipv4_result = try_proto(PF_INET);
105 #ifdef ISC_PLATFORM_HAVEIPV6
106 #ifdef WANT_IPV6
107 #ifdef ISC_PLATFORM_HAVEIN6PKTINFO
108 	ipv6_result = try_proto(PF_INET6);
109 #endif
110 #endif
111 #endif
112 }
113 
114 static void
115 initialize(void) {
116 	RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS);
117 }
118 
119 isc_result_t
120 isc_net_probeipv4(void) {
121 	initialize();
122 	return (ipv4_result);
123 }
124 
125 isc_result_t
126 isc_net_probeipv6(void) {
127 	initialize();
128 	return (ipv6_result);
129 }
130 
131 isc_result_t
132 isc_net_probeunix(void) {
133 	return (ISC_R_NOTFOUND);
134 }
135 
136 #ifdef ISC_PLATFORM_HAVEIPV6
137 #ifdef WANT_IPV6
138 static void
139 try_ipv6only(void) {
140 #ifdef IPV6_V6ONLY
141 	SOCKET s;
142 	int on;
143 	char strbuf[ISC_STRERRORSIZE];
144 #endif
145 	isc_result_t result;
146 
147 	result = isc_net_probeipv6();
148 	if (result != ISC_R_SUCCESS) {
149 		ipv6only_result = result;
150 		return;
151 	}
152 
153 #ifndef IPV6_V6ONLY
154 	ipv6only_result = ISC_R_NOTFOUND;
155 	return;
156 #else
157 	/* check for TCP sockets */
158 	s = socket(PF_INET6, SOCK_STREAM, 0);
159 	if (s == INVALID_SOCKET) {
160 		isc__strerror(errno, strbuf, sizeof(strbuf));
161 		UNEXPECTED_ERROR(__FILE__, __LINE__,
162 				 "socket() %s: %s",
163 				 isc_msgcat_get(isc_msgcat,
164 						ISC_MSGSET_GENERAL,
165 						ISC_MSG_FAILED,
166 						"failed"),
167 				 strbuf);
168 		ipv6only_result = ISC_R_UNEXPECTED;
169 		return;
170 	}
171 
172 	on = 1;
173 	if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, (const char *)&on,
174 		       sizeof(on)) < 0) {
175 		ipv6only_result = ISC_R_NOTFOUND;
176 		goto close;
177 	}
178 
179 	closesocket(s);
180 
181 	/* check for UDP sockets */
182 	s = socket(PF_INET6, SOCK_DGRAM, 0);
183 	if (s == INVALID_SOCKET) {
184 		isc__strerror(errno, strbuf, sizeof(strbuf));
185 		UNEXPECTED_ERROR(__FILE__, __LINE__,
186 				 "socket() %s: %s",
187 				 isc_msgcat_get(isc_msgcat,
188 						ISC_MSGSET_GENERAL,
189 						ISC_MSG_FAILED,
190 						"failed"),
191 				 strbuf);
192 		ipv6only_result = ISC_R_UNEXPECTED;
193 		return;
194 	}
195 
196 	on = 1;
197 	if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, (const char *)&on,
198 		       sizeof(on)) < 0) {
199 		ipv6only_result = ISC_R_NOTFOUND;
200 		goto close;
201 	}
202 
203 	ipv6only_result = ISC_R_SUCCESS;
204 
205 close:
206 	closesocket(s);
207 	return;
208 #endif /* IPV6_V6ONLY */
209 }
210 
211 static void
212 initialize_ipv6only(void) {
213 	RUNTIME_CHECK(isc_once_do(&once_ipv6only,
214 				  try_ipv6only) == ISC_R_SUCCESS);
215 }
216 
217 static void
218 try_ipv6pktinfo(void) {
219 	SOCKET s;
220 	int 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