diff options
Diffstat (limited to 'lib/isc/unix/net.c')
-rw-r--r-- | lib/isc/unix/net.c | 860 |
1 files changed, 860 insertions, 0 deletions
diff --git a/lib/isc/unix/net.c b/lib/isc/unix/net.c new file mode 100644 index 0000000..1e43b4b --- /dev/null +++ b/lib/isc/unix/net.c @@ -0,0 +1,860 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include <stdbool.h> +#include <sys/types.h> + +#if defined(HAVE_SYS_SYSCTL_H) && !defined(__linux__) +#if defined(HAVE_SYS_PARAM_H) +#include <sys/param.h> +#endif /* if defined(HAVE_SYS_PARAM_H) */ +#include <sys/sysctl.h> +#endif /* if defined(HAVE_SYS_SYSCTL_H) && !defined(__linux__) */ +#include <errno.h> +#include <fcntl.h> +#include <sys/uio.h> +#include <unistd.h> + +#include <isc/log.h> +#include <isc/net.h> +#include <isc/netdb.h> +#include <isc/once.h> +#include <isc/strerr.h> +#include <isc/string.h> +#include <isc/util.h> + +#ifndef socklen_t +#define socklen_t unsigned int +#endif /* ifndef socklen_t */ + +/*% + * Definitions about UDP port range specification. This is a total mess of + * portability variants: some use sysctl (but the sysctl names vary), some use + * system-specific interfaces, some have the same interface for IPv4 and IPv6, + * some separate them, etc... + */ + +/*% + * The last resort defaults: use all non well known port space + */ +#ifndef ISC_NET_PORTRANGELOW +#define ISC_NET_PORTRANGELOW 1024 +#endif /* ISC_NET_PORTRANGELOW */ +#ifndef ISC_NET_PORTRANGEHIGH +#define ISC_NET_PORTRANGEHIGH 65535 +#endif /* ISC_NET_PORTRANGEHIGH */ + +#ifdef HAVE_SYSCTLBYNAME + +/*% + * sysctl variants + */ +#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__) +#define USE_SYSCTL_PORTRANGE +#define SYSCTL_V4PORTRANGE_LOW "net.inet.ip.portrange.hifirst" +#define SYSCTL_V4PORTRANGE_HIGH "net.inet.ip.portrange.hilast" +#define SYSCTL_V6PORTRANGE_LOW "net.inet.ip.portrange.hifirst" +#define SYSCTL_V6PORTRANGE_HIGH "net.inet.ip.portrange.hilast" +#endif /* if defined(__FreeBSD__) || defined(__APPLE__) || \ + * defined(__DragonFly__) */ + +#ifdef __NetBSD__ +#define USE_SYSCTL_PORTRANGE +#define SYSCTL_V4PORTRANGE_LOW "net.inet.ip.anonportmin" +#define SYSCTL_V4PORTRANGE_HIGH "net.inet.ip.anonportmax" +#define SYSCTL_V6PORTRANGE_LOW "net.inet6.ip6.anonportmin" +#define SYSCTL_V6PORTRANGE_HIGH "net.inet6.ip6.anonportmax" +#endif /* ifdef __NetBSD__ */ + +#else /* !HAVE_SYSCTLBYNAME */ + +#ifdef __OpenBSD__ +#define USE_SYSCTL_PORTRANGE +#define SYSCTL_V4PORTRANGE_LOW \ + { \ + CTL_NET, PF_INET, IPPROTO_IP, IPCTL_IPPORT_HIFIRSTAUTO \ + } +#define SYSCTL_V4PORTRANGE_HIGH \ + { \ + CTL_NET, PF_INET, IPPROTO_IP, IPCTL_IPPORT_HILASTAUTO \ + } +/* Same for IPv6 */ +#define SYSCTL_V6PORTRANGE_LOW SYSCTL_V4PORTRANGE_LOW +#define SYSCTL_V6PORTRANGE_HIGH SYSCTL_V4PORTRANGE_HIGH +#endif /* ifdef __OpenBSD__ */ + +#endif /* HAVE_SYSCTLBYNAME */ + +static isc_once_t once_ipv6only = ISC_ONCE_INIT; +#ifdef __notyet__ +static isc_once_t once_ipv6pktinfo = ISC_ONCE_INIT; +#endif /* ifdef __notyet__ */ + +#ifndef ISC_CMSG_IP_TOS +#ifdef __APPLE__ +#define ISC_CMSG_IP_TOS 0 /* As of 10.8.2. */ +#else /* ! __APPLE__ */ +#define ISC_CMSG_IP_TOS 1 +#endif /* ! __APPLE__ */ +#endif /* ! ISC_CMSG_IP_TOS */ + +static isc_once_t once = ISC_ONCE_INIT; +static isc_once_t once_dscp = ISC_ONCE_INIT; + +static isc_result_t ipv4_result = ISC_R_NOTFOUND; +static isc_result_t ipv6_result = ISC_R_NOTFOUND; +static isc_result_t unix_result = ISC_R_NOTFOUND; +static isc_result_t ipv6only_result = ISC_R_NOTFOUND; +static isc_result_t ipv6pktinfo_result = ISC_R_NOTFOUND; +static unsigned int dscp_result = 0; + +static isc_result_t +try_proto(int domain) { + int s; + isc_result_t result = ISC_R_SUCCESS; + char strbuf[ISC_STRERRORSIZE]; + + s = socket(domain, SOCK_STREAM, 0); + if (s == -1) { + switch (errno) { +#ifdef EAFNOSUPPORT + case EAFNOSUPPORT: +#endif /* ifdef EAFNOSUPPORT */ +#ifdef EPFNOSUPPORT + case EPFNOSUPPORT: +#endif /* ifdef EPFNOSUPPORT */ +#ifdef EPROTONOSUPPORT + case EPROTONOSUPPORT: +#endif /* ifdef EPROTONOSUPPORT */ +#ifdef EINVAL + case EINVAL: +#endif /* ifdef EINVAL */ + return (ISC_R_NOTFOUND); + default: + strerror_r(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "socket() failed: %s", strbuf); + return (ISC_R_UNEXPECTED); + } + } + + if (domain == PF_INET6) { + struct sockaddr_in6 sin6; + unsigned int len; + + /* + * Check to see if IPv6 is broken, as is common on Linux. + */ + len = sizeof(sin6); + if (getsockname(s, (struct sockaddr *)&sin6, (void *)&len) < 0) + { + isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_SOCKET, ISC_LOG_ERROR, + "retrieving the address of an IPv6 " + "socket from the kernel failed."); + isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_SOCKET, ISC_LOG_ERROR, + "IPv6 is not supported."); + result = ISC_R_NOTFOUND; + } else { + if (len == sizeof(struct sockaddr_in6)) { + result = ISC_R_SUCCESS; + } else { + isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_SOCKET, + ISC_LOG_ERROR, + "IPv6 structures in kernel and " + "user space do not match."); + isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_SOCKET, + ISC_LOG_ERROR, + "IPv6 is not supported."); + result = ISC_R_NOTFOUND; + } + } + } + + (void)close(s); + + return (result); +} + +static void +initialize_action(void) { + ipv4_result = try_proto(PF_INET); + ipv6_result = try_proto(PF_INET6); +#ifdef ISC_PLATFORM_HAVESYSUNH + unix_result = try_proto(PF_UNIX); +#endif /* ifdef ISC_PLATFORM_HAVESYSUNH */ +} + +static void +initialize(void) { + RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS); +} + +isc_result_t +isc_net_probeipv4(void) { + initialize(); + return (ipv4_result); +} + +isc_result_t +isc_net_probeipv6(void) { + initialize(); + return (ipv6_result); +} + +isc_result_t +isc_net_probeunix(void) { + initialize(); + return (unix_result); +} + +static void +try_ipv6only(void) { +#ifdef IPV6_V6ONLY + int s, on; + char strbuf[ISC_STRERRORSIZE]; +#endif /* ifdef IPV6_V6ONLY */ + isc_result_t result; + + result = isc_net_probeipv6(); + if (result != ISC_R_SUCCESS) { + ipv6only_result = result; + return; + } + +#ifndef IPV6_V6ONLY + ipv6only_result = ISC_R_NOTFOUND; + return; +#else /* ifndef IPV6_V6ONLY */ + /* check for TCP sockets */ + s = socket(PF_INET6, SOCK_STREAM, 0); + if (s == -1) { + strerror_r(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, "socket() failed: %s", + strbuf); + ipv6only_result = ISC_R_UNEXPECTED; + return; + } + + on = 1; + if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) { + ipv6only_result = ISC_R_NOTFOUND; + goto close; + } + + close(s); + + /* check for UDP sockets */ + s = socket(PF_INET6, SOCK_DGRAM, 0); + if (s == -1) { + strerror_r(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, "socket() failed: %s", + strbuf); + ipv6only_result = ISC_R_UNEXPECTED; + return; + } + + on = 1; + if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) { + ipv6only_result = ISC_R_NOTFOUND; + goto close; + } + + ipv6only_result = ISC_R_SUCCESS; + +close: + close(s); + return; +#endif /* IPV6_V6ONLY */ +} + +static void +initialize_ipv6only(void) { + RUNTIME_CHECK(isc_once_do(&once_ipv6only, try_ipv6only) == + ISC_R_SUCCESS); +} + +#ifdef __notyet__ +static void +try_ipv6pktinfo(void) { + int s, on; + char strbuf[ISC_STRERRORSIZE]; + isc_result_t result; + int optname; + + result = isc_net_probeipv6(); + if (result != ISC_R_SUCCESS) { + ipv6pktinfo_result = result; + return; + } + + /* we only use this for UDP sockets */ + s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP); + if (s == -1) { + strerror_r(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, "socket() failed: %s", + strbuf); + ipv6pktinfo_result = ISC_R_UNEXPECTED; + return; + } + +#ifdef IPV6_RECVPKTINFO + optname = IPV6_RECVPKTINFO; +#else /* ifdef IPV6_RECVPKTINFO */ + optname = IPV6_PKTINFO; +#endif /* ifdef IPV6_RECVPKTINFO */ + on = 1; + if (setsockopt(s, IPPROTO_IPV6, optname, &on, sizeof(on)) < 0) { + ipv6pktinfo_result = ISC_R_NOTFOUND; + goto close; + } + + ipv6pktinfo_result = ISC_R_SUCCESS; + +close: + close(s); + return; +} + +static void +initialize_ipv6pktinfo(void) { + RUNTIME_CHECK(isc_once_do(&once_ipv6pktinfo, try_ipv6pktinfo) == + ISC_R_SUCCESS); +} +#endif /* ifdef __notyet__ */ + +isc_result_t +isc_net_probe_ipv6only(void) { + initialize_ipv6only(); + return (ipv6only_result); +} + +isc_result_t +isc_net_probe_ipv6pktinfo(void) { +/* + * XXXWPK if pktinfo were supported then we could listen on :: for ipv6 and get + * the information about the destination address from pktinfo structure passed + * in recvmsg but this method is not portable and libuv doesn't support it - so + * we need to listen on all interfaces. + * We should verify that this doesn't impact performance (we already do it for + * ipv4) and either remove all the ipv6pktinfo detection code from above + * or think of fixing libuv. + */ +#ifdef __notyet__ + initialize_ipv6pktinfo(); +#endif /* ifdef __notyet__ */ + return (ipv6pktinfo_result); +} + +#if ISC_CMSG_IP_TOS || defined(IPV6_TCLASS) + +static socklen_t +cmsg_len(socklen_t len) { +#ifdef CMSG_LEN + return (CMSG_LEN(len)); +#else /* ifdef CMSG_LEN */ + socklen_t hdrlen; + + /* + * Cast NULL so that any pointer arithmetic performed by CMSG_DATA + * is correct. + */ + hdrlen = (socklen_t)CMSG_DATA(((struct cmsghdr *)NULL)); + return (hdrlen + len); +#endif /* ifdef CMSG_LEN */ +} + +static socklen_t +cmsg_space(socklen_t len) { +#ifdef CMSG_SPACE + return (CMSG_SPACE(len)); +#else /* ifdef CMSG_SPACE */ + struct msghdr msg; + struct cmsghdr *cmsgp; + /* + * XXX: The buffer length is an ad-hoc value, but should be enough + * in a practical sense. + */ + char dummybuf[sizeof(struct cmsghdr) + 1024]; + + memset(&msg, 0, sizeof(msg)); + msg.msg_control = dummybuf; + msg.msg_controllen = sizeof(dummybuf); + + cmsgp = (struct cmsghdr *)dummybuf; + cmsgp->cmsg_len = cmsg_len(len); + + cmsgp = CMSG_NXTHDR(&msg, cmsgp); + if (cmsgp != NULL) { + return ((char *)cmsgp - (char *)msg.msg_control); + } else { + return (0); + } +#endif /* ifdef CMSG_SPACE */ +} + +/* + * Make a fd non-blocking. + */ +static isc_result_t +make_nonblock(int fd) { + int ret; + int flags; + char strbuf[ISC_STRERRORSIZE]; +#ifdef USE_FIONBIO_IOCTL + int on = 1; + + ret = ioctl(fd, FIONBIO, (char *)&on); +#else /* ifdef USE_FIONBIO_IOCTL */ + flags = fcntl(fd, F_GETFL, 0); + flags |= PORT_NONBLOCK; + ret = fcntl(fd, F_SETFL, flags); +#endif /* ifdef USE_FIONBIO_IOCTL */ + + if (ret == -1) { + strerror_r(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, +#ifdef USE_FIONBIO_IOCTL + "ioctl(%d, FIONBIO, &on): %s", fd, +#else /* ifdef USE_FIONBIO_IOCTL */ + "fcntl(%d, F_SETFL, %d): %s", fd, flags, +#endif /* ifdef USE_FIONBIO_IOCTL */ + strbuf); + + return (ISC_R_UNEXPECTED); + } + + return (ISC_R_SUCCESS); +} + +static bool +cmsgsend(int s, int level, int type, struct addrinfo *res) { + char strbuf[ISC_STRERRORSIZE]; + struct sockaddr_storage ss; + socklen_t len = sizeof(ss); + struct msghdr msg; + union { + struct cmsghdr h; + unsigned char b[256]; + } control; + struct cmsghdr *cmsgp; + int dscp = (46 << 2); /* Expedited forwarding. */ + struct iovec iovec; + char buf[1] = { 0 }; + isc_result_t result; + + if (bind(s, res->ai_addr, res->ai_addrlen) < 0) { + strerror_r(errno, strbuf, sizeof(strbuf)); + isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_SOCKET, ISC_LOG_DEBUG(10), + "bind: %s", strbuf); + return (false); + } + + if (getsockname(s, (struct sockaddr *)&ss, &len) < 0) { + strerror_r(errno, strbuf, sizeof(strbuf)); + isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_SOCKET, ISC_LOG_DEBUG(10), + "getsockname: %s", strbuf); + return (false); + } + + iovec.iov_base = buf; + iovec.iov_len = sizeof(buf); + + memset(&msg, 0, sizeof(msg)); + msg.msg_name = (struct sockaddr *)&ss; + msg.msg_namelen = len; + msg.msg_iov = &iovec; + msg.msg_iovlen = 1; + msg.msg_control = (void *)&control; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + cmsgp = msg.msg_control; + + switch (type) { +#ifdef IP_TOS + case IP_TOS: + memset(cmsgp, 0, cmsg_space(sizeof(char))); + cmsgp->cmsg_level = level; + cmsgp->cmsg_type = type; + cmsgp->cmsg_len = cmsg_len(sizeof(char)); + *(unsigned char *)CMSG_DATA(cmsgp) = dscp; + msg.msg_controllen += cmsg_space(sizeof(char)); + break; +#endif /* ifdef IP_TOS */ +#ifdef IPV6_TCLASS + case IPV6_TCLASS: + memset(cmsgp, 0, cmsg_space(sizeof(dscp))); + cmsgp->cmsg_level = level; + cmsgp->cmsg_type = type; + cmsgp->cmsg_len = cmsg_len(sizeof(dscp)); + memmove(CMSG_DATA(cmsgp), &dscp, sizeof(dscp)); + msg.msg_controllen += cmsg_space(sizeof(dscp)); + break; +#endif /* ifdef IPV6_TCLASS */ + default: + UNREACHABLE(); + } + + if (sendmsg(s, &msg, 0) < 0) { + int debug = ISC_LOG_DEBUG(10); + const char *typestr; + switch (errno) { +#ifdef ENOPROTOOPT + case ENOPROTOOPT: +#endif /* ifdef ENOPROTOOPT */ +#ifdef EOPNOTSUPP + case EOPNOTSUPP: +#endif /* ifdef EOPNOTSUPP */ + case EINVAL: + case EPERM: + break; + default: + debug = ISC_LOG_NOTICE; + } + strerror_r(errno, strbuf, sizeof(strbuf)); + if (debug != ISC_LOG_NOTICE) { + isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_SOCKET, ISC_LOG_DEBUG(10), + "sendmsg: %s", strbuf); + } else { + typestr = (type == IP_TOS) ? "IP_TOS" : "IPV6_TCLASS"; + UNEXPECTED_ERROR(__FILE__, __LINE__, + "probing " + "sendmsg() with %s=%02x failed: %s", + typestr, dscp, strbuf); + } + return (false); + } + + /* + * Make sure the message actually got sent. + */ + result = make_nonblock(s); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + + iovec.iov_base = buf; + iovec.iov_len = sizeof(buf); + + memset(&msg, 0, sizeof(msg)); + msg.msg_name = (struct sockaddr *)&ss; + msg.msg_namelen = sizeof(ss); + msg.msg_iov = &iovec; + msg.msg_iovlen = 1; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + if (recvmsg(s, &msg, 0) < 0) { + return (false); + } + + return (true); +} +#endif /* if ISC_CMSG_IP_TOS || defined(IPV6_TCLASS) */ + +static void +try_dscp_v4(void) { +#ifdef IP_TOS + char strbuf[ISC_STRERRORSIZE]; + struct addrinfo hints, *res0; + int s, dscp = 0, n; +#ifdef IP_RECVTOS + int on = 1; +#endif /* IP_RECVTOS */ + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; +#ifdef AI_NUMERICHOST + hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST; +#else /* ifdef AI_NUMERICHOST */ + hints.ai_flags = AI_PASSIVE; +#endif /* ifdef AI_NUMERICHOST */ + + n = getaddrinfo("127.0.0.1", NULL, &hints, &res0); + if (n != 0 || res0 == NULL) { + isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_SOCKET, ISC_LOG_DEBUG(10), + "getaddrinfo(127.0.0.1): %s", gai_strerror(n)); + return; + } + + s = socket(res0->ai_family, res0->ai_socktype, res0->ai_protocol); + + if (s == -1) { + strerror_r(errno, strbuf, sizeof(strbuf)); + isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_SOCKET, ISC_LOG_DEBUG(10), + "socket: %s", strbuf); + freeaddrinfo(res0); + return; + } + + if (setsockopt(s, IPPROTO_IP, IP_TOS, &dscp, sizeof(dscp)) == 0) { + dscp_result |= ISC_NET_DSCPSETV4; + } + +#ifdef IP_RECVTOS + on = 1; + if (setsockopt(s, IPPROTO_IP, IP_RECVTOS, &on, sizeof(on)) == 0) { + dscp_result |= ISC_NET_DSCPRECVV4; + } +#endif /* IP_RECVTOS */ + +#if ISC_CMSG_IP_TOS + if (cmsgsend(s, IPPROTO_IP, IP_TOS, res0)) { + dscp_result |= ISC_NET_DSCPPKTV4; + } +#endif /* ISC_CMSG_IP_TOS */ + + freeaddrinfo(res0); + close(s); + +#endif /* IP_TOS */ +} + +static void +try_dscp_v6(void) { +#ifdef IPV6_TCLASS + char strbuf[ISC_STRERRORSIZE]; + struct addrinfo hints, *res0; + int s, dscp = 0, n; +#if defined(IPV6_RECVTCLASS) + int on = 1; +#endif /* IPV6_RECVTCLASS */ + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; +#ifdef AI_NUMERICHOST + hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST; +#else /* ifdef AI_NUMERICHOST */ + hints.ai_flags = AI_PASSIVE; +#endif /* ifdef AI_NUMERICHOST */ + + n = getaddrinfo("::1", NULL, &hints, &res0); + if (n != 0 || res0 == NULL) { + isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_SOCKET, ISC_LOG_DEBUG(10), + "getaddrinfo(::1): %s", gai_strerror(n)); + return; + } + + s = socket(res0->ai_family, res0->ai_socktype, res0->ai_protocol); + if (s == -1) { + strerror_r(errno, strbuf, sizeof(strbuf)); + isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_SOCKET, ISC_LOG_DEBUG(10), + "socket: %s", strbuf); + freeaddrinfo(res0); + return; + } + if (setsockopt(s, IPPROTO_IPV6, IPV6_TCLASS, &dscp, sizeof(dscp)) == 0) + { + dscp_result |= ISC_NET_DSCPSETV6; + } + +#ifdef IPV6_RECVTCLASS + on = 1; + if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVTCLASS, &on, sizeof(on)) == 0) + { + dscp_result |= ISC_NET_DSCPRECVV6; + } +#endif /* IPV6_RECVTCLASS */ + + if (cmsgsend(s, IPPROTO_IPV6, IPV6_TCLASS, res0)) { + dscp_result |= ISC_NET_DSCPPKTV6; + } + + freeaddrinfo(res0); + close(s); + +#endif /* IPV6_TCLASS */ +} + +static void +try_dscp(void) { + try_dscp_v4(); + try_dscp_v6(); +} + +static void +initialize_dscp(void) { + RUNTIME_CHECK(isc_once_do(&once_dscp, try_dscp) == ISC_R_SUCCESS); +} + +unsigned int +isc_net_probedscp(void) { + initialize_dscp(); + return (dscp_result); +} + +#if defined(USE_SYSCTL_PORTRANGE) +#if defined(HAVE_SYSCTLBYNAME) +static isc_result_t +getudpportrange_sysctl(int af, in_port_t *low, in_port_t *high) { + int port_low, port_high; + size_t portlen; + const char *sysctlname_lowport, *sysctlname_hiport; + + if (af == AF_INET) { + sysctlname_lowport = SYSCTL_V4PORTRANGE_LOW; + sysctlname_hiport = SYSCTL_V4PORTRANGE_HIGH; + } else { + sysctlname_lowport = SYSCTL_V6PORTRANGE_LOW; + sysctlname_hiport = SYSCTL_V6PORTRANGE_HIGH; + } + portlen = sizeof(port_low); + if (sysctlbyname(sysctlname_lowport, &port_low, &portlen, NULL, 0) < 0) + { + return (ISC_R_FAILURE); + } + portlen = sizeof(port_high); + if (sysctlbyname(sysctlname_hiport, &port_high, &portlen, NULL, 0) < 0) + { + return (ISC_R_FAILURE); + } + if ((port_low & ~0xffff) != 0 || (port_high & ~0xffff) != 0) { + return (ISC_R_RANGE); + } + + *low = (in_port_t)port_low; + *high = (in_port_t)port_high; + + return (ISC_R_SUCCESS); +} +#else /* !HAVE_SYSCTLBYNAME */ +static isc_result_t +getudpportrange_sysctl(int af, in_port_t *low, in_port_t *high) { + int mib_lo4[4] = SYSCTL_V4PORTRANGE_LOW; + int mib_hi4[4] = SYSCTL_V4PORTRANGE_HIGH; + int mib_lo6[4] = SYSCTL_V6PORTRANGE_LOW; + int mib_hi6[4] = SYSCTL_V6PORTRANGE_HIGH; + int *mib_lo, *mib_hi, miblen; + int port_low, port_high; + size_t portlen; + + if (af == AF_INET) { + mib_lo = mib_lo4; + mib_hi = mib_hi4; + miblen = sizeof(mib_lo4) / sizeof(mib_lo4[0]); + } else { + mib_lo = mib_lo6; + mib_hi = mib_hi6; + miblen = sizeof(mib_lo6) / sizeof(mib_lo6[0]); + } + + portlen = sizeof(port_low); + if (sysctl(mib_lo, miblen, &port_low, &portlen, NULL, 0) < 0) { + return (ISC_R_FAILURE); + } + + portlen = sizeof(port_high); + if (sysctl(mib_hi, miblen, &port_high, &portlen, NULL, 0) < 0) { + return (ISC_R_FAILURE); + } + + if ((port_low & ~0xffff) != 0 || (port_high & ~0xffff) != 0) { + return (ISC_R_RANGE); + } + + *low = (in_port_t)port_low; + *high = (in_port_t)port_high; + + return (ISC_R_SUCCESS); +} +#endif /* HAVE_SYSCTLBYNAME */ +#endif /* USE_SYSCTL_PORTRANGE */ + +isc_result_t +isc_net_getudpportrange(int af, in_port_t *low, in_port_t *high) { + int result = ISC_R_FAILURE; +#if !defined(USE_SYSCTL_PORTRANGE) && defined(__linux) + FILE *fp; +#endif /* if !defined(USE_SYSCTL_PORTRANGE) && defined(__linux) */ + + REQUIRE(low != NULL && high != NULL); + +#if defined(USE_SYSCTL_PORTRANGE) + result = getudpportrange_sysctl(af, low, high); +#elif defined(__linux) + + UNUSED(af); + + /* + * Linux local ports are address family agnostic. + */ + fp = fopen("/proc/sys/net/ipv4/ip_local_port_range", "r"); + if (fp != NULL) { + int n; + unsigned int l, h; + + n = fscanf(fp, "%u %u", &l, &h); + if (n == 2 && (l & ~0xffff) == 0 && (h & ~0xffff) == 0) { + *low = l; + *high = h; + result = ISC_R_SUCCESS; + } + fclose(fp); + } +#else /* if defined(USE_SYSCTL_PORTRANGE) */ + UNUSED(af); +#endif /* if defined(USE_SYSCTL_PORTRANGE) */ + + if (result != ISC_R_SUCCESS) { + *low = ISC_NET_PORTRANGELOW; + *high = ISC_NET_PORTRANGEHIGH; + } + + return (ISC_R_SUCCESS); /* we currently never fail in this function */ +} + +void +isc_net_disableipv4(void) { + initialize(); + if (ipv4_result == ISC_R_SUCCESS) { + ipv4_result = ISC_R_DISABLED; + } +} + +void +isc_net_disableipv6(void) { + initialize(); + if (ipv6_result == ISC_R_SUCCESS) { + ipv6_result = ISC_R_DISABLED; + } +} + +void +isc_net_enableipv4(void) { + initialize(); + if (ipv4_result == ISC_R_DISABLED) { + ipv4_result = ISC_R_SUCCESS; + } +} + +void +isc_net_enableipv6(void) { + initialize(); + if (ipv6_result == ISC_R_DISABLED) { + ipv6_result = ISC_R_SUCCESS; + } +} |