vn-verdnaturachat/ios/Pods/Folly/folly/net/NetOps.cpp

493 lines
13 KiB
C++

/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* 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 <fcntl.h>
#include <cerrno>
#include <cstddef>
#include <folly/Portability.h>
#include <folly/net/detail/SocketFileDescriptorMap.h>
#ifdef _WIN32
#include <event2/util.h> // @manual
#include <MSWSock.h> // @manual
#include <folly/ScopeGuard.h>
#endif
namespace folly {
namespace netops {
namespace {
#ifdef _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...);
#ifdef _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 close(NetworkSocket s) {
return netops::detail::SocketFileDescriptorMap::close(s.data);
}
int connect(NetworkSocket s, const sockaddr* name, socklen_t namelen) {
auto r = wrapSocketFunction<int>(::connect, s, name, namelen);
#ifdef _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);
#ifdef _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
auto files = reinterpret_cast<pollfd*>(reinterpret_cast<void*>(fds));
#ifdef _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) {
#ifdef _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) {
#ifdef _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) {
#ifdef _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) {
#ifdef _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) {
#ifdef _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
}
int sendmmsg(
NetworkSocket socket,
mmsghdr* msgvec,
unsigned int vlen,
int flags) {
#if FOLLY_HAVE_SENDMMSG
return wrapSocketFunction<int>(::sendmmsg, socket, msgvec, vlen, flags);
#else
// implement via sendmsg
for (unsigned int i = 0; i < vlen; i++) {
ssize_t ret = sendmsg(socket, &msgvec[i].msg_hdr, flags);
// in case of an error
// we return the number of msgs sent if > 0
// or an error if no msg was sent
if (ret < 0) {
if (i) {
return static_cast<int>(i);
}
return static_cast<int>(ret);
}
}
return static_cast<int>(vlen);
#endif
}
ssize_t sendto(
NetworkSocket s,
const void* buf,
size_t len,
int flags,
const sockaddr* to,
socklen_t tolen) {
#ifdef _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) {
#ifdef _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]) {
#ifdef _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) {
#ifdef _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) {
#ifdef _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