1 /* $NetBSD: net.c,v 1.1.1.1 2009/12/13 16:54:41 kardel Exp $ */ 2 3 /* 4 * Copyright (C) 2004, 2005, 2007, 2008 Internet Systems Consortium, Inc. ("ISC") 5 * Copyright (C) 1999-2003 Internet Software Consortium. 6 * 7 * Permission to use, copy, modify, and/or distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 12 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 13 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 14 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 15 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 16 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 17 * PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 /* Id: net.c,v 1.18 2008/08/08 05:06:49 marka Exp */ 21 22 #include <config.h> 23 24 #include <errno.h> 25 #include <unistd.h> 26 27 #include <isc/log.h> 28 #include <isc/msgs.h> 29 #include <isc/net.h> 30 #include <isc/once.h> 31 #include <isc/strerror.h> 32 #include <isc/string.h> 33 #include <isc/util.h> 34 35 /*% 36 * Definitions about UDP port range specification. This is a total mess of 37 * portability variants: some use sysctl (but the sysctl names vary), some use 38 * system-specific interfaces, some have the same interface for IPv4 and IPv6, 39 * some separate them, etc... 40 */ 41 42 /*% 43 * The last resort defaults: use all non well known port space 44 */ 45 #ifndef ISC_NET_PORTRANGELOW 46 #define ISC_NET_PORTRANGELOW 1024 47 #endif /* ISC_NET_PORTRANGELOW */ 48 #ifndef ISC_NET_PORTRANGEHIGH 49 #define ISC_NET_PORTRANGEHIGH 65535 50 #endif /* ISC_NET_PORTRANGEHIGH */ 51 52 #if defined(ISC_PLATFORM_HAVEIPV6) && defined(ISC_PLATFORM_NEEDIN6ADDRANY) 53 const struct in6_addr isc_net_in6addrany = IN6ADDR_ANY_INIT; 54 #endif 55 56 #if defined(ISC_PLATFORM_HAVEIPV6) && defined(ISC_PLATFORM_NEEDIN6ADDRLOOPBACK) 57 const struct in6_addr isc_net_in6addrloop = IN6ADDR_LOOPBACK_INIT; 58 #endif 59 60 61 static isc_once_t once = ISC_ONCE_INIT; 62 static isc_once_t once_ipv6only = ISC_ONCE_INIT; 63 static isc_once_t once_ipv6pktinfo = ISC_ONCE_INIT; 64 static isc_result_t ipv4_result = ISC_R_NOTFOUND; 65 static isc_result_t ipv6_result = ISC_R_NOTFOUND; 66 static isc_result_t ipv6only_result = ISC_R_NOTFOUND; 67 static isc_result_t ipv6pktinfo_result = ISC_R_NOTFOUND; 68 69 void InitSockets(void); 70 71 static isc_result_t 72 try_proto(int domain) { 73 SOCKET s; 74 isc_result_t result = ISC_R_SUCCESS; 75 char strbuf[ISC_STRERRORSIZE]; 76 int errval; 77 78 s = socket(domain, SOCK_STREAM, IPPROTO_TCP); 79 if (s == INVALID_SOCKET) { 80 errval = WSAGetLastError(); 81 switch (errval) { 82 case WSAEAFNOSUPPORT: 83 case WSAEPROTONOSUPPORT: 84 case WSAEINVAL: 85 return (ISC_R_NOTFOUND); 86 default: 87 isc__strerror(errval, strbuf, sizeof(strbuf)); 88 UNEXPECTED_ERROR(__FILE__, __LINE__, 89 "socket() %s: %s", 90 isc_msgcat_get(isc_msgcat, 91 ISC_MSGSET_GENERAL, 92 ISC_MSG_FAILED, 93 "failed"), 94 strbuf); 95 return (ISC_R_UNEXPECTED); 96 } 97 } 98 99 closesocket(s); 100 101 return (ISC_R_SUCCESS); 102 } 103 104 static void 105 initialize_action(void) { 106 InitSockets(); 107 ipv4_result = try_proto(PF_INET); 108 #ifdef ISC_PLATFORM_HAVEIPV6 109 #ifdef WANT_IPV6 110 #ifdef ISC_PLATFORM_HAVEIN6PKTINFO 111 ipv6_result = try_proto(PF_INET6); 112 #endif 113 #endif 114 #endif 115 } 116 117 static void 118 initialize(void) { 119 RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS); 120 } 121 122 isc_result_t 123 isc_net_probeipv4(void) { 124 initialize(); 125 return (ipv4_result); 126 } 127 128 isc_result_t 129 isc_net_probeipv6(void) { 130 initialize(); 131 return (ipv6_result); 132 } 133 134 isc_result_t 135 isc_net_probeunix(void) { 136 return (ISC_R_NOTFOUND); 137 } 138 139 #ifdef ISC_PLATFORM_HAVEIPV6 140 #ifdef WANT_IPV6 141 static void 142 try_ipv6only(void) { 143 #ifdef IPV6_V6ONLY 144 SOCKET s; 145 int on; 146 char strbuf[ISC_STRERRORSIZE]; 147 #endif 148 isc_result_t result; 149 150 result = isc_net_probeipv6(); 151 if (result != ISC_R_SUCCESS) { 152 ipv6only_result = result; 153 return; 154 } 155 156 #ifndef IPV6_V6ONLY 157 ipv6only_result = ISC_R_NOTFOUND; 158 return; 159 #else 160 /* check for TCP sockets */ 161 s = socket(PF_INET6, SOCK_STREAM, 0); 162 if (s == INVALID_SOCKET) { 163 isc__strerror(errno, strbuf, sizeof(strbuf)); 164 UNEXPECTED_ERROR(__FILE__, __LINE__, 165 "socket() %s: %s", 166 isc_msgcat_get(isc_msgcat, 167 ISC_MSGSET_GENERAL, 168 ISC_MSG_FAILED, 169 "failed"), 170 strbuf); 171 ipv6only_result = ISC_R_UNEXPECTED; 172 return; 173 } 174 175 on = 1; 176 if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&on, sizeof(on)) < 0) { 177 ipv6only_result = ISC_R_NOTFOUND; 178 goto close; 179 } 180 181 closesocket(s); 182 183 /* check for UDP sockets */ 184 s = socket(PF_INET6, SOCK_DGRAM, 0); 185 if (s == INVALID_SOCKET) { 186 isc__strerror(errno, strbuf, sizeof(strbuf)); 187 UNEXPECTED_ERROR(__FILE__, __LINE__, 188 "socket() %s: %s", 189 isc_msgcat_get(isc_msgcat, 190 ISC_MSGSET_GENERAL, 191 ISC_MSG_FAILED, 192 "failed"), 193 strbuf); 194 ipv6only_result = ISC_R_UNEXPECTED; 195 return; 196 } 197 198 on = 1; 199 if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&on, sizeof(on)) < 0) { 200 ipv6only_result = ISC_R_NOTFOUND; 201 goto close; 202 } 203 204 ipv6only_result = ISC_R_SUCCESS; 205 206 close: 207 closesocket(s); 208 return; 209 #endif /* IPV6_V6ONLY */ 210 } 211 212 static void 213 initialize_ipv6only(void) { 214 RUNTIME_CHECK(isc_once_do(&once_ipv6only, 215 try_ipv6only) == ISC_R_SUCCESS); 216 } 217 218 static void 219 try_ipv6pktinfo(void) { 220 int s, on; 221 char strbuf[ISC_STRERRORSIZE]; 222 isc_result_t result; 223 int optname; 224 225 result = isc_net_probeipv6(); 226 if (result != ISC_R_SUCCESS) { 227 ipv6pktinfo_result = result; 228 return; 229 } 230 231 /* we only use this for UDP sockets */ 232 s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP); 233 if (s == INVALID_SOCKET) { 234 isc__strerror(errno, strbuf, sizeof(strbuf)); 235 UNEXPECTED_ERROR(__FILE__, __LINE__, 236 "socket() %s: %s", 237 isc_msgcat_get(isc_msgcat, 238 ISC_MSGSET_GENERAL, 239 ISC_MSG_FAILED, 240 "failed"), 241 strbuf); 242 ipv6pktinfo_result = ISC_R_UNEXPECTED; 243 return; 244 } 245 246 #ifdef IPV6_RECVPKTINFO 247 optname = IPV6_RECVPKTINFO; 248 #else 249 optname = IPV6_PKTINFO; 250 #endif 251 on = 1; 252 if (setsockopt(s, IPPROTO_IPV6, optname, (const char *) &on, 253 sizeof(on)) < 0) { 254 ipv6pktinfo_result = ISC_R_NOTFOUND; 255 goto close; 256 } 257 258 ipv6pktinfo_result = ISC_R_SUCCESS; 259 260 close: 261 closesocket(s); 262 return; 263 } 264 265 static void 266 initialize_ipv6pktinfo(void) { 267 RUNTIME_CHECK(isc_once_do(&once_ipv6pktinfo, 268 try_ipv6pktinfo) == ISC_R_SUCCESS); 269 } 270 #endif /* WANT_IPV6 */ 271 #endif /* ISC_PLATFORM_HAVEIPV6 */ 272 273 isc_result_t 274 isc_net_probe_ipv6only(void) { 275 #ifdef ISC_PLATFORM_HAVEIPV6 276 #ifdef WANT_IPV6 277 initialize_ipv6only(); 278 #else 279 ipv6only_result = ISC_R_NOTFOUND; 280 #endif 281 #endif 282 return (ipv6only_result); 283 } 284 285 isc_result_t 286 isc_net_probe_ipv6pktinfo(void) { 287 #ifdef ISC_PLATFORM_HAVEIPV6 288 #ifdef WANT_IPV6 289 initialize_ipv6pktinfo(); 290 #else 291 ipv6pktinfo_result = ISC_R_NOTFOUND; 292 #endif 293 #endif 294 return (ipv6pktinfo_result); 295 } 296 297 isc_result_t 298 isc_net_getudpportrange(int af, in_port_t *low, in_port_t *high) { 299 int result = ISC_R_FAILURE; 300 301 REQUIRE(low != NULL && high != NULL); 302 303 UNUSED(af); 304 305 if (result != ISC_R_SUCCESS) { 306 *low = ISC_NET_PORTRANGELOW; 307 *high = ISC_NET_PORTRANGEHIGH; 308 } 309 310 return (ISC_R_SUCCESS); /* we currently never fail in this function */ 311 } 312 313 void 314 isc_net_disableipv4(void) { 315 initialize(); 316 if (ipv4_result == ISC_R_SUCCESS) 317 ipv4_result = ISC_R_DISABLED; 318 } 319 320 void 321 isc_net_disableipv6(void) { 322 initialize(); 323 if (ipv6_result == ISC_R_SUCCESS) 324 ipv6_result = ISC_R_DISABLED; 325 } 326 327 void 328 isc_net_enableipv4(void) { 329 initialize(); 330 if (ipv4_result == ISC_R_DISABLED) 331 ipv4_result = ISC_R_SUCCESS; 332 } 333 334 void 335 isc_net_enableipv6(void) { 336 initialize(); 337 if (ipv6_result == ISC_R_DISABLED) 338 ipv6_result = ISC_R_SUCCESS; 339 } 340