| /* |
| * iperf, Copyright (c) 2014-2019, The Regents of the University of |
| * California, through Lawrence Berkeley National Laboratory (subject |
| * to receipt of any required approvals from the U.S. Dept. of |
| * Energy). All rights reserved. |
| * |
| * If you have questions about your rights to use or distribute this |
| * software, please contact Berkeley Lab's Technology Transfer |
| * Department at TTD@lbl.gov. |
| * |
| * NOTICE. This software is owned by the U.S. Department of Energy. |
| * As such, the U.S. Government has been granted for itself and others |
| * acting on its behalf a paid-up, nonexclusive, irrevocable, |
| * worldwide license in the Software to reproduce, prepare derivative |
| * works, and perform publicly and display publicly. Beginning five |
| * (5) years after the date permission to assert copyright is obtained |
| * from the U.S. Department of Energy, and subject to any subsequent |
| * five (5) year renewals, the U.S. Government is granted for itself |
| * and others acting on its behalf a paid-up, nonexclusive, |
| * irrevocable, worldwide license in the Software to reproduce, |
| * prepare derivative works, distribute copies to the public, perform |
| * publicly and display publicly, and to permit others to do so. |
| * |
| * This code is distributed under a BSD style license, see the LICENSE |
| * file for complete information. |
| */ |
| #include "iperf_config.h" |
| |
| #include <stdio.h> |
| #include <unistd.h> |
| #include <errno.h> |
| #include <sys/socket.h> |
| #include <sys/types.h> |
| #include <netinet/in.h> |
| #include <netinet/tcp.h> |
| #include <assert.h> |
| #include <netdb.h> |
| #include <string.h> |
| #include <fcntl.h> |
| #include <limits.h> |
| |
| #ifdef HAVE_SENDFILE |
| #ifdef linux |
| #include <sys/sendfile.h> |
| #else |
| #ifdef __FreeBSD__ |
| #include <sys/uio.h> |
| #else |
| #if defined(__APPLE__) && defined(__MACH__) /* OS X */ |
| #include <AvailabilityMacros.h> |
| #if defined(MAC_OS_X_VERSION_10_6) |
| #include <sys/uio.h> |
| #endif |
| #endif |
| #endif |
| #endif |
| #endif /* HAVE_SENDFILE */ |
| |
| #ifdef HAVE_POLL_H |
| #include <poll.h> |
| #endif /* HAVE_POLL_H */ |
| |
| #include "iperf_util.h" |
| #include "net.h" |
| #include "timer.h" |
| |
| /* |
| * Declaration of gerror in iperf_error.c. Most other files in iperf3 can get this |
| * by including "iperf.h", but net.c lives "below" this layer. Clearly the |
| * presence of this declaration is a sign we need to revisit this layering. |
| */ |
| extern int gerror; |
| |
| /* |
| * timeout_connect adapted from netcat, via OpenBSD and FreeBSD |
| * Copyright (c) 2001 Eric Jackson <ericj@monkey.org> |
| */ |
| int |
| timeout_connect(int s, const struct sockaddr *name, socklen_t namelen, |
| int timeout) |
| { |
| struct pollfd pfd; |
| socklen_t optlen; |
| int flags, optval; |
| int ret; |
| |
| flags = 0; |
| if (timeout != -1) { |
| flags = fcntl(s, F_GETFL, 0); |
| if (fcntl(s, F_SETFL, flags | O_NONBLOCK) == -1) |
| return -1; |
| } |
| |
| if ((ret = connect(s, name, namelen)) != 0 && errno == EINPROGRESS) { |
| pfd.fd = s; |
| pfd.events = POLLOUT; |
| if ((ret = poll(&pfd, 1, timeout)) == 1) { |
| optlen = sizeof(optval); |
| if ((ret = getsockopt(s, SOL_SOCKET, SO_ERROR, |
| &optval, &optlen)) == 0) { |
| errno = optval; |
| ret = optval == 0 ? 0 : -1; |
| } |
| } else if (ret == 0) { |
| errno = ETIMEDOUT; |
| ret = -1; |
| } else |
| ret = -1; |
| } |
| |
| if (timeout != -1 && fcntl(s, F_SETFL, flags) == -1) |
| ret = -1; |
| |
| return (ret); |
| } |
| |
| /* netdial and netannouce code comes from libtask: http://swtch.com/libtask/ |
| * Copyright: http://swtch.com/libtask/COPYRIGHT |
| */ |
| |
| /* make connection to server */ |
| int |
| netdial(int domain, int proto, char *local, int local_port, char *server, int port, int timeout) |
| { |
| struct addrinfo hints, *local_res, *server_res; |
| int s, saved_errno; |
| |
| if (local) { |
| memset(&hints, 0, sizeof(hints)); |
| hints.ai_family = domain; |
| hints.ai_socktype = proto; |
| if ((gerror = getaddrinfo(local, NULL, &hints, &local_res)) != 0) |
| return -1; |
| } |
| |
| memset(&hints, 0, sizeof(hints)); |
| hints.ai_family = domain; |
| hints.ai_socktype = proto; |
| if ((gerror = getaddrinfo(server, NULL, &hints, &server_res)) != 0) |
| return -1; |
| |
| s = socket(server_res->ai_family, proto, 0); |
| if (s < 0) { |
| if (local) |
| freeaddrinfo(local_res); |
| freeaddrinfo(server_res); |
| return -1; |
| } |
| |
| /* Bind the local address if given a name (with or without --cport) */ |
| if (local) { |
| if (local_port) { |
| struct sockaddr_in *lcladdr; |
| lcladdr = (struct sockaddr_in *)local_res->ai_addr; |
| lcladdr->sin_port = htons(local_port); |
| } |
| |
| if (bind(s, (struct sockaddr *) local_res->ai_addr, local_res->ai_addrlen) < 0) { |
| saved_errno = errno; |
| close(s); |
| freeaddrinfo(local_res); |
| freeaddrinfo(server_res); |
| errno = saved_errno; |
| return -1; |
| } |
| freeaddrinfo(local_res); |
| } |
| /* No local name, but --cport given */ |
| else if (local_port) { |
| size_t addrlen; |
| struct sockaddr_storage lcl; |
| |
| /* IPv4 */ |
| if (server_res->ai_family == AF_INET) { |
| struct sockaddr_in *lcladdr = (struct sockaddr_in *) &lcl; |
| lcladdr->sin_family = AF_INET; |
| lcladdr->sin_port = htons(local_port); |
| lcladdr->sin_addr.s_addr = INADDR_ANY; |
| addrlen = sizeof(struct sockaddr_in); |
| } |
| /* IPv6 */ |
| else if (server_res->ai_family == AF_INET6) { |
| struct sockaddr_in6 *lcladdr = (struct sockaddr_in6 *) &lcl; |
| lcladdr->sin6_family = AF_INET6; |
| lcladdr->sin6_port = htons(local_port); |
| lcladdr->sin6_addr = in6addr_any; |
| addrlen = sizeof(struct sockaddr_in6); |
| } |
| /* Unknown protocol */ |
| else { |
| errno = EAFNOSUPPORT; |
| return -1; |
| } |
| |
| if (bind(s, (struct sockaddr *) &lcl, addrlen) < 0) { |
| saved_errno = errno; |
| close(s); |
| freeaddrinfo(server_res); |
| errno = saved_errno; |
| return -1; |
| } |
| } |
| |
| ((struct sockaddr_in *) server_res->ai_addr)->sin_port = htons(port); |
| if (timeout_connect(s, (struct sockaddr *) server_res->ai_addr, server_res->ai_addrlen, timeout) < 0 && errno != EINPROGRESS) { |
| saved_errno = errno; |
| close(s); |
| freeaddrinfo(server_res); |
| errno = saved_errno; |
| return -1; |
| } |
| |
| freeaddrinfo(server_res); |
| return s; |
| } |
| |
| /***************************************************************/ |
| |
| int |
| netannounce(int domain, int proto, char *local, int port) |
| { |
| struct addrinfo hints, *res; |
| char portstr[6]; |
| int s, opt, saved_errno; |
| |
| snprintf(portstr, 6, "%d", port); |
| memset(&hints, 0, sizeof(hints)); |
| /* |
| * If binding to the wildcard address with no explicit address |
| * family specified, then force us to get an AF_INET6 socket. On |
| * CentOS 6 and MacOS, getaddrinfo(3) with AF_UNSPEC in ai_family, |
| * and ai_flags containing AI_PASSIVE returns a result structure |
| * with ai_family set to AF_INET, with the result that we create |
| * and bind an IPv4 address wildcard address and by default, we |
| * can't accept IPv6 connections. |
| * |
| * On FreeBSD, under the above circumstances, ai_family in the |
| * result structure is set to AF_INET6. |
| */ |
| if (domain == AF_UNSPEC && !local) { |
| hints.ai_family = AF_INET6; |
| } |
| else { |
| hints.ai_family = domain; |
| } |
| hints.ai_socktype = proto; |
| hints.ai_flags = AI_PASSIVE; |
| if ((gerror = getaddrinfo(local, portstr, &hints, &res)) != 0) |
| return -1; |
| |
| s = socket(res->ai_family, proto, 0); |
| if (s < 0) { |
| freeaddrinfo(res); |
| return -1; |
| } |
| |
| opt = 1; |
| if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &opt, sizeof(opt)) < 0) { |
| saved_errno = errno; |
| close(s); |
| freeaddrinfo(res); |
| errno = saved_errno; |
| return -1; |
| } |
| /* |
| * If we got an IPv6 socket, figure out if it should accept IPv4 |
| * connections as well. We do that if and only if no address |
| * family was specified explicitly. Note that we can only |
| * do this if the IPV6_V6ONLY socket option is supported. Also, |
| * OpenBSD explicitly omits support for IPv4-mapped addresses, |
| * even though it implements IPV6_V6ONLY. |
| */ |
| #if defined(IPV6_V6ONLY) && !defined(__OpenBSD__) |
| if (res->ai_family == AF_INET6 && (domain == AF_UNSPEC || domain == AF_INET6)) { |
| if (domain == AF_UNSPEC) |
| opt = 0; |
| else |
| opt = 1; |
| if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, |
| (char *) &opt, sizeof(opt)) < 0) { |
| saved_errno = errno; |
| close(s); |
| freeaddrinfo(res); |
| errno = saved_errno; |
| return -1; |
| } |
| } |
| #endif /* IPV6_V6ONLY */ |
| |
| if (bind(s, (struct sockaddr *) res->ai_addr, res->ai_addrlen) < 0) { |
| saved_errno = errno; |
| close(s); |
| freeaddrinfo(res); |
| errno = saved_errno; |
| return -1; |
| } |
| |
| freeaddrinfo(res); |
| |
| if (proto == SOCK_STREAM) { |
| if (listen(s, INT_MAX) < 0) { |
| saved_errno = errno; |
| close(s); |
| errno = saved_errno; |
| return -1; |
| } |
| } |
| |
| return s; |
| } |
| |
| |
| /*******************************************************************/ |
| /* reads 'count' bytes from a socket */ |
| /********************************************************************/ |
| |
| int |
| Nread(int fd, char *buf, size_t count, int prot) |
| { |
| register ssize_t r; |
| register size_t nleft = count; |
| |
| while (nleft > 0) { |
| r = read(fd, buf, nleft); |
| if (r < 0) { |
| if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) |
| break; |
| else |
| return NET_HARDERROR; |
| } else if (r == 0) |
| break; |
| |
| nleft -= r; |
| buf += r; |
| } |
| return count - nleft; |
| } |
| |
| |
| /* |
| * N W R I T E |
| */ |
| |
| int |
| Nwrite(int fd, const char *buf, size_t count, int prot) |
| { |
| register ssize_t r; |
| register size_t nleft = count; |
| |
| while (nleft > 0) { |
| r = write(fd, buf, nleft); |
| if (r < 0) { |
| switch (errno) { |
| case EINTR: |
| case EAGAIN: |
| #if (EAGAIN != EWOULDBLOCK) |
| case EWOULDBLOCK: |
| #endif |
| return count - nleft; |
| |
| case ENOBUFS: |
| return NET_SOFTERROR; |
| |
| default: |
| return NET_HARDERROR; |
| } |
| } else if (r == 0) |
| return NET_SOFTERROR; |
| nleft -= r; |
| buf += r; |
| } |
| return count; |
| } |
| |
| |
| int |
| has_sendfile(void) |
| { |
| #if defined(HAVE_SENDFILE) |
| return 1; |
| #else /* HAVE_SENDFILE */ |
| return 0; |
| #endif /* HAVE_SENDFILE */ |
| |
| } |
| |
| |
| /* |
| * N S E N D F I L E |
| */ |
| |
| int |
| Nsendfile(int fromfd, int tofd, const char *buf, size_t count) |
| { |
| off_t offset; |
| #if defined(HAVE_SENDFILE) |
| #if defined(__FreeBSD__) || (defined(__APPLE__) && defined(__MACH__) && defined(MAC_OS_X_VERSION_10_6)) |
| off_t sent; |
| #endif |
| register size_t nleft; |
| register ssize_t r; |
| |
| nleft = count; |
| while (nleft > 0) { |
| offset = count - nleft; |
| #ifdef linux |
| r = sendfile(tofd, fromfd, &offset, nleft); |
| if (r > 0) |
| nleft -= r; |
| #elif defined(__FreeBSD__) |
| r = sendfile(fromfd, tofd, offset, nleft, NULL, &sent, 0); |
| nleft -= sent; |
| #elif defined(__APPLE__) && defined(__MACH__) && defined(MAC_OS_X_VERSION_10_6) /* OS X */ |
| sent = nleft; |
| r = sendfile(fromfd, tofd, offset, &sent, NULL, 0); |
| nleft -= sent; |
| #else |
| /* Shouldn't happen. */ |
| r = -1; |
| errno = ENOSYS; |
| #endif |
| if (r < 0) { |
| switch (errno) { |
| case EINTR: |
| case EAGAIN: |
| #if (EAGAIN != EWOULDBLOCK) |
| case EWOULDBLOCK: |
| #endif |
| if (count == nleft) |
| return NET_SOFTERROR; |
| return count - nleft; |
| |
| case ENOBUFS: |
| case ENOMEM: |
| return NET_SOFTERROR; |
| |
| default: |
| return NET_HARDERROR; |
| } |
| } |
| #ifdef linux |
| else if (r == 0) |
| return NET_SOFTERROR; |
| #endif |
| } |
| return count; |
| #else /* HAVE_SENDFILE */ |
| errno = ENOSYS; /* error if somehow get called without HAVE_SENDFILE */ |
| return NET_HARDERROR; |
| #endif /* HAVE_SENDFILE */ |
| } |
| |
| /*************************************************************************/ |
| |
| int |
| setnonblocking(int fd, int nonblocking) |
| { |
| int flags, newflags; |
| |
| flags = fcntl(fd, F_GETFL, 0); |
| if (flags < 0) { |
| perror("fcntl(F_GETFL)"); |
| return -1; |
| } |
| if (nonblocking) |
| newflags = flags | (int) O_NONBLOCK; |
| else |
| newflags = flags & ~((int) O_NONBLOCK); |
| if (newflags != flags) |
| if (fcntl(fd, F_SETFL, newflags) < 0) { |
| perror("fcntl(F_SETFL)"); |
| return -1; |
| } |
| return 0; |
| } |
| |
| /****************************************************************************/ |
| |
| int |
| getsockdomain(int sock) |
| { |
| struct sockaddr_storage sa; |
| socklen_t len = sizeof(sa); |
| |
| if (getsockname(sock, (struct sockaddr *)&sa, &len) < 0) { |
| return -1; |
| } |
| return ((struct sockaddr *) &sa)->sa_family; |
| } |