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