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