1 /* 2 * Copyright (C) 2004, 2005, 2007-2009, 2011, 2012 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$ */ 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_NEEDIN6ADDRANY) 51 const struct in6_addr isc_net_in6addrany = IN6ADDR_ANY_INIT; 52 #endif 53 54 #if defined(ISC_PLATFORM_NEEDIN6ADDRLOOPBACK) 55 const struct in6_addr isc_net_in6addrloop = IN6ADDR_LOOPBACK_INIT; 56 #endif 57 58 59 static isc_once_t once = ISC_ONCE_INIT; 60 static isc_once_t once_ipv6only = ISC_ONCE_INIT; 61 static isc_once_t once_ipv6pktinfo = ISC_ONCE_INIT; 62 static isc_result_t ipv4_result = ISC_R_NOTFOUND; 63 static isc_result_t ipv6_result = ISC_R_NOTFOUND; 64 static isc_result_t ipv6only_result = ISC_R_NOTFOUND; 65 static isc_result_t ipv6pktinfo_result = ISC_R_NOTFOUND; 66 67 void InitSockets(void); 68 69 static isc_result_t 70 try_proto(int domain) { 71 SOCKET s; 72 char strbuf[ISC_STRERRORSIZE]; 73 int errval; 74 75 s = socket(domain, SOCK_STREAM, IPPROTO_TCP); 76 if (s == INVALID_SOCKET) { 77 errval = WSAGetLastError(); 78 switch (errval) { 79 case WSAEAFNOSUPPORT: 80 case WSAEPROTONOSUPPORT: 81 case WSAEINVAL: 82 return (ISC_R_NOTFOUND); 83 default: 84 isc__strerror(errval, strbuf, sizeof(strbuf)); 85 UNEXPECTED_ERROR(__FILE__, __LINE__, 86 "socket() %s: %s", 87 isc_msgcat_get(isc_msgcat, 88 ISC_MSGSET_GENERAL, 89 ISC_MSG_FAILED, 90 "failed"), 91 strbuf); 92 return (ISC_R_UNEXPECTED); 93 } 94 } 95 96 closesocket(s); 97 98 return (ISC_R_SUCCESS); 99 } 100 101 static void 102 initialize_action(void) { 103 InitSockets(); 104 ipv4_result = try_proto(PF_INET); 105 #ifdef ISC_PLATFORM_HAVEIPV6 106 #ifdef WANT_IPV6 107 #ifdef ISC_PLATFORM_HAVEIN6PKTINFO 108 ipv6_result = try_proto(PF_INET6); 109 #endif 110 #endif 111 #endif 112 } 113 114 static void 115 initialize(void) { 116 RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS); 117 } 118 119 isc_result_t 120 isc_net_probeipv4(void) { 121 initialize(); 122 return (ipv4_result); 123 } 124 125 isc_result_t 126 isc_net_probeipv6(void) { 127 initialize(); 128 return (ipv6_result); 129 } 130 131 isc_result_t 132 isc_net_probeunix(void) { 133 return (ISC_R_NOTFOUND); 134 } 135 136 #ifdef ISC_PLATFORM_HAVEIPV6 137 #ifdef WANT_IPV6 138 static void 139 try_ipv6only(void) { 140 #ifdef IPV6_V6ONLY 141 SOCKET s; 142 int on; 143 char strbuf[ISC_STRERRORSIZE]; 144 #endif 145 isc_result_t result; 146 147 result = isc_net_probeipv6(); 148 if (result != ISC_R_SUCCESS) { 149 ipv6only_result = result; 150 return; 151 } 152 153 #ifndef IPV6_V6ONLY 154 ipv6only_result = ISC_R_NOTFOUND; 155 return; 156 #else 157 /* check for TCP sockets */ 158 s = socket(PF_INET6, SOCK_STREAM, 0); 159 if (s == INVALID_SOCKET) { 160 isc__strerror(errno, strbuf, sizeof(strbuf)); 161 UNEXPECTED_ERROR(__FILE__, __LINE__, 162 "socket() %s: %s", 163 isc_msgcat_get(isc_msgcat, 164 ISC_MSGSET_GENERAL, 165 ISC_MSG_FAILED, 166 "failed"), 167 strbuf); 168 ipv6only_result = ISC_R_UNEXPECTED; 169 return; 170 } 171 172 on = 1; 173 if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, (const char *)&on, 174 sizeof(on)) < 0) { 175 ipv6only_result = ISC_R_NOTFOUND; 176 goto close; 177 } 178 179 closesocket(s); 180 181 /* check for UDP sockets */ 182 s = socket(PF_INET6, SOCK_DGRAM, 0); 183 if (s == INVALID_SOCKET) { 184 isc__strerror(errno, strbuf, sizeof(strbuf)); 185 UNEXPECTED_ERROR(__FILE__, __LINE__, 186 "socket() %s: %s", 187 isc_msgcat_get(isc_msgcat, 188 ISC_MSGSET_GENERAL, 189 ISC_MSG_FAILED, 190 "failed"), 191 strbuf); 192 ipv6only_result = ISC_R_UNEXPECTED; 193 return; 194 } 195 196 on = 1; 197 if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, (const char *)&on, 198 sizeof(on)) < 0) { 199 ipv6only_result = ISC_R_NOTFOUND; 200 goto close; 201 } 202 203 ipv6only_result = ISC_R_SUCCESS; 204 205 close: 206 closesocket(s); 207 return; 208 #endif /* IPV6_V6ONLY */ 209 } 210 211 static void 212 initialize_ipv6only(void) { 213 RUNTIME_CHECK(isc_once_do(&once_ipv6only, 214 try_ipv6only) == ISC_R_SUCCESS); 215 } 216 217 static void 218 try_ipv6pktinfo(void) { 219 SOCKET s; 220 int 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