/contrib/ntp/libisc/net.c
C | 311 lines | 247 code | 41 blank | 23 comment | 37 complexity | 31e5b139d5f9e2d5da133a130fca111d MD5 | raw file
1/* 2 * Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC") 3 * Copyright (C) 1999-2003 Internet Software Consortium. 4 * 5 * Permission to use, copy, modify, and 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.22.2.2.10.7 2004/04/29 01:31:22 marka Exp $ */ 19 20#include <config.h> 21 22#include <errno.h> 23#include <unistd.h> 24 25#include <isc/net.h> 26#include <isc/once.h> 27#include <isc/strerror.h> 28#include <isc/string.h> 29#include <isc/util.h> 30 31#if defined(ISC_PLATFORM_HAVEIPV6) && defined(ISC_PLATFORM_NEEDIN6ADDRANY) 32const struct in6_addr isc_net_in6addrany = IN6ADDR_ANY_INIT; 33#endif 34 35#if defined(ISC_PLATFORM_HAVEIPV6) && defined(ISC_PLATFORM_NEEDIN6ADDRLOOPBACK) 36const struct in6_addr isc_net_in6addrloop = IN6ADDR_LOOPBACK_INIT; 37#endif 38 39static isc_boolean_t once = ISC_FALSE; 40static isc_once_t once_ipv6only = ISC_ONCE_INIT; 41static isc_once_t once_ipv6pktinfo = ISC_ONCE_INIT; 42static isc_result_t ipv4_result = ISC_R_NOTFOUND; 43static isc_result_t ipv6_result = ISC_R_NOTFOUND; 44static isc_result_t ipv6only_result = ISC_R_NOTFOUND; 45static isc_result_t ipv6pktinfo_result = ISC_R_NOTFOUND; 46 47static isc_result_t 48try_proto(int domain) { 49 int s; 50 isc_result_t result = ISC_R_SUCCESS; 51 char strbuf[ISC_STRERRORSIZE]; 52 53 s = socket(domain, SOCK_STREAM, 0); 54 if (s == -1) { 55 switch (errno) { 56#ifdef EAFNOSUPPORT 57 case EAFNOSUPPORT: 58#endif 59#ifdef EPROTONOSUPPORT 60 case EPROTONOSUPPORT: 61#endif 62#ifdef EINVAL 63 case EINVAL: 64#endif 65 return (ISC_R_NOTFOUND); 66 default: 67 isc__strerror(errno, strbuf, sizeof(strbuf)); 68 UNEXPECTED_ERROR(__FILE__, __LINE__, 69 "socket() failed: %s", 70 strbuf); 71 return (ISC_R_UNEXPECTED); 72 } 73 } 74 75#ifdef ISC_PLATFORM_HAVEIPV6 76#ifdef WANT_IPV6 77#ifdef ISC_PLATFORM_HAVEIN6PKTINFO 78 if (domain == PF_INET6) { 79 struct sockaddr_in6 sin6; 80 GETSOCKNAME_SOCKLEN_TYPE len; 81 82 /* 83 * Check to see if IPv6 is broken, as is common on Linux. 84 */ 85 len = sizeof(sin6); 86 if (getsockname(s, (struct sockaddr *)&sin6, &len) < 0) 87 { 88 result = ISC_R_NOTFOUND; 89 } else { 90 if (len == sizeof(struct sockaddr_in6)) 91 result = ISC_R_SUCCESS; 92 else { 93 result = ISC_R_NOTFOUND; 94 } 95 } 96 } 97#endif 98#endif 99#endif 100 101 (void)close(s); 102 103 return (result); 104} 105 106static void 107initialize_action(void) { 108 ipv4_result = try_proto(PF_INET); 109#ifdef ISC_PLATFORM_HAVEIPV6 110#ifdef WANT_IPV6 111#ifdef ISC_PLATFORM_HAVEIN6PKTINFO 112 ipv6_result = try_proto(PF_INET6); 113#endif 114#endif 115#endif 116} 117 118static void 119initialize(void) { 120 if(once == ISC_FALSE) { 121 initialize_action(); 122 once = ISC_TRUE; 123 } 124} 125 126isc_result_t 127isc_net_probeipv4(void) { 128 initialize(); 129 return (ipv4_result); 130} 131 132isc_result_t 133isc_net_probeipv6(void) { 134 initialize(); 135 return (ipv6_result); 136} 137 138#ifdef ISC_PLATFORM_HAVEIPV6 139#ifdef WANT_IPV6 140static void 141try_ipv6only(void) { 142#ifdef IPV6_V6ONLY 143 int s, on; 144 char strbuf[ISC_STRERRORSIZE]; 145#endif 146 isc_result_t result; 147 148 result = isc_net_probeipv6(); 149 if (result != ISC_R_SUCCESS) { 150 ipv6only_result = result; 151 return; 152 } 153 154#ifndef IPV6_V6ONLY 155 ipv6only_result = ISC_R_NOTFOUND; 156 return; 157#else 158 /* check for TCP sockets */ 159 s = socket(PF_INET6, SOCK_STREAM, 0); 160 if (s == -1) { 161 isc__strerror(errno, strbuf, sizeof(strbuf)); 162 UNEXPECTED_ERROR(__FILE__, __LINE__, 163 "socket() failed: %s", 164 strbuf); 165 ipv6only_result = ISC_R_UNEXPECTED; 166 return; 167 } 168 169 on = 1; 170 if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) { 171 ipv6only_result = ISC_R_NOTFOUND; 172 goto close; 173 } 174 175 close(s); 176 177 /* check for UDP sockets */ 178 s = socket(PF_INET6, SOCK_DGRAM, 0); 179 if (s == -1) { 180 isc__strerror(errno, strbuf, sizeof(strbuf)); 181 UNEXPECTED_ERROR(__FILE__, __LINE__, 182 "socket() failed: %s", 183 strbuf); 184 ipv6only_result = ISC_R_UNEXPECTED; 185 return; 186 } 187 188 on = 1; 189 if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) { 190 ipv6only_result = ISC_R_NOTFOUND; 191 goto close; 192 } 193 194 close(s); 195 196 ipv6only_result = ISC_R_SUCCESS; 197 198close: 199 close(s); 200 return; 201#endif /* IPV6_V6ONLY */ 202} 203 204static void 205initialize_ipv6only(void) { 206 RUNTIME_CHECK(isc_once_do(&once_ipv6only, 207 try_ipv6only) == ISC_R_SUCCESS); 208} 209 210static void 211try_ipv6pktinfo(void) { 212 int s, on; 213 char strbuf[ISC_STRERRORSIZE]; 214 isc_result_t result; 215 int optname; 216 217 result = isc_net_probeipv6(); 218 if (result != ISC_R_SUCCESS) { 219 ipv6pktinfo_result = result; 220 return; 221 } 222 223 /* we only use this for UDP sockets */ 224 s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP); 225 if (s == -1) { 226 isc__strerror(errno, strbuf, sizeof(strbuf)); 227 UNEXPECTED_ERROR(__FILE__, __LINE__, 228 "socket() failed: %s", 229 strbuf); 230 ipv6pktinfo_result = ISC_R_UNEXPECTED; 231 return; 232 } 233 234#ifdef IPV6_RECVPKTINFO 235 optname = IPV6_RECVPKTINFO; 236#else 237 optname = IPV6_PKTINFO; 238#endif 239 on = 1; 240 if (setsockopt(s, IPPROTO_IPV6, optname, &on, sizeof(on)) < 0) { 241 ipv6pktinfo_result = ISC_R_NOTFOUND; 242 goto close; 243 } 244 245 close(s); 246 ipv6pktinfo_result = ISC_R_SUCCESS; 247 248close: 249 close(s); 250 return; 251} 252 253static void 254initialize_ipv6pktinfo(void) { 255 RUNTIME_CHECK(isc_once_do(&once_ipv6pktinfo, 256 try_ipv6pktinfo) == ISC_R_SUCCESS); 257} 258#endif /* WANT_IPV6 */ 259#endif /* ISC_PLATFORM_HAVEIPV6 */ 260 261isc_result_t 262isc_net_probe_ipv6only(void) { 263#ifdef ISC_PLATFORM_HAVEIPV6 264#ifdef WANT_IPV6 265 initialize_ipv6only(); 266#else 267 ipv6only_result = ISC_R_NOTFOUND; 268#endif 269#endif 270 return (ipv6only_result); 271} 272 273isc_result_t 274isc_net_probe_ipv6pktinfo(void) { 275#ifdef ISC_PLATFORM_HAVEIPV6 276#ifdef WANT_IPV6 277 initialize_ipv6pktinfo(); 278#else 279 ipv6pktinfo_result = ISC_R_NOTFOUND; 280#endif 281#endif 282 return (ipv6pktinfo_result); 283} 284 285void 286isc_net_disableipv4(void) { 287 initialize(); 288 if (ipv4_result == ISC_R_SUCCESS) 289 ipv4_result = ISC_R_DISABLED; 290} 291 292void 293isc_net_disableipv6(void) { 294 initialize(); 295 if (ipv6_result == ISC_R_SUCCESS) 296 ipv6_result = ISC_R_DISABLED; 297} 298 299void 300isc_net_enableipv4(void) { 301 initialize(); 302 if (ipv4_result == ISC_R_DISABLED) 303 ipv4_result = ISC_R_SUCCESS; 304} 305 306void 307isc_net_enableipv6(void) { 308 initialize(); 309 if (ipv6_result == ISC_R_DISABLED) 310 ipv6_result = ISC_R_SUCCESS; 311}