update to 9.7.1-P2
[tridge/bind9.git] / lib / isc / win32 / net.c
1 /*
2  * Copyright (C) 2004, 2005, 2007-2009  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: net.c,v 1.20 2009/09/08 23:41:50 tbox Exp $ */
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_HAVEIPV6) && defined(ISC_PLATFORM_NEEDIN6ADDRANY)
51 const struct in6_addr isc_net_in6addrany = IN6ADDR_ANY_INIT;
52 #endif
53
54 static isc_once_t       once = ISC_ONCE_INIT;
55 static isc_once_t       once_ipv6only = ISC_ONCE_INIT;
56 static isc_once_t       once_ipv6pktinfo = ISC_ONCE_INIT;
57 static isc_result_t     ipv4_result = ISC_R_NOTFOUND;
58 static isc_result_t     ipv6_result = ISC_R_NOTFOUND;
59 static isc_result_t     ipv6only_result = ISC_R_NOTFOUND;
60 static isc_result_t     ipv6pktinfo_result = ISC_R_NOTFOUND;
61
62 void InitSockets(void);
63
64 static isc_result_t
65 try_proto(int domain) {
66         SOCKET s;
67         isc_result_t result = ISC_R_SUCCESS;
68         char strbuf[ISC_STRERRORSIZE];
69         int errval;
70
71         s = socket(domain, SOCK_STREAM, IPPROTO_TCP);
72         if (s == INVALID_SOCKET) {
73                 errval = WSAGetLastError();
74                 switch (errval) {
75                 case WSAEAFNOSUPPORT:
76                 case WSAEPROTONOSUPPORT:
77                 case WSAEINVAL:
78                         return (ISC_R_NOTFOUND);
79                 default:
80                         isc__strerror(errval, strbuf, sizeof(strbuf));
81                         UNEXPECTED_ERROR(__FILE__, __LINE__,
82                                          "socket() %s: %s",
83                                          isc_msgcat_get(isc_msgcat,
84                                                         ISC_MSGSET_GENERAL,
85                                                         ISC_MSG_FAILED,
86                                                         "failed"),
87                                          strbuf);
88                         return (ISC_R_UNEXPECTED);
89                 }
90         }
91
92         closesocket(s);
93
94         return (ISC_R_SUCCESS);
95 }
96
97 static void
98 initialize_action(void) {
99         InitSockets();
100         ipv4_result = try_proto(PF_INET);
101 #ifdef ISC_PLATFORM_HAVEIPV6
102 #ifdef WANT_IPV6
103 #ifdef ISC_PLATFORM_HAVEIN6PKTINFO
104         ipv6_result = try_proto(PF_INET6);
105 #endif
106 #endif
107 #endif
108 }
109
110 static void
111 initialize(void) {
112         RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS);
113 }
114
115 isc_result_t
116 isc_net_probeipv4(void) {
117         initialize();
118         return (ipv4_result);
119 }
120
121 isc_result_t
122 isc_net_probeipv6(void) {
123         initialize();
124         return (ipv6_result);
125 }
126
127 isc_result_t
128 isc_net_probeunix(void) {
129         return (ISC_R_NOTFOUND);
130 }
131
132 #ifdef ISC_PLATFORM_HAVEIPV6
133 #ifdef WANT_IPV6
134 static void
135 try_ipv6only(void) {
136 #ifdef IPV6_V6ONLY
137         SOCKET s;
138         int on;
139         char strbuf[ISC_STRERRORSIZE];
140 #endif
141         isc_result_t result;
142
143         result = isc_net_probeipv6();
144         if (result != ISC_R_SUCCESS) {
145                 ipv6only_result = result;
146                 return;
147         }
148
149 #ifndef IPV6_V6ONLY
150         ipv6only_result = ISC_R_NOTFOUND;
151         return;
152 #else
153         /* check for TCP sockets */
154         s = socket(PF_INET6, SOCK_STREAM, 0);
155         if (s == INVALID_SOCKET) {
156                 isc__strerror(errno, strbuf, sizeof(strbuf));
157                 UNEXPECTED_ERROR(__FILE__, __LINE__,
158                                  "socket() %s: %s",
159                                  isc_msgcat_get(isc_msgcat,
160                                                 ISC_MSGSET_GENERAL,
161                                                 ISC_MSG_FAILED,
162                                                 "failed"),
163                                  strbuf);
164                 ipv6only_result = ISC_R_UNEXPECTED;
165                 return;
166         }
167
168         on = 1;
169         if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) {
170                 ipv6only_result = ISC_R_NOTFOUND;
171                 goto close;
172         }
173
174         closesocket(s);
175
176         /* check for UDP sockets */
177         s = socket(PF_INET6, SOCK_DGRAM, 0);
178         if (s == INVALID_SOCKET) {
179                 isc__strerror(errno, strbuf, sizeof(strbuf));
180                 UNEXPECTED_ERROR(__FILE__, __LINE__,
181                                  "socket() %s: %s",
182                                  isc_msgcat_get(isc_msgcat,
183                                                 ISC_MSGSET_GENERAL,
184                                                 ISC_MSG_FAILED,
185                                                 "failed"),
186                                  strbuf);
187                 ipv6only_result = ISC_R_UNEXPECTED;
188                 return;
189         }
190
191         on = 1;
192         if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) {
193                 ipv6only_result = ISC_R_NOTFOUND;
194                 goto close;
195         }
196
197         ipv6only_result = ISC_R_SUCCESS;
198
199 close:
200         closesocket(s);
201         return;
202 #endif /* IPV6_V6ONLY */
203 }
204
205 static void
206 initialize_ipv6only(void) {
207         RUNTIME_CHECK(isc_once_do(&once_ipv6only,
208                                   try_ipv6only) == ISC_R_SUCCESS);
209 }
210
211 static void
212 try_ipv6pktinfo(void) {
213         int s, on;
214         char strbuf[ISC_STRERRORSIZE];
215         isc_result_t result;
216         int optname;
217
218         result = isc_net_probeipv6();
219         if (result != ISC_R_SUCCESS) {
220                 ipv6pktinfo_result = result;
221                 return;
222         }
223
224         /* we only use this for UDP sockets */
225         s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
226         if (s == INVALID_SOCKET) {
227                 isc__strerror(errno, strbuf, sizeof(strbuf));
228                 UNEXPECTED_ERROR(__FILE__, __LINE__,
229                                  "socket() %s: %s",
230                                  isc_msgcat_get(isc_msgcat,
231                                                 ISC_MSGSET_GENERAL,
232                                                 ISC_MSG_FAILED,
233                                                 "failed"),
234                                  strbuf);
235                 ipv6pktinfo_result = ISC_R_UNEXPECTED;
236                 return;
237         }
238
239 #ifdef IPV6_RECVPKTINFO
240         optname = IPV6_RECVPKTINFO;
241 #else
242         optname = IPV6_PKTINFO;
243 #endif
244         on = 1;
245         if (setsockopt(s, IPPROTO_IPV6, optname, (const char *) &on,
246                        sizeof(on)) < 0) {
247                 ipv6pktinfo_result = ISC_R_NOTFOUND;
248                 goto close;
249         }
250
251         ipv6pktinfo_result = ISC_R_SUCCESS;
252
253 close:
254         closesocket(s);
255         return;
256 }
257
258 static void
259 initialize_ipv6pktinfo(void) {
260         RUNTIME_CHECK(isc_once_do(&once_ipv6pktinfo,
261                                   try_ipv6pktinfo) == ISC_R_SUCCESS);
262 }
263 #endif /* WANT_IPV6 */
264 #endif /* ISC_PLATFORM_HAVEIPV6 */
265
266 isc_result_t
267 isc_net_probe_ipv6only(void) {
268 #ifdef ISC_PLATFORM_HAVEIPV6
269 #ifdef WANT_IPV6
270         initialize_ipv6only();
271 #else
272         ipv6only_result = ISC_R_NOTFOUND;
273 #endif
274 #endif
275         return (ipv6only_result);
276 }
277
278 isc_result_t
279 isc_net_probe_ipv6pktinfo(void) {
280 #ifdef ISC_PLATFORM_HAVEIPV6
281 #ifdef WANT_IPV6
282         initialize_ipv6pktinfo();
283 #else
284         ipv6pktinfo_result = ISC_R_NOTFOUND;
285 #endif
286 #endif
287         return (ipv6pktinfo_result);
288 }
289
290 isc_result_t
291 isc_net_getudpportrange(int af, in_port_t *low, in_port_t *high) {
292         int result = ISC_R_FAILURE;
293
294         REQUIRE(low != NULL && high != NULL);
295
296         UNUSED(af);
297
298         if (result != ISC_R_SUCCESS) {
299                 *low = ISC_NET_PORTRANGELOW;
300                 *high = ISC_NET_PORTRANGEHIGH;
301         }
302
303         return (ISC_R_SUCCESS); /* we currently never fail in this function */
304 }
305
306 void
307 isc_net_disableipv4(void) {
308         initialize();
309         if (ipv4_result == ISC_R_SUCCESS)
310                 ipv4_result = ISC_R_DISABLED;
311 }
312
313 void
314 isc_net_disableipv6(void) {
315         initialize();
316         if (ipv6_result == ISC_R_SUCCESS)
317                 ipv6_result = ISC_R_DISABLED;
318 }
319
320 void
321 isc_net_enableipv4(void) {
322         initialize();
323         if (ipv4_result == ISC_R_DISABLED)
324                 ipv4_result = ISC_R_SUCCESS;
325 }
326
327 void
328 isc_net_enableipv6(void) {
329         initialize();
330         if (ipv6_result == ISC_R_DISABLED)
331                 ipv6_result = ISC_R_SUCCESS;
332 }