Import of bind-9.3.2-P1
[dragonfly.git] / contrib / bind-9.3 / lib / isc / unix / net.c
1 /*
2  * Copyright (C) 2004, 2005  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.9 2005/03/17 03:58:33 marka 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 #if defined(ISC_PLATFORM_HAVEIPV6) && defined(ISC_PLATFORM_NEEDIN6ADDRANY)
34 const struct in6_addr isc_net_in6addrany = IN6ADDR_ANY_INIT;
35 #endif
36
37 #if defined(ISC_PLATFORM_HAVEIPV6) && defined(ISC_PLATFORM_NEEDIN6ADDRLOOPBACK)
38 const struct in6_addr isc_net_in6addrloop = IN6ADDR_LOOPBACK_INIT;
39 #endif
40
41 static isc_once_t       once = ISC_ONCE_INIT;
42 static isc_once_t       once_ipv6only = ISC_ONCE_INIT;
43 static isc_once_t       once_ipv6pktinfo = ISC_ONCE_INIT;
44 static isc_result_t     ipv4_result = ISC_R_NOTFOUND;
45 static isc_result_t     ipv6_result = ISC_R_NOTFOUND;
46 static isc_result_t     ipv6only_result = ISC_R_NOTFOUND;
47 static isc_result_t     ipv6pktinfo_result = ISC_R_NOTFOUND;
48
49 static isc_result_t
50 try_proto(int domain) {
51         int s;
52         isc_result_t result = ISC_R_SUCCESS;
53         char strbuf[ISC_STRERRORSIZE];
54
55         s = socket(domain, SOCK_STREAM, 0);
56         if (s == -1) {
57                 switch (errno) {
58 #ifdef EAFNOSUPPORT
59                 case EAFNOSUPPORT:
60 #endif
61 #ifdef EPROTONOSUPPORT
62                 case EPROTONOSUPPORT:
63 #endif
64 #ifdef EINVAL
65                 case EINVAL:
66 #endif
67                         return (ISC_R_NOTFOUND);
68                 default:
69                         isc__strerror(errno, strbuf, sizeof(strbuf));
70                         UNEXPECTED_ERROR(__FILE__, __LINE__,
71                                          "socket() %s: %s",
72                                          isc_msgcat_get(isc_msgcat,
73                                                         ISC_MSGSET_GENERAL,
74                                                         ISC_MSG_FAILED,
75                                                         "failed"),
76                                          strbuf);
77                         return (ISC_R_UNEXPECTED);
78                 }
79         }
80
81 #ifdef ISC_PLATFORM_HAVEIPV6
82 #ifdef WANT_IPV6
83 #ifdef ISC_PLATFORM_HAVEIN6PKTINFO
84         if (domain == PF_INET6) {
85                 struct sockaddr_in6 sin6;
86                 unsigned int len;
87
88                 /*
89                  * Check to see if IPv6 is broken, as is common on Linux.
90                  */
91                 len = sizeof(sin6);
92                 if (getsockname(s, (struct sockaddr *)&sin6, (void *)&len) < 0)
93                 {
94                         isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
95                                       ISC_LOGMODULE_SOCKET, ISC_LOG_ERROR,
96                                       "retrieving the address of an IPv6 "
97                                       "socket from the kernel failed.");
98                         isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
99                                       ISC_LOGMODULE_SOCKET, ISC_LOG_ERROR,
100                                       "IPv6 is not supported.");
101                         result = ISC_R_NOTFOUND;
102                 } else {
103                         if (len == sizeof(struct sockaddr_in6))
104                                 result = ISC_R_SUCCESS;
105                         else {
106                                 isc_log_write(isc_lctx,
107                                               ISC_LOGCATEGORY_GENERAL,
108                                               ISC_LOGMODULE_SOCKET,
109                                               ISC_LOG_ERROR,
110                                               "IPv6 structures in kernel and "
111                                               "user space do not match.");
112                                 isc_log_write(isc_lctx,
113                                               ISC_LOGCATEGORY_GENERAL,
114                                               ISC_LOGMODULE_SOCKET,
115                                               ISC_LOG_ERROR,
116                                               "IPv6 is not supported.");
117                                 result = ISC_R_NOTFOUND;
118                         }
119                 }
120         }
121 #endif
122 #endif
123 #endif
124
125         (void)close(s);
126
127         return (result);
128 }
129
130 static void
131 initialize_action(void) {
132         ipv4_result = try_proto(PF_INET);
133 #ifdef ISC_PLATFORM_HAVEIPV6
134 #ifdef WANT_IPV6
135 #ifdef ISC_PLATFORM_HAVEIN6PKTINFO
136         ipv6_result = try_proto(PF_INET6);
137 #endif
138 #endif
139 #endif
140 }
141
142 static void
143 initialize(void) {
144         RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS);
145 }
146
147 isc_result_t
148 isc_net_probeipv4(void) {
149         initialize();
150         return (ipv4_result);
151 }
152
153 isc_result_t
154 isc_net_probeipv6(void) {
155         initialize();
156         return (ipv6_result);
157 }
158
159 #ifdef ISC_PLATFORM_HAVEIPV6
160 #ifdef WANT_IPV6
161 static void
162 try_ipv6only(void) {
163 #ifdef IPV6_V6ONLY
164         int s, on;
165         char strbuf[ISC_STRERRORSIZE];
166 #endif
167         isc_result_t result;
168
169         result = isc_net_probeipv6();
170         if (result != ISC_R_SUCCESS) {
171                 ipv6only_result = result;
172                 return;
173         }
174
175 #ifndef IPV6_V6ONLY
176         ipv6only_result = ISC_R_NOTFOUND;
177         return;
178 #else
179         /* check for TCP sockets */
180         s = socket(PF_INET6, SOCK_STREAM, 0);
181         if (s == -1) {
182                 isc__strerror(errno, strbuf, sizeof(strbuf));
183                 UNEXPECTED_ERROR(__FILE__, __LINE__,
184                                  "socket() %s: %s",
185                                  isc_msgcat_get(isc_msgcat,
186                                                 ISC_MSGSET_GENERAL,
187                                                 ISC_MSG_FAILED,
188                                                 "failed"),
189                                  strbuf);
190                 ipv6only_result = ISC_R_UNEXPECTED;
191                 return;
192         }
193
194         on = 1;
195         if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) {
196                 ipv6only_result = ISC_R_NOTFOUND;
197                 goto close;
198         }
199
200         close(s);
201
202         /* check for UDP sockets */
203         s = socket(PF_INET6, SOCK_DGRAM, 0);
204         if (s == -1) {
205                 isc__strerror(errno, strbuf, sizeof(strbuf));
206                 UNEXPECTED_ERROR(__FILE__, __LINE__,
207                                  "socket() %s: %s",
208                                  isc_msgcat_get(isc_msgcat,
209                                                 ISC_MSGSET_GENERAL,
210                                                 ISC_MSG_FAILED,
211                                                 "failed"),
212                                  strbuf);
213                 ipv6only_result = ISC_R_UNEXPECTED;
214                 return;
215         }
216
217         on = 1;
218         if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) {
219                 ipv6only_result = ISC_R_NOTFOUND;
220                 goto close;
221         }
222
223         close(s);
224
225         ipv6only_result = ISC_R_SUCCESS;
226
227 close:
228         close(s);
229         return;
230 #endif /* IPV6_V6ONLY */
231 }
232
233 static void
234 initialize_ipv6only(void) {
235         RUNTIME_CHECK(isc_once_do(&once_ipv6only,
236                                   try_ipv6only) == ISC_R_SUCCESS);
237 }
238 #endif /* IPV6_V6ONLY */
239
240 #ifdef ISC_PLATFORM_HAVEIN6PKTINFO
241 static void
242 try_ipv6pktinfo(void) {
243         int s, on;
244         char strbuf[ISC_STRERRORSIZE];
245         isc_result_t result;
246         int optname;
247
248         result = isc_net_probeipv6();
249         if (result != ISC_R_SUCCESS) {
250                 ipv6pktinfo_result = result;
251                 return;
252         }
253
254         /* we only use this for UDP sockets */
255         s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
256         if (s == -1) {
257                 isc__strerror(errno, strbuf, sizeof(strbuf));
258                 UNEXPECTED_ERROR(__FILE__, __LINE__,
259                                  "socket() %s: %s",
260                                  isc_msgcat_get(isc_msgcat,
261                                                 ISC_MSGSET_GENERAL,
262                                                 ISC_MSG_FAILED,
263                                                 "failed"),
264                                  strbuf);
265                 ipv6pktinfo_result = ISC_R_UNEXPECTED;
266                 return;
267         }
268
269 #ifdef IPV6_RECVPKTINFO
270         optname = IPV6_RECVPKTINFO;
271 #else
272         optname = IPV6_PKTINFO;
273 #endif
274         on = 1;
275         if (setsockopt(s, IPPROTO_IPV6, optname, &on, sizeof(on)) < 0) {
276                 ipv6pktinfo_result = ISC_R_NOTFOUND;
277                 goto close;
278         }
279
280         close(s);
281         ipv6pktinfo_result = ISC_R_SUCCESS;
282
283 close:
284         close(s);
285         return;
286 }
287
288 static void
289 initialize_ipv6pktinfo(void) {
290         RUNTIME_CHECK(isc_once_do(&once_ipv6pktinfo,
291                                   try_ipv6pktinfo) == ISC_R_SUCCESS);
292 }
293 #endif /* ISC_PLATFORM_HAVEIN6PKTINFO */
294 #endif /* WANT_IPV6 */
295
296 isc_result_t
297 isc_net_probe_ipv6only(void) {
298 #ifdef ISC_PLATFORM_HAVEIPV6
299 #ifdef WANT_IPV6
300         initialize_ipv6only();
301 #else
302         ipv6only_result = ISC_R_NOTFOUND;
303 #endif
304 #endif
305         return (ipv6only_result);
306 }
307
308 isc_result_t
309 isc_net_probe_ipv6pktinfo(void) {
310 #ifdef ISC_PLATFORM_HAVEIPV6
311 #ifdef ISC_PLATFORM_HAVEIN6PKTINFO
312 #ifdef WANT_IPV6
313         initialize_ipv6pktinfo();
314 #else
315         ipv6pktinfo_result = ISC_R_NOTFOUND;
316 #endif
317 #endif
318 #endif
319         return (ipv6pktinfo_result);
320 }
321
322 void
323 isc_net_disableipv4(void) {
324         initialize();
325         if (ipv4_result == ISC_R_SUCCESS)
326                 ipv4_result = ISC_R_DISABLED;
327 }
328
329 void
330 isc_net_disableipv6(void) {
331         initialize();
332         if (ipv6_result == ISC_R_SUCCESS)
333                 ipv6_result = ISC_R_DISABLED;
334 }
335
336 void
337 isc_net_enableipv4(void) {
338         initialize();
339         if (ipv4_result == ISC_R_DISABLED)
340                 ipv4_result = ISC_R_SUCCESS;
341 }
342
343 void
344 isc_net_enableipv6(void) {
345         initialize();
346         if (ipv6_result == ISC_R_DISABLED)
347                 ipv6_result = ISC_R_SUCCESS;
348 }