net.c revision 258945
1/* 2 * Copyright (C) 2004, 2005, 2007, 2008 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.18 2008/08/08 05:06:49 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/*% 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) 51const struct in6_addr isc_net_in6addrany = IN6ADDR_ANY_INIT; 52#endif 53 54#if defined(ISC_PLATFORM_NEEDIN6ADDRLOOPBACK) 55const struct in6_addr isc_net_in6addrloop = IN6ADDR_LOOPBACK_INIT; 56#endif 57 58 59static isc_once_t once = ISC_ONCE_INIT; 60static isc_once_t once_ipv6only = ISC_ONCE_INIT; 61static isc_once_t once_ipv6pktinfo = ISC_ONCE_INIT; 62static isc_result_t ipv4_result = ISC_R_NOTFOUND; 63static isc_result_t ipv6_result = ISC_R_NOTFOUND; 64static isc_result_t ipv6only_result = ISC_R_NOTFOUND; 65static isc_result_t ipv6pktinfo_result = ISC_R_NOTFOUND; 66 67void InitSockets(void); 68 69static isc_result_t 70try_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 101static void 102initialize_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 114static void 115initialize(void) { 116 RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS); 117} 118 119isc_result_t 120isc_net_probeipv4(void) { 121 initialize(); 122 return (ipv4_result); 123} 124 125isc_result_t 126isc_net_probeipv6(void) { 127 initialize(); 128 return (ipv6_result); 129} 130 131isc_result_t 132isc_net_probeunix(void) { 133 return (ISC_R_NOTFOUND); 134} 135 136#ifdef ISC_PLATFORM_HAVEIPV6 137#ifdef WANT_IPV6 138static void 139try_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, (char *)&on, sizeof(on)) < 0) { 174 ipv6only_result = ISC_R_NOTFOUND; 175 goto close; 176 } 177 178 closesocket(s); 179 180 /* check for UDP sockets */ 181 s = socket(PF_INET6, SOCK_DGRAM, 0); 182 if (s == INVALID_SOCKET) { 183 isc__strerror(errno, strbuf, sizeof(strbuf)); 184 UNEXPECTED_ERROR(__FILE__, __LINE__, 185 "socket() %s: %s", 186 isc_msgcat_get(isc_msgcat, 187 ISC_MSGSET_GENERAL, 188 ISC_MSG_FAILED, 189 "failed"), 190 strbuf); 191 ipv6only_result = ISC_R_UNEXPECTED; 192 return; 193 } 194 195 on = 1; 196 if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&on, sizeof(on)) < 0) { 197 ipv6only_result = ISC_R_NOTFOUND; 198 goto close; 199 } 200 201 ipv6only_result = ISC_R_SUCCESS; 202 203close: 204 closesocket(s); 205 return; 206#endif /* IPV6_V6ONLY */ 207} 208 209static void 210initialize_ipv6only(void) { 211 RUNTIME_CHECK(isc_once_do(&once_ipv6only, 212 try_ipv6only) == ISC_R_SUCCESS); 213} 214 215static void 216try_ipv6pktinfo(void) { 217 int s, 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 257close: 258 closesocket(s); 259 return; 260} 261 262static void 263initialize_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 270isc_result_t 271isc_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 282isc_result_t 283isc_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 294isc_result_t 295isc_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 310void 311isc_net_disableipv4(void) { 312 initialize(); 313 if (ipv4_result == ISC_R_SUCCESS) 314 ipv4_result = ISC_R_DISABLED; 315} 316 317void 318isc_net_disableipv6(void) { 319 initialize(); 320 if (ipv6_result == ISC_R_SUCCESS) 321 ipv6_result = ISC_R_DISABLED; 322} 323 324void 325isc_net_enableipv4(void) { 326 initialize(); 327 if (ipv4_result == ISC_R_DISABLED) 328 ipv4_result = ISC_R_SUCCESS; 329} 330 331void 332isc_net_enableipv6(void) { 333 initialize(); 334 if (ipv6_result == ISC_R_DISABLED) 335 ipv6_result = ISC_R_SUCCESS; 336} 337