xref: /netbsd-src/external/bsd/ntp/dist/libntp/lib/isc/win32/net.c (revision eabc0478de71e4e011a5b4e0392741e01d491794)
1*eabc0478Schristos /*	$NetBSD: net.c,v 1.2 2024/08/18 20:47:16 christos Exp $	*/
2897be3a4Schristos 
3897be3a4Schristos /*
4897be3a4Schristos  * Copyright (C) 2004, 2005, 2007-2009, 2011, 2012  Internet Systems Consortium, Inc. ("ISC")
5897be3a4Schristos  * Copyright (C) 1999-2003  Internet Software Consortium.
6897be3a4Schristos  *
7897be3a4Schristos  * Permission to use, copy, modify, and/or distribute this software for any
8897be3a4Schristos  * purpose with or without fee is hereby granted, provided that the above
9897be3a4Schristos  * copyright notice and this permission notice appear in all copies.
10897be3a4Schristos  *
11897be3a4Schristos  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
12897be3a4Schristos  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13897be3a4Schristos  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
14897be3a4Schristos  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15897be3a4Schristos  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
16897be3a4Schristos  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17897be3a4Schristos  * PERFORMANCE OF THIS SOFTWARE.
18897be3a4Schristos  */
19897be3a4Schristos 
20897be3a4Schristos /* Id */
21897be3a4Schristos 
22897be3a4Schristos #include <config.h>
23897be3a4Schristos 
24897be3a4Schristos #include <errno.h>
25897be3a4Schristos #include <unistd.h>
26897be3a4Schristos 
27897be3a4Schristos #include <isc/log.h>
28897be3a4Schristos #include <isc/msgs.h>
29897be3a4Schristos #include <isc/net.h>
30897be3a4Schristos #include <isc/once.h>
31897be3a4Schristos #include <isc/strerror.h>
32897be3a4Schristos #include <isc/string.h>
33897be3a4Schristos #include <isc/util.h>
34897be3a4Schristos 
35897be3a4Schristos /*%
36897be3a4Schristos  * Definitions about UDP port range specification.  This is a total mess of
37897be3a4Schristos  * portability variants: some use sysctl (but the sysctl names vary), some use
38897be3a4Schristos  * system-specific interfaces, some have the same interface for IPv4 and IPv6,
39897be3a4Schristos  * some separate them, etc...
40897be3a4Schristos  */
41897be3a4Schristos 
42897be3a4Schristos /*%
43897be3a4Schristos  * The last resort defaults: use all non well known port space
44897be3a4Schristos  */
45897be3a4Schristos #ifndef ISC_NET_PORTRANGELOW
46897be3a4Schristos #define ISC_NET_PORTRANGELOW 1024
47897be3a4Schristos #endif	/* ISC_NET_PORTRANGELOW */
48897be3a4Schristos #ifndef ISC_NET_PORTRANGEHIGH
49897be3a4Schristos #define ISC_NET_PORTRANGEHIGH 65535
50897be3a4Schristos #endif	/* ISC_NET_PORTRANGEHIGH */
51897be3a4Schristos 
52897be3a4Schristos #if defined(ISC_PLATFORM_NEEDIN6ADDRANY)
53897be3a4Schristos const struct in6_addr isc_net_in6addrany = IN6ADDR_ANY_INIT;
54897be3a4Schristos #endif
55897be3a4Schristos 
56897be3a4Schristos #if defined(ISC_PLATFORM_NEEDIN6ADDRLOOPBACK)
57897be3a4Schristos const struct in6_addr isc_net_in6addrloop = IN6ADDR_LOOPBACK_INIT;
58897be3a4Schristos #endif
59897be3a4Schristos 
60897be3a4Schristos 
61897be3a4Schristos static isc_once_t 	once = ISC_ONCE_INIT;
62897be3a4Schristos static isc_once_t 	once_ipv6only = ISC_ONCE_INIT;
63897be3a4Schristos static isc_once_t 	once_ipv6pktinfo = ISC_ONCE_INIT;
64897be3a4Schristos static isc_result_t	ipv4_result = ISC_R_NOTFOUND;
65897be3a4Schristos static isc_result_t	ipv6_result = ISC_R_NOTFOUND;
66897be3a4Schristos static isc_result_t	ipv6only_result = ISC_R_NOTFOUND;
67897be3a4Schristos static isc_result_t	ipv6pktinfo_result = ISC_R_NOTFOUND;
68897be3a4Schristos 
69897be3a4Schristos void InitSockets(void);
70897be3a4Schristos 
71897be3a4Schristos static isc_result_t
72897be3a4Schristos try_proto(int domain) {
73897be3a4Schristos 	SOCKET s;
74897be3a4Schristos 	char strbuf[ISC_STRERRORSIZE];
75897be3a4Schristos 	int errval;
76897be3a4Schristos 
77897be3a4Schristos 	s = socket(domain, SOCK_STREAM, IPPROTO_TCP);
78897be3a4Schristos 	if (s == INVALID_SOCKET) {
79897be3a4Schristos 		errval = WSAGetLastError();
80897be3a4Schristos 		switch (errval) {
81897be3a4Schristos 		case WSAEAFNOSUPPORT:
82897be3a4Schristos 		case WSAEPROTONOSUPPORT:
83897be3a4Schristos 		case WSAEINVAL:
84897be3a4Schristos 			return (ISC_R_NOTFOUND);
85897be3a4Schristos 		default:
86897be3a4Schristos 			isc__strerror(errval, strbuf, sizeof(strbuf));
87897be3a4Schristos 			UNEXPECTED_ERROR(__FILE__, __LINE__,
88897be3a4Schristos 					 "socket() %s: %s",
89897be3a4Schristos 					 isc_msgcat_get(isc_msgcat,
90897be3a4Schristos 							ISC_MSGSET_GENERAL,
91897be3a4Schristos 							ISC_MSG_FAILED,
92897be3a4Schristos 							"failed"),
93897be3a4Schristos 					 strbuf);
94897be3a4Schristos 			return (ISC_R_UNEXPECTED);
95897be3a4Schristos 		}
96897be3a4Schristos 	}
97897be3a4Schristos 
98897be3a4Schristos 	closesocket(s);
99897be3a4Schristos 
100897be3a4Schristos 	return (ISC_R_SUCCESS);
101897be3a4Schristos }
102897be3a4Schristos 
103897be3a4Schristos static void
104897be3a4Schristos initialize_action(void) {
105897be3a4Schristos 	InitSockets();
106897be3a4Schristos 	ipv4_result = try_proto(PF_INET);
107897be3a4Schristos #ifdef ISC_PLATFORM_HAVEIPV6
108897be3a4Schristos #ifdef WANT_IPV6
109897be3a4Schristos #ifdef ISC_PLATFORM_HAVEIN6PKTINFO
110897be3a4Schristos 	ipv6_result = try_proto(PF_INET6);
111897be3a4Schristos #endif
112897be3a4Schristos #endif
113897be3a4Schristos #endif
114897be3a4Schristos }
115897be3a4Schristos 
116897be3a4Schristos static void
117897be3a4Schristos initialize(void) {
118897be3a4Schristos 	RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS);
119897be3a4Schristos }
120897be3a4Schristos 
121897be3a4Schristos isc_result_t
122897be3a4Schristos isc_net_probeipv4(void) {
123897be3a4Schristos 	initialize();
124897be3a4Schristos 	return (ipv4_result);
125897be3a4Schristos }
126897be3a4Schristos 
127897be3a4Schristos isc_result_t
128897be3a4Schristos isc_net_probeipv6(void) {
129897be3a4Schristos 	initialize();
130897be3a4Schristos 	return (ipv6_result);
131897be3a4Schristos }
132897be3a4Schristos 
133897be3a4Schristos isc_result_t
134897be3a4Schristos isc_net_probeunix(void) {
135897be3a4Schristos 	return (ISC_R_NOTFOUND);
136897be3a4Schristos }
137897be3a4Schristos 
138897be3a4Schristos #ifdef ISC_PLATFORM_HAVEIPV6
139897be3a4Schristos #ifdef WANT_IPV6
140897be3a4Schristos static void
141897be3a4Schristos try_ipv6only(void) {
142897be3a4Schristos #ifdef IPV6_V6ONLY
143897be3a4Schristos 	SOCKET s;
144897be3a4Schristos 	int on;
145897be3a4Schristos 	char strbuf[ISC_STRERRORSIZE];
146897be3a4Schristos #endif
147897be3a4Schristos 	isc_result_t result;
148897be3a4Schristos 
149897be3a4Schristos 	result = isc_net_probeipv6();
150897be3a4Schristos 	if (result != ISC_R_SUCCESS) {
151897be3a4Schristos 		ipv6only_result = result;
152897be3a4Schristos 		return;
153897be3a4Schristos 	}
154897be3a4Schristos 
155897be3a4Schristos #ifndef IPV6_V6ONLY
156897be3a4Schristos 	ipv6only_result = ISC_R_NOTFOUND;
157897be3a4Schristos 	return;
158897be3a4Schristos #else
159897be3a4Schristos 	/* check for TCP sockets */
160897be3a4Schristos 	s = socket(PF_INET6, SOCK_STREAM, 0);
161897be3a4Schristos 	if (s == INVALID_SOCKET) {
162897be3a4Schristos 		isc__strerror(errno, strbuf, sizeof(strbuf));
163897be3a4Schristos 		UNEXPECTED_ERROR(__FILE__, __LINE__,
164897be3a4Schristos 				 "socket() %s: %s",
165897be3a4Schristos 				 isc_msgcat_get(isc_msgcat,
166897be3a4Schristos 						ISC_MSGSET_GENERAL,
167897be3a4Schristos 						ISC_MSG_FAILED,
168897be3a4Schristos 						"failed"),
169897be3a4Schristos 				 strbuf);
170897be3a4Schristos 		ipv6only_result = ISC_R_UNEXPECTED;
171897be3a4Schristos 		return;
172897be3a4Schristos 	}
173897be3a4Schristos 
174897be3a4Schristos 	on = 1;
175897be3a4Schristos 	if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, (const char *)&on,
176897be3a4Schristos 		       sizeof(on)) < 0) {
177897be3a4Schristos 		ipv6only_result = ISC_R_NOTFOUND;
178897be3a4Schristos 		goto close;
179897be3a4Schristos 	}
180897be3a4Schristos 
181897be3a4Schristos 	closesocket(s);
182897be3a4Schristos 
183897be3a4Schristos 	/* check for UDP sockets */
184897be3a4Schristos 	s = socket(PF_INET6, SOCK_DGRAM, 0);
185897be3a4Schristos 	if (s == INVALID_SOCKET) {
186897be3a4Schristos 		isc__strerror(errno, strbuf, sizeof(strbuf));
187897be3a4Schristos 		UNEXPECTED_ERROR(__FILE__, __LINE__,
188897be3a4Schristos 				 "socket() %s: %s",
189897be3a4Schristos 				 isc_msgcat_get(isc_msgcat,
190897be3a4Schristos 						ISC_MSGSET_GENERAL,
191897be3a4Schristos 						ISC_MSG_FAILED,
192897be3a4Schristos 						"failed"),
193897be3a4Schristos 				 strbuf);
194897be3a4Schristos 		ipv6only_result = ISC_R_UNEXPECTED;
195897be3a4Schristos 		return;
196897be3a4Schristos 	}
197897be3a4Schristos 
198897be3a4Schristos 	on = 1;
199897be3a4Schristos 	if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, (const char *)&on,
200897be3a4Schristos 		       sizeof(on)) < 0) {
201897be3a4Schristos 		ipv6only_result = ISC_R_NOTFOUND;
202897be3a4Schristos 		goto close;
203897be3a4Schristos 	}
204897be3a4Schristos 
205897be3a4Schristos 	ipv6only_result = ISC_R_SUCCESS;
206897be3a4Schristos 
207897be3a4Schristos close:
208897be3a4Schristos 	closesocket(s);
209897be3a4Schristos 	return;
210897be3a4Schristos #endif /* IPV6_V6ONLY */
211897be3a4Schristos }
212897be3a4Schristos 
213897be3a4Schristos static void
214897be3a4Schristos initialize_ipv6only(void) {
215897be3a4Schristos 	RUNTIME_CHECK(isc_once_do(&once_ipv6only,
216897be3a4Schristos 				  try_ipv6only) == ISC_R_SUCCESS);
217897be3a4Schristos }
218897be3a4Schristos 
219897be3a4Schristos static void
220897be3a4Schristos try_ipv6pktinfo(void) {
221897be3a4Schristos 	SOCKET s;
222897be3a4Schristos 	int on;
223897be3a4Schristos 	char strbuf[ISC_STRERRORSIZE];
224897be3a4Schristos 	isc_result_t result;
225897be3a4Schristos 	int optname;
226897be3a4Schristos 
227897be3a4Schristos 	result = isc_net_probeipv6();
228897be3a4Schristos 	if (result != ISC_R_SUCCESS) {
229897be3a4Schristos 		ipv6pktinfo_result = result;
230897be3a4Schristos 		return;
231897be3a4Schristos 	}
232897be3a4Schristos 
233897be3a4Schristos 	/* we only use this for UDP sockets */
234897be3a4Schristos 	s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
235897be3a4Schristos 	if (s == INVALID_SOCKET) {
236897be3a4Schristos 		isc__strerror(errno, strbuf, sizeof(strbuf));
237897be3a4Schristos 		UNEXPECTED_ERROR(__FILE__, __LINE__,
238897be3a4Schristos 				 "socket() %s: %s",
239897be3a4Schristos 				 isc_msgcat_get(isc_msgcat,
240897be3a4Schristos 						ISC_MSGSET_GENERAL,
241897be3a4Schristos 						ISC_MSG_FAILED,
242897be3a4Schristos 						"failed"),
243897be3a4Schristos 				 strbuf);
244897be3a4Schristos 		ipv6pktinfo_result = ISC_R_UNEXPECTED;
245897be3a4Schristos 		return;
246897be3a4Schristos 	}
247897be3a4Schristos 
248897be3a4Schristos #ifdef IPV6_RECVPKTINFO
249897be3a4Schristos 	optname = IPV6_RECVPKTINFO;
250897be3a4Schristos #else
251897be3a4Schristos 	optname = IPV6_PKTINFO;
252897be3a4Schristos #endif
253897be3a4Schristos 	on = 1;
254897be3a4Schristos 	if (setsockopt(s, IPPROTO_IPV6, optname, (const char *) &on,
255897be3a4Schristos 		       sizeof(on)) < 0) {
256897be3a4Schristos 		ipv6pktinfo_result = ISC_R_NOTFOUND;
257897be3a4Schristos 		goto close;
258897be3a4Schristos 	}
259897be3a4Schristos 
260897be3a4Schristos 	ipv6pktinfo_result = ISC_R_SUCCESS;
261897be3a4Schristos 
262897be3a4Schristos close:
263897be3a4Schristos 	closesocket(s);
264897be3a4Schristos 	return;
265897be3a4Schristos }
266897be3a4Schristos 
267897be3a4Schristos static void
268897be3a4Schristos initialize_ipv6pktinfo(void) {
269897be3a4Schristos 	RUNTIME_CHECK(isc_once_do(&once_ipv6pktinfo,
270897be3a4Schristos 				  try_ipv6pktinfo) == ISC_R_SUCCESS);
271897be3a4Schristos }
272897be3a4Schristos #endif /* WANT_IPV6 */
273897be3a4Schristos #endif /* ISC_PLATFORM_HAVEIPV6 */
274897be3a4Schristos 
275897be3a4Schristos isc_result_t
276897be3a4Schristos isc_net_probe_ipv6only(void) {
277897be3a4Schristos #ifdef ISC_PLATFORM_HAVEIPV6
278897be3a4Schristos #ifdef WANT_IPV6
279897be3a4Schristos 	initialize_ipv6only();
280897be3a4Schristos #else
281897be3a4Schristos 	ipv6only_result = ISC_R_NOTFOUND;
282897be3a4Schristos #endif
283897be3a4Schristos #endif
284897be3a4Schristos 	return (ipv6only_result);
285897be3a4Schristos }
286897be3a4Schristos 
287897be3a4Schristos isc_result_t
288897be3a4Schristos isc_net_probe_ipv6pktinfo(void) {
289897be3a4Schristos #ifdef ISC_PLATFORM_HAVEIPV6
290897be3a4Schristos #ifdef WANT_IPV6
291897be3a4Schristos 	initialize_ipv6pktinfo();
292897be3a4Schristos #else
293897be3a4Schristos 	ipv6pktinfo_result = ISC_R_NOTFOUND;
294897be3a4Schristos #endif
295897be3a4Schristos #endif
296897be3a4Schristos 	return (ipv6pktinfo_result);
297897be3a4Schristos }
298897be3a4Schristos 
299897be3a4Schristos isc_result_t
300897be3a4Schristos isc_net_getudpportrange(int af, in_port_t *low, in_port_t *high) {
301897be3a4Schristos 	int result = ISC_R_FAILURE;
302897be3a4Schristos 
303897be3a4Schristos 	REQUIRE(low != NULL && high != NULL);
304897be3a4Schristos 
305897be3a4Schristos 	UNUSED(af);
306897be3a4Schristos 
307897be3a4Schristos 	if (result != ISC_R_SUCCESS) {
308897be3a4Schristos 		*low = ISC_NET_PORTRANGELOW;
309897be3a4Schristos 		*high = ISC_NET_PORTRANGEHIGH;
310897be3a4Schristos 	}
311897be3a4Schristos 
312897be3a4Schristos 	return (ISC_R_SUCCESS);	/* we currently never fail in this function */
313897be3a4Schristos }
314897be3a4Schristos 
315897be3a4Schristos void
316897be3a4Schristos isc_net_disableipv4(void) {
317897be3a4Schristos 	initialize();
318897be3a4Schristos 	if (ipv4_result == ISC_R_SUCCESS)
319897be3a4Schristos 		ipv4_result = ISC_R_DISABLED;
320897be3a4Schristos }
321897be3a4Schristos 
322897be3a4Schristos void
323897be3a4Schristos isc_net_disableipv6(void) {
324897be3a4Schristos 	initialize();
325897be3a4Schristos 	if (ipv6_result == ISC_R_SUCCESS)
326897be3a4Schristos 		ipv6_result = ISC_R_DISABLED;
327897be3a4Schristos }
328897be3a4Schristos 
329897be3a4Schristos void
330897be3a4Schristos isc_net_enableipv4(void) {
331897be3a4Schristos 	initialize();
332897be3a4Schristos 	if (ipv4_result == ISC_R_DISABLED)
333897be3a4Schristos 		ipv4_result = ISC_R_SUCCESS;
334897be3a4Schristos }
335897be3a4Schristos 
336897be3a4Schristos void
337897be3a4Schristos isc_net_enableipv6(void) {
338897be3a4Schristos 	initialize();
339897be3a4Schristos 	if (ipv6_result == ISC_R_DISABLED)
340897be3a4Schristos 		ipv6_result = ISC_R_SUCCESS;
341897be3a4Schristos }
342