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