Commit d4a7542e authored by Orvid King's avatar Orvid King Committed by Facebook Github Bot

Create a handle-type agnostic sockets API and back the Windows socket portability layer with it

Summary:
The end goal will be to have all calls to the sockets APIs go through the agnostic API rather than the current approach, as the current approach causes a limit of ~64k sockets, due to file descriptor limitations on Windows.
All this diff does for now is create a sockets API that is not backed by file descriptors on Windows.
This also shifts the sockets portability layer to be backed by these APIs until the sockets portability layer is removed.

Reviewed By: yfeldblum

Differential Revision: D10235797

fbshipit-source-id: 1c073231290da48832608f6fec272c3d675bdfd7
parent 0207d08f
/*
* Copyright 2013-present Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <folly/net/NetOps.h>
#include <errno.h>
#include <fcntl.h>
#include <cstddef>
#include <folly/Portability.h>
#if _WIN32
#include <event2/util.h> // @manual
#include <MSWSock.h> // @manual
#include <folly/ScopeGuard.h>
#endif
namespace folly {
namespace netops {
namespace {
#if _WIN32
// WSA has to be explicitly initialized.
static struct WinSockInit {
WinSockInit() {
WSADATA dat;
WSAStartup(MAKEWORD(2, 2), &dat);
}
~WinSockInit() {
WSACleanup();
}
} winsockInit;
int translate_wsa_error(int wsaErr) {
switch (wsaErr) {
case WSAEWOULDBLOCK:
return EAGAIN;
default:
return wsaErr;
}
}
#endif
template <class R, class F, class... Args>
static R wrapSocketFunction(F f, NetworkSocket s, Args... args) {
R ret = f(s.data, args...);
#if _WIN32
errno = translate_wsa_error(WSAGetLastError());
#endif
return ret;
}
} // namespace
NetworkSocket accept(NetworkSocket s, sockaddr* addr, socklen_t* addrlen) {
return NetworkSocket(wrapSocketFunction<NetworkSocket::native_handle_type>(
::accept, s, addr, addrlen));
}
int bind(NetworkSocket s, const sockaddr* name, socklen_t namelen) {
if (kIsWindows && name->sa_family == AF_UNIX) {
// Windows added support for AF_UNIX sockets, but didn't add
// support for autobind sockets, so detect requests for autobind
// sockets and treat them as invalid. (otherwise they don't trigger
// an error, but also don't actually work)
if (name->sa_data[0] == '\0') {
errno = EINVAL;
return -1;
}
}
return wrapSocketFunction<int>(::bind, s, name, namelen);
}
int connect(NetworkSocket s, const sockaddr* name, socklen_t namelen) {
auto r = wrapSocketFunction<int>(::connect, s, name, namelen);
#if _WIN32
if (r == -1 && WSAGetLastError() == WSAEWOULDBLOCK) {
errno = EINPROGRESS;
}
#endif
return r;
}
int getpeername(NetworkSocket s, sockaddr* name, socklen_t* namelen) {
return wrapSocketFunction<int>(::getpeername, s, name, namelen);
}
int getsockname(NetworkSocket s, sockaddr* name, socklen_t* namelen) {
return wrapSocketFunction<int>(::getsockname, s, name, namelen);
}
int getsockopt(
NetworkSocket s,
int level,
int optname,
void* optval,
socklen_t* optlen) {
auto ret = wrapSocketFunction<int>(
::getsockopt, s, level, optname, (char*)optval, optlen);
#if _WIN32
if (optname == TCP_NODELAY && *optlen == 1) {
// Windows is weird about this value, and documents it as a
// BOOL (ie. int) but expects the variable to be bool (1-byte),
// so we get to adapt the interface to work that way.
*(int*)optval = *(uint8_t*)optval;
*optlen = sizeof(int);
}
#endif
return ret;
}
int inet_aton(const char* cp, in_addr* inp) {
inp->s_addr = inet_addr(cp);
return inp->s_addr == INADDR_NONE ? 0 : 1;
}
int listen(NetworkSocket s, int backlog) {
return wrapSocketFunction<int>(::listen, s, backlog);
}
int poll(PollDescriptor fds[], nfds_t nfds, int timeout) {
// Make sure that PollDescriptor is byte-for-byte identical to pollfd,
// so we don't need extra allocations just for the safety of this shim.
static_assert(
alignof(PollDescriptor) == alignof(pollfd),
"PollDescriptor is misaligned");
static_assert(
sizeof(PollDescriptor) == sizeof(pollfd),
"PollDescriptor is the wrong size");
static_assert(
offsetof(PollDescriptor, fd) == offsetof(pollfd, fd),
"PollDescriptor.fd is at the wrong place");
static_assert(
sizeof(decltype(PollDescriptor().fd)) == sizeof(decltype(pollfd().fd)),
"PollDescriptor.fd is the wrong size");
static_assert(
offsetof(PollDescriptor, events) == offsetof(pollfd, events),
"PollDescriptor.events is at the wrong place");
static_assert(
sizeof(decltype(PollDescriptor().events)) ==
sizeof(decltype(pollfd().events)),
"PollDescriptor.events is the wrong size");
static_assert(
offsetof(PollDescriptor, revents) == offsetof(pollfd, revents),
"PollDescriptor.revents is at the wrong place");
static_assert(
sizeof(decltype(PollDescriptor().revents)) ==
sizeof(decltype(pollfd().revents)),
"PollDescriptor.revents is the wrong size");
// Pun it through
pollfd* files = reinterpret_cast<pollfd*>(reinterpret_cast<void*>(fds));
#if _WIN32
return ::WSAPoll(files, (ULONG)nfds, timeout);
#else
return ::poll(files, nfds, timeout);
#endif
}
ssize_t recv(NetworkSocket s, void* buf, size_t len, int flags) {
#if _WIN32
if ((flags & MSG_DONTWAIT) == MSG_DONTWAIT) {
flags &= ~MSG_DONTWAIT;
u_long pendingRead = 0;
if (ioctlsocket(s.data, FIONREAD, &pendingRead)) {
errno = translate_wsa_error(WSAGetLastError());
return -1;
}
fd_set readSet;
FD_ZERO(&readSet);
FD_SET(s.data, &readSet);
timeval timeout{0, 0};
auto ret = select(1, &readSet, nullptr, nullptr, &timeout);
if (ret == 0) {
errno = EWOULDBLOCK;
return -1;
}
}
return wrapSocketFunction<ssize_t>(::recv, s, (char*)buf, (int)len, flags);
#else
return wrapSocketFunction<ssize_t>(::recv, s, buf, len, flags);
#endif
}
ssize_t recvfrom(
NetworkSocket s,
void* buf,
size_t len,
int flags,
sockaddr* from,
socklen_t* fromlen) {
#if _WIN32
if ((flags & MSG_TRUNC) == MSG_TRUNC) {
SOCKET h = s.data;
WSABUF wBuf{};
wBuf.buf = (CHAR*)buf;
wBuf.len = (ULONG)len;
WSAMSG wMsg{};
wMsg.dwBufferCount = 1;
wMsg.lpBuffers = &wBuf;
wMsg.name = from;
if (fromlen != nullptr) {
wMsg.namelen = *fromlen;
}
// WSARecvMsg is an extension, so we don't get
// the convenience of being able to call it directly, even though
// WSASendMsg is part of the normal API -_-...
LPFN_WSARECVMSG WSARecvMsg;
GUID WSARecgMsg_GUID = WSAID_WSARECVMSG;
DWORD recMsgBytes;
WSAIoctl(
h,
SIO_GET_EXTENSION_FUNCTION_POINTER,
&WSARecgMsg_GUID,
sizeof(WSARecgMsg_GUID),
&WSARecvMsg,
sizeof(WSARecvMsg),
&recMsgBytes,
nullptr,
nullptr);
DWORD bytesReceived;
int res = WSARecvMsg(h, &wMsg, &bytesReceived, nullptr, nullptr);
errno = translate_wsa_error(WSAGetLastError());
if (res == 0) {
return bytesReceived;
}
if (fromlen != nullptr) {
*fromlen = wMsg.namelen;
}
if ((wMsg.dwFlags & MSG_TRUNC) == MSG_TRUNC) {
return wBuf.len + 1;
}
return -1;
}
return wrapSocketFunction<ssize_t>(
::recvfrom, s, (char*)buf, (int)len, flags, from, fromlen);
#else
return wrapSocketFunction<ssize_t>(
::recvfrom, s, buf, len, flags, from, fromlen);
#endif
}
ssize_t recvmsg(NetworkSocket s, msghdr* message, int flags) {
#if _WIN32
(void)flags;
SOCKET h = s.data;
// Don't currently support the name translation.
if (message->msg_name != nullptr || message->msg_namelen != 0) {
return (ssize_t)-1;
}
WSAMSG msg;
msg.name = nullptr;
msg.namelen = 0;
msg.Control.buf = (CHAR*)message->msg_control;
msg.Control.len = (ULONG)message->msg_controllen;
msg.dwFlags = 0;
msg.dwBufferCount = (DWORD)message->msg_iovlen;
msg.lpBuffers = new WSABUF[message->msg_iovlen];
SCOPE_EXIT {
delete[] msg.lpBuffers;
};
for (size_t i = 0; i < message->msg_iovlen; i++) {
msg.lpBuffers[i].buf = (CHAR*)message->msg_iov[i].iov_base;
msg.lpBuffers[i].len = (ULONG)message->msg_iov[i].iov_len;
}
// WSARecvMsg is an extension, so we don't get
// the convenience of being able to call it directly, even though
// WSASendMsg is part of the normal API -_-...
LPFN_WSARECVMSG WSARecvMsg;
GUID WSARecgMsg_GUID = WSAID_WSARECVMSG;
DWORD recMsgBytes;
WSAIoctl(
h,
SIO_GET_EXTENSION_FUNCTION_POINTER,
&WSARecgMsg_GUID,
sizeof(WSARecgMsg_GUID),
&WSARecvMsg,
sizeof(WSARecvMsg),
&recMsgBytes,
nullptr,
nullptr);
DWORD bytesReceived;
int res = WSARecvMsg(h, &msg, &bytesReceived, nullptr, nullptr);
errno = translate_wsa_error(WSAGetLastError());
return res == 0 ? (ssize_t)bytesReceived : -1;
#else
return wrapSocketFunction<ssize_t>(::recvmsg, s, message, flags);
#endif
}
ssize_t send(NetworkSocket s, const void* buf, size_t len, int flags) {
#if _WIN32
return wrapSocketFunction<ssize_t>(
::send, s, (const char*)buf, (int)len, flags);
#else
return wrapSocketFunction<ssize_t>(::send, s, buf, len, flags);
#endif
}
ssize_t sendmsg(NetworkSocket socket, const msghdr* message, int flags) {
#if _WIN32
(void)flags;
SOCKET h = socket.data;
// Unfortunately, WSASendMsg requires the socket to have been opened
// as either SOCK_DGRAM or SOCK_RAW, but sendmsg has no such requirement,
// so we have to implement it based on send instead :(
ssize_t bytesSent = 0;
for (size_t i = 0; i < message->msg_iovlen; i++) {
int r = -1;
if (message->msg_name != nullptr) {
r = ::sendto(
h,
(const char*)message->msg_iov[i].iov_base,
(int)message->msg_iov[i].iov_len,
message->msg_flags,
(const sockaddr*)message->msg_name,
(int)message->msg_namelen);
} else {
r = ::send(
h,
(const char*)message->msg_iov[i].iov_base,
(int)message->msg_iov[i].iov_len,
message->msg_flags);
}
if (r == -1 || size_t(r) != message->msg_iov[i].iov_len) {
errno = translate_wsa_error(WSAGetLastError());
if (WSAGetLastError() == WSAEWOULDBLOCK && bytesSent > 0) {
return bytesSent;
}
return -1;
}
bytesSent += r;
}
return bytesSent;
#else
return wrapSocketFunction<ssize_t>(::sendmsg, socket, message, flags);
#endif
}
ssize_t sendto(
NetworkSocket s,
const void* buf,
size_t len,
int flags,
const sockaddr* to,
socklen_t tolen) {
#if _WIN32
return wrapSocketFunction<ssize_t>(
::sendto, s, (const char*)buf, (int)len, flags, to, (int)tolen);
#else
return wrapSocketFunction<ssize_t>(::sendto, s, buf, len, flags, to, tolen);
#endif
}
int setsockopt(
NetworkSocket s,
int level,
int optname,
const void* optval,
socklen_t optlen) {
#if _WIN32
if (optname == SO_REUSEADDR) {
// We don't have an equivelent to the Linux & OSX meaning of this
// on Windows, so ignore it.
return 0;
} else if (optname == SO_REUSEPORT) {
// Windows's SO_REUSEADDR option is closer to SO_REUSEPORT than
// it is to the Linux & OSX meaning of SO_REUSEADDR.
return -1;
}
return wrapSocketFunction<int>(
::setsockopt, s, level, optname, (char*)optval, optlen);
#else
return wrapSocketFunction<int>(
::setsockopt, s, level, optname, optval, optlen);
#endif
}
int shutdown(NetworkSocket s, int how) {
return wrapSocketFunction<int>(::shutdown, s, how);
}
NetworkSocket socket(int af, int type, int protocol) {
return NetworkSocket(::socket(af, type, protocol));
}
int socketpair(int domain, int type, int protocol, NetworkSocket sv[2]) {
#if _WIN32
if (domain != PF_UNIX || type != SOCK_STREAM || protocol != 0) {
return -1;
}
intptr_t pair[2];
auto r = evutil_socketpair(AF_INET, type, protocol, pair);
if (r == -1) {
return r;
}
sv[0] = NetworkSocket(static_cast<SOCKET>(pair[0]));
sv[1] = NetworkSocket(static_cast<SOCKET>(pair[1]));
return r;
#else
int pair[2];
auto r = ::socketpair(domain, type, protocol, pair);
if (r == -1) {
return r;
}
sv[0] = NetworkSocket(pair[0]);
sv[1] = NetworkSocket(pair[1]);
return r;
#endif
}
int set_socket_non_blocking(NetworkSocket s) {
#if _WIN32
u_long nonBlockingEnabled = 1;
return ioctlsocket(s.data, FIONBIO, &nonBlockingEnabled);
#else
int flags = fcntl(s.data, F_GETFL, 0);
if (flags == -1) {
return -1;
}
return fcntl(s.data, F_SETFL, flags | O_NONBLOCK);
#endif
}
int set_socket_close_on_exec(NetworkSocket s) {
#if _WIN32
if (SetHandleInformation((HANDLE)s.data, HANDLE_FLAG_INHERIT, 0)) {
return 0;
}
return -1;
#else
return fcntl(s.data, F_SETFD, FD_CLOEXEC);
#endif
}
} // namespace netops
} // namespace folly
/*
* Copyright 2013-present Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <cstdint>
#include <folly/net/NetworkSocket.h>
#include <folly/portability/IOVec.h>
#include <folly/portability/SysTypes.h>
#include <folly/portability/Windows.h>
#ifndef _WIN32
#include <netdb.h>
#include <poll.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/socket.h>
#include <sys/un.h>
#ifdef MSG_ERRQUEUE
#define FOLLY_HAVE_MSG_ERRQUEUE 1
/* for struct sock_extended_err*/
#include <linux/errqueue.h>
#endif
#ifndef SO_EE_ORIGIN_ZEROCOPY
#define SO_EE_ORIGIN_ZEROCOPY 5
#endif
#ifndef SO_EE_CODE_ZEROCOPY_COPIED
#define SO_EE_CODE_ZEROCOPY_COPIED 1
#endif
#ifndef SO_ZEROCOPY
#define SO_ZEROCOPY 60
#endif
#ifndef MSG_ZEROCOPY
#define MSG_ZEROCOPY 0x4000000
#endif
#ifndef SOL_UDP
#define SOL_UDP 17
#endif
#ifndef ETH_MAX_MTU
#define ETH_MAX_MTU 0xFFFFU
#endif
#ifndef UDP_SEGMENT
#define UDP_SEGMENT 103
#endif
#ifndef UDP_MAX_SEGMENTS
#define UDP_MAX_SEGMENTS (1 << 6UL)
#endif
#else
#include <WS2tcpip.h> // @manual
using nfds_t = int;
using sa_family_t = ADDRESS_FAMILY;
// these are not supported
#define SO_EE_ORIGIN_ZEROCOPY 0
#define SO_ZEROCOPY 0
#define MSG_ZEROCOPY 0x0
#define SOL_UDP 0x0
#define UDP_SEGMENT 0x0
// We don't actually support either of these flags
// currently.
#define MSG_DONTWAIT 0x1000
#define MSG_EOR 0
struct msghdr {
void* msg_name;
socklen_t msg_namelen;
struct iovec* msg_iov;
size_t msg_iovlen;
void* msg_control;
size_t msg_controllen;
int msg_flags;
};
struct sockaddr_un {
sa_family_t sun_family;
char sun_path[108];
};
#define SHUT_RD SD_RECEIVE
#define SHUT_WR SD_SEND
#define SHUT_RDWR SD_BOTH
// These are the same, but PF_LOCAL
// isn't defined by WinSock.
#define AF_LOCAL PF_UNIX
#define PF_LOCAL PF_UNIX
// This isn't defined by Windows, and we need to
// distinguish it from SO_REUSEADDR
#define SO_REUSEPORT 0x7001
// Someone thought it would be a good idea
// to define a field via a macro...
#undef s_host
#endif
namespace folly {
namespace netops {
// Poll descriptor is intended to be byte-for-byte identical to pollfd,
// except that it is typed as containing a NetworkSocket for sane interactions.
struct PollDescriptor {
NetworkSocket fd;
int16_t events;
int16_t revents;
};
NetworkSocket accept(NetworkSocket s, sockaddr* addr, socklen_t* addrlen);
int bind(NetworkSocket s, const sockaddr* name, socklen_t namelen);
int connect(NetworkSocket s, const sockaddr* name, socklen_t namelen);
int getpeername(NetworkSocket s, sockaddr* name, socklen_t* namelen);
int getsockname(NetworkSocket s, sockaddr* name, socklen_t* namelen);
int getsockopt(
NetworkSocket s,
int level,
int optname,
void* optval,
socklen_t* optlen);
int inet_aton(const char* cp, in_addr* inp);
int listen(NetworkSocket s, int backlog);
int poll(PollDescriptor fds[], nfds_t nfds, int timeout);
ssize_t recv(NetworkSocket s, void* buf, size_t len, int flags);
ssize_t recvfrom(
NetworkSocket s,
void* buf,
size_t len,
int flags,
sockaddr* from,
socklen_t* fromlen);
ssize_t recvmsg(NetworkSocket s, msghdr* message, int flags);
ssize_t send(NetworkSocket s, const void* buf, size_t len, int flags);
ssize_t sendto(
NetworkSocket s,
const void* buf,
size_t len,
int flags,
const sockaddr* to,
socklen_t tolen);
ssize_t sendmsg(NetworkSocket socket, const msghdr* message, int flags);
int setsockopt(
NetworkSocket s,
int level,
int optname,
const void* optval,
socklen_t optlen);
int shutdown(NetworkSocket s, int how);
NetworkSocket socket(int af, int type, int protocol);
int socketpair(int domain, int type, int protocol, NetworkSocket sv[2]);
// And now we diverge from the Posix way of doing things and just do things
// our own way.
int set_socket_non_blocking(NetworkSocket s);
int set_socket_close_on_exec(NetworkSocket s);
} // namespace netops
} // namespace folly
/*
* Copyright 2013-present Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <ostream>
#include <folly/portability/Windows.h>
namespace folly {
/**
* This is just a very thin wrapper around either a file descriptor or
* a SOCKET depending on platform, along with a couple of helper methods
* for explicitly converting to/from file descriptors, even on Windows.
*/
struct NetworkSocket {
#if _WIN32
using native_handle_type = SOCKET;
static constexpr native_handle_type invalid_handle_value = INVALID_SOCKET;
#else
using native_handle_type = int;
static constexpr native_handle_type invalid_handle_value = -1;
#endif
native_handle_type data;
constexpr NetworkSocket() : data(invalid_handle_value) {}
constexpr explicit NetworkSocket(native_handle_type d) : data(d) {}
friend constexpr bool operator==(
const NetworkSocket& a,
const NetworkSocket& b) noexcept {
return a.data == b.data;
}
friend constexpr bool operator!=(
const NetworkSocket& a,
const NetworkSocket& b) noexcept {
return !(a == b);
}
};
template <class CharT, class Traits>
inline std::basic_ostream<CharT, Traits>& operator<<(
std::basic_ostream<CharT, Traits>& os,
const NetworkSocket& addr) {
os << "folly::NetworkSocket(" << addr.data << ")";
return os;
}
} // namespace folly
...@@ -21,26 +21,24 @@ ...@@ -21,26 +21,24 @@
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <event2/util.h> // @manual
#include <MSWSock.h> // @manual #include <MSWSock.h> // @manual
#include <folly/ScopeGuard.h> #include <folly/ScopeGuard.h>
#include <folly/net/NetworkSocket.h>
namespace folly { namespace folly {
namespace portability { namespace portability {
namespace sockets { namespace sockets {
// We have to startup WSA. namespace {
static struct FSPInit { int network_socket_to_fd(NetworkSocket sock) {
FSPInit() { return socket_to_fd(sock.data);
WSADATA dat; }
WSAStartup(MAKEWORD(2, 2), &dat);
} NetworkSocket fd_to_network_socket(int fd) {
~FSPInit() { return NetworkSocket(fd_to_socket(fd));
WSACleanup(); }
} } // namespace
} fspInit;
bool is_fh_socket(int fh) { bool is_fh_socket(int fh) {
SOCKET h = fd_to_socket(fh); SOCKET h = fd_to_socket(fh);
...@@ -72,45 +70,31 @@ int socket_to_fd(SOCKET s) { ...@@ -72,45 +70,31 @@ int socket_to_fd(SOCKET s) {
return _open_osfhandle((intptr_t)s, O_RDWR | O_BINARY); return _open_osfhandle((intptr_t)s, O_RDWR | O_BINARY);
} }
int translate_wsa_error(int wsaErr) {
switch (wsaErr) {
case WSAEWOULDBLOCK:
return EAGAIN;
default:
return wsaErr;
}
}
template <class R, class F, class... Args> template <class R, class F, class... Args>
static R wrapSocketFunction(F f, int s, Args... args) { static R wrapSocketFunction(F f, int s, Args... args) {
SOCKET h = fd_to_socket(s); NetworkSocket h = fd_to_network_socket(s);
R ret = f(h, args...); return f(h, args...);
errno = translate_wsa_error(WSAGetLastError());
return ret;
} }
int accept(int s, struct sockaddr* addr, socklen_t* addrlen) { int accept(int s, struct sockaddr* addr, socklen_t* addrlen) {
return socket_to_fd(wrapSocketFunction<SOCKET>(::accept, s, addr, addrlen)); return network_socket_to_fd(
wrapSocketFunction<NetworkSocket>(netops::accept, s, addr, addrlen));
} }
int bind(int s, const struct sockaddr* name, socklen_t namelen) { int bind(int s, const struct sockaddr* name, socklen_t namelen) {
return wrapSocketFunction<int>(::bind, s, name, namelen); return wrapSocketFunction<int>(netops::bind, s, name, namelen);
} }
int connect(int s, const struct sockaddr* name, socklen_t namelen) { int connect(int s, const struct sockaddr* name, socklen_t namelen) {
auto r = wrapSocketFunction<int>(::connect, s, name, namelen); return wrapSocketFunction<int>(netops::connect, s, name, namelen);
if (r == -1 && WSAGetLastError() == WSAEWOULDBLOCK) {
errno = EINPROGRESS;
}
return r;
} }
int getpeername(int s, struct sockaddr* name, socklen_t* namelen) { int getpeername(int s, struct sockaddr* name, socklen_t* namelen) {
return wrapSocketFunction<int>(::getpeername, s, name, namelen); return wrapSocketFunction<int>(netops::getpeername, s, name, namelen);
} }
int getsockname(int s, struct sockaddr* name, socklen_t* namelen) { int getsockname(int s, struct sockaddr* name, socklen_t* namelen) {
return wrapSocketFunction<int>(::getsockname, s, name, namelen); return wrapSocketFunction<int>(netops::getsockname, s, name, namelen);
} }
int getsockopt(int s, int level, int optname, char* optval, socklen_t* optlen) { int getsockopt(int s, int level, int optname, char* optval, socklen_t* optlen) {
...@@ -118,21 +102,12 @@ int getsockopt(int s, int level, int optname, char* optval, socklen_t* optlen) { ...@@ -118,21 +102,12 @@ int getsockopt(int s, int level, int optname, char* optval, socklen_t* optlen) {
} }
int getsockopt(int s, int level, int optname, void* optval, socklen_t* optlen) { int getsockopt(int s, int level, int optname, void* optval, socklen_t* optlen) {
auto ret = wrapSocketFunction<int>( return wrapSocketFunction<int>(
::getsockopt, s, level, optname, (char*)optval, (int*)optlen); netops::getsockopt, s, level, optname, optval, optlen);
if (optname == TCP_NODELAY && *optlen == 1) {
// Windows is weird about this value, and documents it as a
// BOOL (ie. int) but expects the variable to be bool (1-byte),
// so we get to adapt the interface to work that way.
*(int*)optval = *(uint8_t*)optval;
*optlen = sizeof(int);
}
return ret;
} }
int inet_aton(const char* cp, struct in_addr* inp) { int inet_aton(const char* cp, struct in_addr* inp) {
inp->s_addr = inet_addr(cp); return netops::inet_aton(cp, inp);
return inp->s_addr == INADDR_NONE ? 0 : 1;
} }
const char* inet_ntop(int af, const void* src, char* dst, socklen_t size) { const char* inet_ntop(int af, const void* src, char* dst, socklen_t size) {
...@@ -140,38 +115,21 @@ const char* inet_ntop(int af, const void* src, char* dst, socklen_t size) { ...@@ -140,38 +115,21 @@ const char* inet_ntop(int af, const void* src, char* dst, socklen_t size) {
} }
int listen(int s, int backlog) { int listen(int s, int backlog) {
return wrapSocketFunction<int>(::listen, s, backlog); return wrapSocketFunction<int>(netops::listen, s, backlog);
} }
int poll(struct pollfd fds[], nfds_t nfds, int timeout) { int poll(struct pollfd fds[], nfds_t nfds, int timeout) {
// TODO: Allow both file descriptors and SOCKETs in this. // NetOps already has the checks to ensure this is safe.
for (int i = 0; i < nfds; i++) { netops::PollDescriptor* desc =
fds[i].fd = fd_to_socket((int)fds[i].fd); reinterpret_cast<netops::PollDescriptor*>(reinterpret_cast<void*>(fds));
for (nfds_t i = 0; i < nfds; ++i) {
desc[i].fd = fd_to_network_socket((int)desc[i].fd.data);
} }
return ::WSAPoll(fds, (ULONG)nfds, timeout); return netops::poll(desc, nfds, timeout);
} }
ssize_t recv(int s, void* buf, size_t len, int flags) { ssize_t recv(int s, void* buf, size_t len, int flags) {
if ((flags & MSG_DONTWAIT) == MSG_DONTWAIT) { return wrapSocketFunction<ssize_t>(netops::recv, s, buf, len, flags);
flags &= ~MSG_DONTWAIT;
u_long pendingRead = 0;
if (ioctlsocket(fd_to_socket(s), FIONREAD, &pendingRead)) {
errno = translate_wsa_error(WSAGetLastError());
return -1;
}
fd_set readSet;
FD_ZERO(&readSet);
FD_SET(fd_to_socket(s), &readSet);
timeval timeout{0, 0};
auto ret = select(1, &readSet, nullptr, nullptr, &timeout);
if (ret == 0) {
errno = EWOULDBLOCK;
return -1;
}
}
return wrapSocketFunction<ssize_t>(::recv, s, (char*)buf, (int)len, flags);
} }
ssize_t recv(int s, char* buf, int len, int flags) { ssize_t recv(int s, char* buf, int len, int flags) {
...@@ -189,53 +147,8 @@ ssize_t recvfrom( ...@@ -189,53 +147,8 @@ ssize_t recvfrom(
int flags, int flags,
struct sockaddr* from, struct sockaddr* from,
socklen_t* fromlen) { socklen_t* fromlen) {
if ((flags & MSG_TRUNC) == MSG_TRUNC) {
SOCKET h = fd_to_socket(s);
WSABUF wBuf{};
wBuf.buf = (CHAR*)buf;
wBuf.len = (ULONG)len;
WSAMSG wMsg{};
wMsg.dwBufferCount = 1;
wMsg.lpBuffers = &wBuf;
wMsg.name = from;
if (fromlen != nullptr) {
wMsg.namelen = *fromlen;
}
// WSARecvMsg is an extension, so we don't get
// the convenience of being able to call it directly, even though
// WSASendMsg is part of the normal API -_-...
LPFN_WSARECVMSG WSARecvMsg;
GUID WSARecgMsg_GUID = WSAID_WSARECVMSG;
DWORD recMsgBytes;
WSAIoctl(
h,
SIO_GET_EXTENSION_FUNCTION_POINTER,
&WSARecgMsg_GUID,
sizeof(WSARecgMsg_GUID),
&WSARecvMsg,
sizeof(WSARecvMsg),
&recMsgBytes,
nullptr,
nullptr);
DWORD bytesReceived;
int res = WSARecvMsg(h, &wMsg, &bytesReceived, nullptr, nullptr);
errno = translate_wsa_error(WSAGetLastError());
if (res == 0) {
return bytesReceived;
}
if (fromlen != nullptr) {
*fromlen = wMsg.namelen;
}
if ((wMsg.dwFlags & MSG_TRUNC) == MSG_TRUNC) {
return wBuf.len + 1;
}
return -1;
}
return wrapSocketFunction<ssize_t>( return wrapSocketFunction<ssize_t>(
::recvfrom, s, (char*)buf, (int)len, flags, from, (int*)fromlen); netops::recvfrom, s, buf, len, flags, from, fromlen);
} }
ssize_t recvfrom( ssize_t recvfrom(
...@@ -258,55 +171,12 @@ ssize_t recvfrom( ...@@ -258,55 +171,12 @@ ssize_t recvfrom(
return recvfrom(s, (void*)buf, (size_t)len, flags, from, fromlen); return recvfrom(s, (void*)buf, (size_t)len, flags, from, fromlen);
} }
ssize_t recvmsg(int s, struct msghdr* message, int /* flags */) { ssize_t recvmsg(int s, struct msghdr* message, int flags) {
SOCKET h = fd_to_socket(s); return wrapSocketFunction<ssize_t>(netops::recvmsg, s, message, flags);
// Don't currently support the name translation.
if (message->msg_name != nullptr || message->msg_namelen != 0) {
return (ssize_t)-1;
}
WSAMSG msg;
msg.name = nullptr;
msg.namelen = 0;
msg.Control.buf = (CHAR*)message->msg_control;
msg.Control.len = (ULONG)message->msg_controllen;
msg.dwFlags = 0;
msg.dwBufferCount = (DWORD)message->msg_iovlen;
msg.lpBuffers = new WSABUF[message->msg_iovlen];
SCOPE_EXIT {
delete[] msg.lpBuffers;
};
for (size_t i = 0; i < message->msg_iovlen; i++) {
msg.lpBuffers[i].buf = (CHAR*)message->msg_iov[i].iov_base;
msg.lpBuffers[i].len = (ULONG)message->msg_iov[i].iov_len;
}
// WSARecvMsg is an extension, so we don't get
// the convenience of being able to call it directly, even though
// WSASendMsg is part of the normal API -_-...
LPFN_WSARECVMSG WSARecvMsg;
GUID WSARecgMsg_GUID = WSAID_WSARECVMSG;
DWORD recMsgBytes;
WSAIoctl(
h,
SIO_GET_EXTENSION_FUNCTION_POINTER,
&WSARecgMsg_GUID,
sizeof(WSARecgMsg_GUID),
&WSARecvMsg,
sizeof(WSARecvMsg),
&recMsgBytes,
nullptr,
nullptr);
DWORD bytesReceived;
int res = WSARecvMsg(h, &msg, &bytesReceived, nullptr, nullptr);
errno = translate_wsa_error(WSAGetLastError());
return res == 0 ? (ssize_t)bytesReceived : -1;
} }
ssize_t send(int s, const void* buf, size_t len, int flags) { ssize_t send(int s, const void* buf, size_t len, int flags) {
return wrapSocketFunction<ssize_t>( return wrapSocketFunction<ssize_t>(netops::send, s, buf, len, flags);
::send, s, (const char*)buf, (int)len, flags);
} }
ssize_t send(int s, const char* buf, int len, int flags) { ssize_t send(int s, const char* buf, int len, int flags) {
...@@ -317,40 +187,8 @@ ssize_t send(int s, const void* buf, int len, int flags) { ...@@ -317,40 +187,8 @@ ssize_t send(int s, const void* buf, int len, int flags) {
return send(s, (const void*)buf, (size_t)len, flags); return send(s, (const void*)buf, (size_t)len, flags);
} }
ssize_t sendmsg(int s, const struct msghdr* message, int /* flags */) { ssize_t sendmsg(int s, const struct msghdr* message, int flags) {
SOCKET h = fd_to_socket(s); return wrapSocketFunction<ssize_t>(netops::sendmsg, s, message, flags);
// Unfortunately, WSASendMsg requires the socket to have been opened
// as either SOCK_DGRAM or SOCK_RAW, but sendmsg has no such requirement,
// so we have to implement it based on send instead :(
ssize_t bytesSent = 0;
for (size_t i = 0; i < message->msg_iovlen; i++) {
int r = -1;
if (message->msg_name != nullptr) {
r = ::sendto(
h,
(const char*)message->msg_iov[i].iov_base,
(int)message->msg_iov[i].iov_len,
message->msg_flags,
(const sockaddr*)message->msg_name,
(int)message->msg_namelen);
} else {
r = ::send(
h,
(const char*)message->msg_iov[i].iov_base,
(int)message->msg_iov[i].iov_len,
message->msg_flags);
}
if (r == -1 || size_t(r) != message->msg_iov[i].iov_len) {
errno = translate_wsa_error(WSAGetLastError());
if (WSAGetLastError() == WSAEWOULDBLOCK && bytesSent > 0) {
return bytesSent;
}
return -1;
}
bytesSent += r;
}
return bytesSent;
} }
ssize_t sendto( ssize_t sendto(
...@@ -361,7 +199,7 @@ ssize_t sendto( ...@@ -361,7 +199,7 @@ ssize_t sendto(
const sockaddr* to, const sockaddr* to,
socklen_t tolen) { socklen_t tolen) {
return wrapSocketFunction<ssize_t>( return wrapSocketFunction<ssize_t>(
::sendto, s, (const char*)buf, (int)len, flags, to, (int)tolen); netops::sendto, s, buf, len, flags, to, tolen);
} }
ssize_t sendto( ssize_t sendto(
...@@ -390,17 +228,8 @@ int setsockopt( ...@@ -390,17 +228,8 @@ int setsockopt(
int optname, int optname,
const void* optval, const void* optval,
socklen_t optlen) { socklen_t optlen) {
if (optname == SO_REUSEADDR) {
// We don't have an equivelent to the Linux & OSX meaning of this
// on Windows, so ignore it.
return 0;
} else if (optname == SO_REUSEPORT) {
// Windows's SO_REUSEADDR option is closer to SO_REUSEPORT than
// it is to the Linux & OSX meaning of SO_REUSEADDR.
return -1;
}
return wrapSocketFunction<int>( return wrapSocketFunction<int>(
::setsockopt, s, level, optname, (char*)optval, optlen); netops::setsockopt, s, level, optname, optval, optlen);
} }
int setsockopt( int setsockopt(
...@@ -413,24 +242,23 @@ int setsockopt( ...@@ -413,24 +242,23 @@ int setsockopt(
} }
int shutdown(int s, int how) { int shutdown(int s, int how) {
return wrapSocketFunction<int>(::shutdown, s, how); return wrapSocketFunction<int>(netops::shutdown, s, how);
} }
int socket(int af, int type, int protocol) { int socket(int af, int type, int protocol) {
return socket_to_fd(::socket(af, type, protocol)); return network_socket_to_fd(netops::socket(af, type, protocol));
} }
int socketpair(int domain, int type, int protocol, int sv[2]) { int socketpair(int domain, int type, int protocol, int sv[2]) {
if (domain != PF_UNIX || type != SOCK_STREAM || protocol != 0) { NetworkSocket pair[2];
return -1; auto r = netops::socketpair(domain, type, protocol, pair);
}
intptr_t pair[2];
auto r = evutil_socketpair(AF_INET, type, protocol, pair);
if (r == -1) { if (r == -1) {
return r; return r;
} }
sv[0] = _open_osfhandle(pair[0], O_RDWR | O_BINARY); sv[0] =
sv[1] = _open_osfhandle(pair[1], O_RDWR | O_BINARY); _open_osfhandle(static_cast<intptr_t>(pair[0].data), O_RDWR | O_BINARY);
sv[1] =
_open_osfhandle(static_cast<intptr_t>(pair[1].data), O_RDWR | O_BINARY);
return 0; return 0;
} }
} // namespace sockets } // namespace sockets
......
...@@ -16,107 +16,7 @@ ...@@ -16,107 +16,7 @@
#pragma once #pragma once
#ifndef _WIN32 #include <folly/net/NetOps.h>
#include <netdb.h>
#include <poll.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/socket.h>
#include <sys/un.h>
#ifdef MSG_ERRQUEUE
#define FOLLY_HAVE_MSG_ERRQUEUE 1
/* for struct sock_extended_err*/
#include <linux/errqueue.h>
#endif
#ifndef SO_EE_ORIGIN_ZEROCOPY
#define SO_EE_ORIGIN_ZEROCOPY 5
#endif
#ifndef SO_EE_CODE_ZEROCOPY_COPIED
#define SO_EE_CODE_ZEROCOPY_COPIED 1
#endif
#ifndef SO_ZEROCOPY
#define SO_ZEROCOPY 60
#endif
#ifndef MSG_ZEROCOPY
#define MSG_ZEROCOPY 0x4000000
#endif
#ifndef SOL_UDP
#define SOL_UDP 17
#endif
#ifndef ETH_MAX_MTU
#define ETH_MAX_MTU 0xFFFFU
#endif
#ifndef UDP_SEGMENT
#define UDP_SEGMENT 103
#endif
#ifndef UDP_MAX_SEGMENTS
#define UDP_MAX_SEGMENTS (1 << 6UL)
#endif
#else
#include <folly/portability/IOVec.h>
#include <folly/portability/SysTypes.h>
#include <folly/portability/Windows.h>
#include <WS2tcpip.h> // @manual
using nfds_t = int;
using sa_family_t = ADDRESS_FAMILY;
// these are not supported
#define SO_EE_ORIGIN_ZEROCOPY 0
#define SO_ZEROCOPY 0
#define MSG_ZEROCOPY 0x0
#define SOL_UDP 0x0
#define UDP_SEGMENT 0x0
// We don't actually support either of these flags
// currently.
#define MSG_DONTWAIT 0x1000
#define MSG_EOR 0
struct msghdr {
void* msg_name;
socklen_t msg_namelen;
struct iovec* msg_iov;
size_t msg_iovlen;
void* msg_control;
size_t msg_controllen;
int msg_flags;
};
struct sockaddr_un {
sa_family_t sun_family;
char sun_path[108];
};
#define SHUT_RD SD_RECEIVE
#define SHUT_WR SD_SEND
#define SHUT_RDWR SD_BOTH
// These are the same, but PF_LOCAL
// isn't defined by WinSock.
#define AF_LOCAL PF_UNIX
#define PF_LOCAL PF_UNIX
// This isn't defined by Windows, and we need to
// distinguish it from SO_REUSEADDR
#define SO_REUSEPORT 0x7001
// Someone thought it would be a good idea
// to define a field via a macro...
#undef s_host
#endif
namespace folly { namespace folly {
namespace portability { namespace portability {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment