1258945Sroberto/* 2280849Scy * Copyright (C) 2004, 2005, 2007-2009, 2011, 2012 Internet Systems Consortium, Inc. ("ISC") 3258945Sroberto * Copyright (C) 1999-2003 Internet Software Consortium. 4258945Sroberto * 5258945Sroberto * Permission to use, copy, modify, and/or distribute this software for any 6258945Sroberto * purpose with or without fee is hereby granted, provided that the above 7258945Sroberto * copyright notice and this permission notice appear in all copies. 8258945Sroberto * 9258945Sroberto * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10258945Sroberto * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11258945Sroberto * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12258945Sroberto * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13258945Sroberto * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14258945Sroberto * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15258945Sroberto * PERFORMANCE OF THIS SOFTWARE. 16258945Sroberto */ 17258945Sroberto 18280849Scy/* $Id$ */ 19258945Sroberto 20258945Sroberto#include <config.h> 21258945Sroberto 22258945Sroberto#include <errno.h> 23258945Sroberto#include <unistd.h> 24258945Sroberto 25258945Sroberto#include <isc/log.h> 26258945Sroberto#include <isc/msgs.h> 27258945Sroberto#include <isc/net.h> 28258945Sroberto#include <isc/once.h> 29258945Sroberto#include <isc/strerror.h> 30258945Sroberto#include <isc/string.h> 31258945Sroberto#include <isc/util.h> 32258945Sroberto 33258945Sroberto/*% 34258945Sroberto * Definitions about UDP port range specification. This is a total mess of 35258945Sroberto * portability variants: some use sysctl (but the sysctl names vary), some use 36258945Sroberto * system-specific interfaces, some have the same interface for IPv4 and IPv6, 37258945Sroberto * some separate them, etc... 38258945Sroberto */ 39258945Sroberto 40258945Sroberto/*% 41258945Sroberto * The last resort defaults: use all non well known port space 42258945Sroberto */ 43258945Sroberto#ifndef ISC_NET_PORTRANGELOW 44258945Sroberto#define ISC_NET_PORTRANGELOW 1024 45258945Sroberto#endif /* ISC_NET_PORTRANGELOW */ 46258945Sroberto#ifndef ISC_NET_PORTRANGEHIGH 47258945Sroberto#define ISC_NET_PORTRANGEHIGH 65535 48258945Sroberto#endif /* ISC_NET_PORTRANGEHIGH */ 49258945Sroberto 50258945Sroberto#if defined(ISC_PLATFORM_NEEDIN6ADDRANY) 51258945Srobertoconst struct in6_addr isc_net_in6addrany = IN6ADDR_ANY_INIT; 52258945Sroberto#endif 53258945Sroberto 54258945Sroberto#if defined(ISC_PLATFORM_NEEDIN6ADDRLOOPBACK) 55258945Srobertoconst struct in6_addr isc_net_in6addrloop = IN6ADDR_LOOPBACK_INIT; 56258945Sroberto#endif 57258945Sroberto 58258945Sroberto 59258945Srobertostatic isc_once_t once = ISC_ONCE_INIT; 60258945Srobertostatic isc_once_t once_ipv6only = ISC_ONCE_INIT; 61258945Srobertostatic isc_once_t once_ipv6pktinfo = ISC_ONCE_INIT; 62258945Srobertostatic isc_result_t ipv4_result = ISC_R_NOTFOUND; 63258945Srobertostatic isc_result_t ipv6_result = ISC_R_NOTFOUND; 64258945Srobertostatic isc_result_t ipv6only_result = ISC_R_NOTFOUND; 65258945Srobertostatic isc_result_t ipv6pktinfo_result = ISC_R_NOTFOUND; 66258945Sroberto 67258945Srobertovoid InitSockets(void); 68258945Sroberto 69258945Srobertostatic isc_result_t 70258945Srobertotry_proto(int domain) { 71258945Sroberto SOCKET s; 72258945Sroberto char strbuf[ISC_STRERRORSIZE]; 73258945Sroberto int errval; 74258945Sroberto 75258945Sroberto s = socket(domain, SOCK_STREAM, IPPROTO_TCP); 76258945Sroberto if (s == INVALID_SOCKET) { 77258945Sroberto errval = WSAGetLastError(); 78258945Sroberto switch (errval) { 79258945Sroberto case WSAEAFNOSUPPORT: 80258945Sroberto case WSAEPROTONOSUPPORT: 81258945Sroberto case WSAEINVAL: 82258945Sroberto return (ISC_R_NOTFOUND); 83258945Sroberto default: 84258945Sroberto isc__strerror(errval, strbuf, sizeof(strbuf)); 85258945Sroberto UNEXPECTED_ERROR(__FILE__, __LINE__, 86258945Sroberto "socket() %s: %s", 87258945Sroberto isc_msgcat_get(isc_msgcat, 88258945Sroberto ISC_MSGSET_GENERAL, 89258945Sroberto ISC_MSG_FAILED, 90258945Sroberto "failed"), 91258945Sroberto strbuf); 92258945Sroberto return (ISC_R_UNEXPECTED); 93258945Sroberto } 94258945Sroberto } 95258945Sroberto 96258945Sroberto closesocket(s); 97258945Sroberto 98258945Sroberto return (ISC_R_SUCCESS); 99258945Sroberto} 100258945Sroberto 101258945Srobertostatic void 102258945Srobertoinitialize_action(void) { 103258945Sroberto InitSockets(); 104258945Sroberto ipv4_result = try_proto(PF_INET); 105258945Sroberto#ifdef ISC_PLATFORM_HAVEIPV6 106258945Sroberto#ifdef WANT_IPV6 107258945Sroberto#ifdef ISC_PLATFORM_HAVEIN6PKTINFO 108258945Sroberto ipv6_result = try_proto(PF_INET6); 109258945Sroberto#endif 110258945Sroberto#endif 111258945Sroberto#endif 112258945Sroberto} 113258945Sroberto 114258945Srobertostatic void 115258945Srobertoinitialize(void) { 116258945Sroberto RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS); 117258945Sroberto} 118258945Sroberto 119258945Srobertoisc_result_t 120258945Srobertoisc_net_probeipv4(void) { 121258945Sroberto initialize(); 122258945Sroberto return (ipv4_result); 123258945Sroberto} 124258945Sroberto 125258945Srobertoisc_result_t 126258945Srobertoisc_net_probeipv6(void) { 127258945Sroberto initialize(); 128258945Sroberto return (ipv6_result); 129258945Sroberto} 130258945Sroberto 131258945Srobertoisc_result_t 132258945Srobertoisc_net_probeunix(void) { 133258945Sroberto return (ISC_R_NOTFOUND); 134258945Sroberto} 135258945Sroberto 136258945Sroberto#ifdef ISC_PLATFORM_HAVEIPV6 137258945Sroberto#ifdef WANT_IPV6 138258945Srobertostatic void 139258945Srobertotry_ipv6only(void) { 140258945Sroberto#ifdef IPV6_V6ONLY 141258945Sroberto SOCKET s; 142258945Sroberto int on; 143258945Sroberto char strbuf[ISC_STRERRORSIZE]; 144258945Sroberto#endif 145258945Sroberto isc_result_t result; 146258945Sroberto 147258945Sroberto result = isc_net_probeipv6(); 148258945Sroberto if (result != ISC_R_SUCCESS) { 149258945Sroberto ipv6only_result = result; 150258945Sroberto return; 151258945Sroberto } 152258945Sroberto 153258945Sroberto#ifndef IPV6_V6ONLY 154258945Sroberto ipv6only_result = ISC_R_NOTFOUND; 155258945Sroberto return; 156258945Sroberto#else 157258945Sroberto /* check for TCP sockets */ 158258945Sroberto s = socket(PF_INET6, SOCK_STREAM, 0); 159258945Sroberto if (s == INVALID_SOCKET) { 160258945Sroberto isc__strerror(errno, strbuf, sizeof(strbuf)); 161258945Sroberto UNEXPECTED_ERROR(__FILE__, __LINE__, 162258945Sroberto "socket() %s: %s", 163258945Sroberto isc_msgcat_get(isc_msgcat, 164258945Sroberto ISC_MSGSET_GENERAL, 165258945Sroberto ISC_MSG_FAILED, 166258945Sroberto "failed"), 167258945Sroberto strbuf); 168258945Sroberto ipv6only_result = ISC_R_UNEXPECTED; 169258945Sroberto return; 170258945Sroberto } 171258945Sroberto 172258945Sroberto on = 1; 173280849Scy if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, (const char *)&on, 174280849Scy sizeof(on)) < 0) { 175258945Sroberto ipv6only_result = ISC_R_NOTFOUND; 176258945Sroberto goto close; 177258945Sroberto } 178258945Sroberto 179258945Sroberto closesocket(s); 180258945Sroberto 181258945Sroberto /* check for UDP sockets */ 182258945Sroberto s = socket(PF_INET6, SOCK_DGRAM, 0); 183258945Sroberto if (s == INVALID_SOCKET) { 184258945Sroberto isc__strerror(errno, strbuf, sizeof(strbuf)); 185258945Sroberto UNEXPECTED_ERROR(__FILE__, __LINE__, 186258945Sroberto "socket() %s: %s", 187258945Sroberto isc_msgcat_get(isc_msgcat, 188258945Sroberto ISC_MSGSET_GENERAL, 189258945Sroberto ISC_MSG_FAILED, 190258945Sroberto "failed"), 191258945Sroberto strbuf); 192258945Sroberto ipv6only_result = ISC_R_UNEXPECTED; 193258945Sroberto return; 194258945Sroberto } 195258945Sroberto 196258945Sroberto on = 1; 197280849Scy if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, (const char *)&on, 198280849Scy sizeof(on)) < 0) { 199258945Sroberto ipv6only_result = ISC_R_NOTFOUND; 200258945Sroberto goto close; 201258945Sroberto } 202258945Sroberto 203258945Sroberto ipv6only_result = ISC_R_SUCCESS; 204258945Sroberto 205258945Srobertoclose: 206258945Sroberto closesocket(s); 207258945Sroberto return; 208258945Sroberto#endif /* IPV6_V6ONLY */ 209258945Sroberto} 210258945Sroberto 211258945Srobertostatic void 212258945Srobertoinitialize_ipv6only(void) { 213258945Sroberto RUNTIME_CHECK(isc_once_do(&once_ipv6only, 214258945Sroberto try_ipv6only) == ISC_R_SUCCESS); 215258945Sroberto} 216258945Sroberto 217258945Srobertostatic void 218258945Srobertotry_ipv6pktinfo(void) { 219293650Sglebius SOCKET s; 220293650Sglebius int on; 221258945Sroberto char strbuf[ISC_STRERRORSIZE]; 222258945Sroberto isc_result_t result; 223258945Sroberto int optname; 224258945Sroberto 225258945Sroberto result = isc_net_probeipv6(); 226258945Sroberto if (result != ISC_R_SUCCESS) { 227258945Sroberto ipv6pktinfo_result = result; 228258945Sroberto return; 229258945Sroberto } 230258945Sroberto 231258945Sroberto /* we only use this for UDP sockets */ 232258945Sroberto s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP); 233258945Sroberto if (s == INVALID_SOCKET) { 234258945Sroberto isc__strerror(errno, strbuf, sizeof(strbuf)); 235258945Sroberto UNEXPECTED_ERROR(__FILE__, __LINE__, 236258945Sroberto "socket() %s: %s", 237258945Sroberto isc_msgcat_get(isc_msgcat, 238258945Sroberto ISC_MSGSET_GENERAL, 239258945Sroberto ISC_MSG_FAILED, 240258945Sroberto "failed"), 241258945Sroberto strbuf); 242258945Sroberto ipv6pktinfo_result = ISC_R_UNEXPECTED; 243258945Sroberto return; 244258945Sroberto } 245258945Sroberto 246258945Sroberto#ifdef IPV6_RECVPKTINFO 247258945Sroberto optname = IPV6_RECVPKTINFO; 248258945Sroberto#else 249258945Sroberto optname = IPV6_PKTINFO; 250258945Sroberto#endif 251258945Sroberto on = 1; 252258945Sroberto if (setsockopt(s, IPPROTO_IPV6, optname, (const char *) &on, 253258945Sroberto sizeof(on)) < 0) { 254258945Sroberto ipv6pktinfo_result = ISC_R_NOTFOUND; 255258945Sroberto goto close; 256258945Sroberto } 257258945Sroberto 258258945Sroberto ipv6pktinfo_result = ISC_R_SUCCESS; 259258945Sroberto 260258945Srobertoclose: 261258945Sroberto closesocket(s); 262258945Sroberto return; 263258945Sroberto} 264258945Sroberto 265258945Srobertostatic void 266258945Srobertoinitialize_ipv6pktinfo(void) { 267258945Sroberto RUNTIME_CHECK(isc_once_do(&once_ipv6pktinfo, 268258945Sroberto try_ipv6pktinfo) == ISC_R_SUCCESS); 269258945Sroberto} 270258945Sroberto#endif /* WANT_IPV6 */ 271258945Sroberto#endif /* ISC_PLATFORM_HAVEIPV6 */ 272258945Sroberto 273258945Srobertoisc_result_t 274258945Srobertoisc_net_probe_ipv6only(void) { 275258945Sroberto#ifdef ISC_PLATFORM_HAVEIPV6 276258945Sroberto#ifdef WANT_IPV6 277258945Sroberto initialize_ipv6only(); 278258945Sroberto#else 279258945Sroberto ipv6only_result = ISC_R_NOTFOUND; 280258945Sroberto#endif 281258945Sroberto#endif 282258945Sroberto return (ipv6only_result); 283258945Sroberto} 284258945Sroberto 285258945Srobertoisc_result_t 286258945Srobertoisc_net_probe_ipv6pktinfo(void) { 287258945Sroberto#ifdef ISC_PLATFORM_HAVEIPV6 288258945Sroberto#ifdef WANT_IPV6 289258945Sroberto initialize_ipv6pktinfo(); 290258945Sroberto#else 291258945Sroberto ipv6pktinfo_result = ISC_R_NOTFOUND; 292258945Sroberto#endif 293258945Sroberto#endif 294258945Sroberto return (ipv6pktinfo_result); 295258945Sroberto} 296258945Sroberto 297258945Srobertoisc_result_t 298258945Srobertoisc_net_getudpportrange(int af, in_port_t *low, in_port_t *high) { 299258945Sroberto int result = ISC_R_FAILURE; 300258945Sroberto 301258945Sroberto REQUIRE(low != NULL && high != NULL); 302258945Sroberto 303258945Sroberto UNUSED(af); 304258945Sroberto 305258945Sroberto if (result != ISC_R_SUCCESS) { 306258945Sroberto *low = ISC_NET_PORTRANGELOW; 307258945Sroberto *high = ISC_NET_PORTRANGEHIGH; 308258945Sroberto } 309258945Sroberto 310258945Sroberto return (ISC_R_SUCCESS); /* we currently never fail in this function */ 311258945Sroberto} 312258945Sroberto 313258945Srobertovoid 314258945Srobertoisc_net_disableipv4(void) { 315258945Sroberto initialize(); 316258945Sroberto if (ipv4_result == ISC_R_SUCCESS) 317258945Sroberto ipv4_result = ISC_R_DISABLED; 318258945Sroberto} 319258945Sroberto 320258945Srobertovoid 321258945Srobertoisc_net_disableipv6(void) { 322258945Sroberto initialize(); 323258945Sroberto if (ipv6_result == ISC_R_SUCCESS) 324258945Sroberto ipv6_result = ISC_R_DISABLED; 325258945Sroberto} 326258945Sroberto 327258945Srobertovoid 328258945Srobertoisc_net_enableipv4(void) { 329258945Sroberto initialize(); 330258945Sroberto if (ipv4_result == ISC_R_DISABLED) 331258945Sroberto ipv4_result = ISC_R_SUCCESS; 332258945Sroberto} 333258945Sroberto 334258945Srobertovoid 335258945Srobertoisc_net_enableipv6(void) { 336258945Sroberto initialize(); 337258945Sroberto if (ipv6_result == ISC_R_DISABLED) 338258945Sroberto ipv6_result = ISC_R_SUCCESS; 339258945Sroberto} 340