xref: /trueos/contrib/ntp/libisc/net.c (revision b3db078f21c6b171ca87f55006f0195783756e42)
1 /*
2  * Copyright (C) 2004  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 1999-2003  Internet Software Consortium.
4  *
5  * Permission to use, copy, modify, and 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: net.c,v 1.22.2.2.10.7 2004/04/29 01:31:22 marka Exp $ */
19 
20 #include <config.h>
21 
22 #include <errno.h>
23 #include <unistd.h>
24 
25 #include <isc/net.h>
26 #include <isc/once.h>
27 #include <isc/strerror.h>
28 #include <isc/string.h>
29 #include <isc/util.h>
30 
31 #if defined(ISC_PLATFORM_HAVEIPV6) && defined(ISC_PLATFORM_NEEDIN6ADDRANY)
32 const struct in6_addr isc_net_in6addrany = IN6ADDR_ANY_INIT;
33 #endif
34 
35 #if defined(ISC_PLATFORM_HAVEIPV6) && defined(ISC_PLATFORM_NEEDIN6ADDRLOOPBACK)
36 const struct in6_addr isc_net_in6addrloop = IN6ADDR_LOOPBACK_INIT;
37 #endif
38 
39 static isc_boolean_t 	once = ISC_FALSE;
40 static isc_once_t 	once_ipv6only = ISC_ONCE_INIT;
41 static isc_once_t 	once_ipv6pktinfo = ISC_ONCE_INIT;
42 static isc_result_t	ipv4_result = ISC_R_NOTFOUND;
43 static isc_result_t	ipv6_result = ISC_R_NOTFOUND;
44 static isc_result_t	ipv6only_result = ISC_R_NOTFOUND;
45 static isc_result_t	ipv6pktinfo_result = ISC_R_NOTFOUND;
46 
47 static isc_result_t
try_proto(int domain)48 try_proto(int domain) {
49 	int s;
50 	isc_result_t result = ISC_R_SUCCESS;
51 	char strbuf[ISC_STRERRORSIZE];
52 
53 	s = socket(domain, SOCK_STREAM, 0);
54 	if (s == -1) {
55 		switch (errno) {
56 #ifdef EAFNOSUPPORT
57 		case EAFNOSUPPORT:
58 #endif
59 #ifdef EPROTONOSUPPORT
60 		case EPROTONOSUPPORT:
61 #endif
62 #ifdef EINVAL
63 		case EINVAL:
64 #endif
65 			return (ISC_R_NOTFOUND);
66 		default:
67 			isc__strerror(errno, strbuf, sizeof(strbuf));
68 			UNEXPECTED_ERROR(__FILE__, __LINE__,
69 					 "socket() failed: %s",
70 					 strbuf);
71 			return (ISC_R_UNEXPECTED);
72 		}
73 	}
74 
75 #ifdef ISC_PLATFORM_HAVEIPV6
76 #ifdef WANT_IPV6
77 #ifdef ISC_PLATFORM_HAVEIN6PKTINFO
78 	if (domain == PF_INET6) {
79 		struct sockaddr_in6 sin6;
80 		GETSOCKNAME_SOCKLEN_TYPE len;
81 
82 		/*
83 		 * Check to see if IPv6 is broken, as is common on Linux.
84 		 */
85 		len = sizeof(sin6);
86 		if (getsockname(s, (struct sockaddr *)&sin6, &len) < 0)
87 		{
88 			result = ISC_R_NOTFOUND;
89 		} else {
90 			if (len == sizeof(struct sockaddr_in6))
91 				result = ISC_R_SUCCESS;
92 			else {
93 				result = ISC_R_NOTFOUND;
94 			}
95 		}
96 	}
97 #endif
98 #endif
99 #endif
100 
101 	(void)close(s);
102 
103 	return (result);
104 }
105 
106 static void
initialize_action(void)107 initialize_action(void) {
108 	ipv4_result = try_proto(PF_INET);
109 #ifdef ISC_PLATFORM_HAVEIPV6
110 #ifdef WANT_IPV6
111 #ifdef ISC_PLATFORM_HAVEIN6PKTINFO
112 	ipv6_result = try_proto(PF_INET6);
113 #endif
114 #endif
115 #endif
116 }
117 
118 static void
initialize(void)119 initialize(void) {
120 	if(once == ISC_FALSE) {
121 		initialize_action();
122 		once = ISC_TRUE;
123 	}
124 }
125 
126 isc_result_t
isc_net_probeipv4(void)127 isc_net_probeipv4(void) {
128 	initialize();
129 	return (ipv4_result);
130 }
131 
132 isc_result_t
isc_net_probeipv6(void)133 isc_net_probeipv6(void) {
134 	initialize();
135 	return (ipv6_result);
136 }
137 
138 #ifdef ISC_PLATFORM_HAVEIPV6
139 #ifdef WANT_IPV6
140 static void
try_ipv6only(void)141 try_ipv6only(void) {
142 #ifdef IPV6_V6ONLY
143 	int s, on;
144 	char strbuf[ISC_STRERRORSIZE];
145 #endif
146 	isc_result_t result;
147 
148 	result = isc_net_probeipv6();
149 	if (result != ISC_R_SUCCESS) {
150 		ipv6only_result = result;
151 		return;
152 	}
153 
154 #ifndef IPV6_V6ONLY
155 	ipv6only_result = ISC_R_NOTFOUND;
156 	return;
157 #else
158 	/* check for TCP sockets */
159 	s = socket(PF_INET6, SOCK_STREAM, 0);
160 	if (s == -1) {
161 		isc__strerror(errno, strbuf, sizeof(strbuf));
162 		UNEXPECTED_ERROR(__FILE__, __LINE__,
163 				 "socket() failed: %s",
164 				 strbuf);
165 		ipv6only_result = ISC_R_UNEXPECTED;
166 		return;
167 	}
168 
169 	on = 1;
170 	if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) {
171 		ipv6only_result = ISC_R_NOTFOUND;
172 		goto close;
173 	}
174 
175 	close(s);
176 
177 	/* check for UDP sockets */
178 	s = socket(PF_INET6, SOCK_DGRAM, 0);
179 	if (s == -1) {
180 		isc__strerror(errno, strbuf, sizeof(strbuf));
181 		UNEXPECTED_ERROR(__FILE__, __LINE__,
182 				 "socket() failed: %s",
183 				 strbuf);
184 		ipv6only_result = ISC_R_UNEXPECTED;
185 		return;
186 	}
187 
188 	on = 1;
189 	if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) {
190 		ipv6only_result = ISC_R_NOTFOUND;
191 		goto close;
192 	}
193 
194 	close(s);
195 
196 	ipv6only_result = ISC_R_SUCCESS;
197 
198 close:
199 	close(s);
200 	return;
201 #endif /* IPV6_V6ONLY */
202 }
203 
204 static void
initialize_ipv6only(void)205 initialize_ipv6only(void) {
206 	RUNTIME_CHECK(isc_once_do(&once_ipv6only,
207 				  try_ipv6only) == ISC_R_SUCCESS);
208 }
209 
210 static void
try_ipv6pktinfo(void)211 try_ipv6pktinfo(void) {
212 	int s, on;
213 	char strbuf[ISC_STRERRORSIZE];
214 	isc_result_t result;
215 	int optname;
216 
217 	result = isc_net_probeipv6();
218 	if (result != ISC_R_SUCCESS) {
219 		ipv6pktinfo_result = result;
220 		return;
221 	}
222 
223 	/* we only use this for UDP sockets */
224 	s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
225 	if (s == -1) {
226 		isc__strerror(errno, strbuf, sizeof(strbuf));
227 		UNEXPECTED_ERROR(__FILE__, __LINE__,
228 				 "socket() failed: %s",
229 				 strbuf);
230 		ipv6pktinfo_result = ISC_R_UNEXPECTED;
231 		return;
232 	}
233 
234 #ifdef IPV6_RECVPKTINFO
235 	optname = IPV6_RECVPKTINFO;
236 #else
237 	optname = IPV6_PKTINFO;
238 #endif
239 	on = 1;
240 	if (setsockopt(s, IPPROTO_IPV6, optname, &on, sizeof(on)) < 0) {
241 		ipv6pktinfo_result = ISC_R_NOTFOUND;
242 		goto close;
243 	}
244 
245 	close(s);
246 	ipv6pktinfo_result = ISC_R_SUCCESS;
247 
248 close:
249 	close(s);
250 	return;
251 }
252 
253 static void
initialize_ipv6pktinfo(void)254 initialize_ipv6pktinfo(void) {
255 	RUNTIME_CHECK(isc_once_do(&once_ipv6pktinfo,
256 				  try_ipv6pktinfo) == ISC_R_SUCCESS);
257 }
258 #endif /* WANT_IPV6 */
259 #endif /* ISC_PLATFORM_HAVEIPV6 */
260 
261 isc_result_t
isc_net_probe_ipv6only(void)262 isc_net_probe_ipv6only(void) {
263 #ifdef ISC_PLATFORM_HAVEIPV6
264 #ifdef WANT_IPV6
265 	initialize_ipv6only();
266 #else
267 	ipv6only_result = ISC_R_NOTFOUND;
268 #endif
269 #endif
270 	return (ipv6only_result);
271 }
272 
273 isc_result_t
isc_net_probe_ipv6pktinfo(void)274 isc_net_probe_ipv6pktinfo(void) {
275 #ifdef ISC_PLATFORM_HAVEIPV6
276 #ifdef WANT_IPV6
277 	initialize_ipv6pktinfo();
278 #else
279 	ipv6pktinfo_result = ISC_R_NOTFOUND;
280 #endif
281 #endif
282 	return (ipv6pktinfo_result);
283 }
284 
285 void
isc_net_disableipv4(void)286 isc_net_disableipv4(void) {
287 	initialize();
288 	if (ipv4_result == ISC_R_SUCCESS)
289 		ipv4_result = ISC_R_DISABLED;
290 }
291 
292 void
isc_net_disableipv6(void)293 isc_net_disableipv6(void) {
294 	initialize();
295 	if (ipv6_result == ISC_R_SUCCESS)
296 		ipv6_result = ISC_R_DISABLED;
297 }
298 
299 void
isc_net_enableipv4(void)300 isc_net_enableipv4(void) {
301 	initialize();
302 	if (ipv4_result == ISC_R_DISABLED)
303 		ipv4_result = ISC_R_SUCCESS;
304 }
305 
306 void
isc_net_enableipv6(void)307 isc_net_enableipv6(void) {
308 	initialize();
309 	if (ipv6_result == ISC_R_DISABLED)
310 		ipv6_result = ISC_R_SUCCESS;
311 }
312