folly/folly/net/NetOps.cpp

/*
 * Copyright (c) Meta Platforms, Inc. and 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>

#ifdef __EMSCRIPTEN__
#include <cassert>
#endif

#include <cstddef>
#include <stdexcept>

#include <folly/CPortability.h>
#include <folly/ScopeGuard.h>
#include <folly/Utility.h>
#include <folly/net/detail/SocketFileDescriptorMap.h>

#ifdef _WIN32
#include <MSWSock.h> // @manual
#endif

#if (defined(__linux__) && !defined(__ANDROID__)) ||                       \
    (defined(__ANDROID__) && __ANDROID_API__ >= 21 /* released 2014 */) || \
    defined(__FreeBSD__) || defined(__SGX__) || defined(__EMSCRIPTEN__)
static_assert(folly::to_bool(::recvmmsg));
static_assert(folly::to_bool(::sendmmsg));
#else
static int (*recvmmsg)(...) =;
static int (*sendmmsg)(...) =;
#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;

static int wsa_error_translator_base(
    NetworkSocket, intptr_t, intptr_t, int wsaErr) {
  switch (wsaErr) {
    case WSAEWOULDBLOCK:
      return EAGAIN;
    default:
      return wsaErr;
  }
}

wsa_error_translator_ptr wsa_error_translator = wsa_error_translator_base;

#define translate_wsa_error

#endif

template <class R, class F, class... Args>
static R wrapSocketFunction(F f, NetworkSocket s, Args... args) {}
} // namespace

#ifdef _WIN32
void set_wsa_error_translator(
    wsa_error_translator_ptr translator,
    wsa_error_translator_ptr* previousOut) {
  // Do an atomic swap of the error translator, ensuring we've filled in the
  // previous one first so they can be safely daisy changed/called, e.g.
  // translator may want to call the previous one and a call to it may come in
  // even before we return.
  PVOID result = nullptr;
  do {
    *previousOut = wsa_error_translator;
    result = InterlockedCompareExchangePointer(
        reinterpret_cast<PVOID volatile*>(&wsa_error_translator),
        reinterpret_cast<PVOID>(translator),
        reinterpret_cast<PVOID>(*previousOut));
  } while (result != reinterpret_cast<PVOID>(*previousOut));
}
#endif

NetworkSocket accept(NetworkSocket s, sockaddr* addr, socklen_t* addrlen) {}

int bind(NetworkSocket s, const sockaddr* name, socklen_t namelen) {}

int close(NetworkSocket s) {}

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) {}

#ifdef _WIN32
ssize_t wsaRecvMesg(NetworkSocket s, WSAMSG* wsaMsg) {
  SOCKET h = s.data;

  // 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);

  // Attempt to disable ICMP behavior which kills the socket.
  BOOL connReset = false;
  DWORD bytesReturned = 0;
  WSAIoctl(
      h,
      SIO_UDP_CONNRESET,
      &connReset,
      sizeof(connReset),
      nullptr,
      0,
      &bytesReturned,
      nullptr,
      nullptr);

  DWORD bytesReceived;
  int res = WSARecvMsg(h, wsaMsg, &bytesReceived, nullptr, nullptr);
  errno = translate_wsa_error(WSAGetLastError(), s, WSARecvMsg, res);

  if (res == 0) {
    return bytesReceived;
  }
  if ((wsaMsg->dwFlags & MSG_TRUNC) == MSG_TRUNC) {
    return wsaMsg->lpBuffers[0].len + 1;
  }
  return -1;
}
#endif

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) {}

int recvmmsg(
    NetworkSocket s,
    mmsghdr* msgvec,
    unsigned int vlen,
    unsigned int flags,
    timespec* timeout) {}

ssize_t send(NetworkSocket s, const void* buf, size_t len, int flags) {}

[[maybe_unused]] static ssize_t fakeSendmsg(
    [[maybe_unused]] NetworkSocket socket,
    [[maybe_unused]] const msghdr* message) {}

#ifdef _WIN32
[[maybe_unused]] ssize_t wsaSendMsgDirect(
    [[maybe_unused]] NetworkSocket socket, [[maybe_unused]] WSAMSG* msg) {
  // WSASendMsg freaks out if this pointer is not set to null but length is 0.
  if (msg->Control.len == 0) {
    msg->Control.buf = nullptr;
  }
  SOCKET h = socket.data;
  DWORD bytesSent;
  auto ret = WSASendMsg(h, msg, 0, &bytesSent, nullptr, nullptr);
  errno = translate_wsa_error(WSAGetLastError(), socket, WSASendMsg, ret);
  return ret == 0 ? (ssize_t)bytesSent : -1;
}
#endif

[[maybe_unused]] static ssize_t wsaSendMsg(
    [[maybe_unused]] NetworkSocket socket,
    [[maybe_unused]] const msghdr* message,
    [[maybe_unused]] int flags) {}

ssize_t sendmsg(NetworkSocket socket, const msghdr* message, int flags) {}

int sendmmsg(
    NetworkSocket socket, mmsghdr* msgvec, unsigned int vlen, int flags) {}

ssize_t sendto(
    NetworkSocket s,
    const void* buf,
    size_t len,
    int flags,
    const sockaddr* to,
    socklen_t tolen) {}

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) {}

#ifdef _WIN32

//  adapted from like code in libevent, itself adapted from like code in tor
//
//  from: https://github.com/libevent/libevent/tree/release-2.1.12-stable
//  license: 3-Clause BSD
static int socketpair_win32(
    int family, int type, int protocol, intptr_t fd[2]) {
  intptr_t listener = -1;
  intptr_t connector = -1;
  intptr_t acceptor = -1;
  struct sockaddr_in listen_addr;
  struct sockaddr_in connect_addr;
  int size;
  int saved_errno = -1;
  int family_test;

  family_test = family != AF_INET && (family != AF_UNIX);
  if (protocol || family_test) {
    WSASetLastError(WSAEAFNOSUPPORT);
    return -1;
  }

  if (!fd) {
    WSASetLastError(WSAEINVAL);
    return -1;
  }

  listener = ::socket(AF_INET, type, 0);
  if (listener < 0) {
    return -1;
  }
  memset(&listen_addr, 0, sizeof(listen_addr));
  listen_addr.sin_family = AF_INET;
  listen_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
  listen_addr.sin_port = 0; /* kernel chooses port.   */
  if (::bind(listener, (struct sockaddr*)&listen_addr, sizeof(listen_addr)) ==
      -1) {
    goto tidy_up_and_fail;
  }
  if (::listen(listener, 1) == -1) {
    goto tidy_up_and_fail;
  }

  connector = ::socket(AF_INET, type, 0);
  if (connector < 0) {
    goto tidy_up_and_fail;
  }

  memset(&connect_addr, 0, sizeof(connect_addr));

  /* We want to find out the port number to connect to.  */
  size = sizeof(connect_addr);
  if (::getsockname(listener, (struct sockaddr*)&connect_addr, &size) == -1) {
    goto tidy_up_and_fail;
  }
  if (size != sizeof(connect_addr)) {
    goto abort_tidy_up_and_fail;
  }
  if (::connect(
          connector, (struct sockaddr*)&connect_addr, sizeof(connect_addr)) ==
      -1) {
    goto tidy_up_and_fail;
  }

  size = sizeof(listen_addr);
  acceptor = ::accept(listener, (struct sockaddr*)&listen_addr, &size);
  if (acceptor < 0) {
    goto tidy_up_and_fail;
  }
  if (size != sizeof(listen_addr)) {
    goto abort_tidy_up_and_fail;
  }
  /* Now check we are talking to ourself by matching port and host on the
     two sockets.   */
  if (::getsockname(connector, (struct sockaddr*)&connect_addr, &size) == -1) {
    goto tidy_up_and_fail;
  }
  if (size != sizeof(connect_addr) ||
      listen_addr.sin_family != connect_addr.sin_family ||
      listen_addr.sin_addr.s_addr != connect_addr.sin_addr.s_addr ||
      listen_addr.sin_port != connect_addr.sin_port) {
    goto abort_tidy_up_and_fail;
  }
  ::closesocket(listener);
  fd[0] = connector;
  fd[1] = acceptor;

  return 0;

abort_tidy_up_and_fail:
  saved_errno = WSAECONNABORTED;

tidy_up_and_fail:
  if (saved_errno < 0) {
    saved_errno = WSAGetLastError();
  }
  if (listener != -1) {
    ::closesocket(listener);
  }
  if (connector != -1) {
    ::closesocket(connector);
  }
  if (acceptor != -1) {
    ::closesocket(acceptor);
  }

  WSASetLastError(saved_errno);
  return -1;
}

#endif

int socketpair(int domain, int type, int protocol, NetworkSocket sv[2]) {}

int set_socket_non_blocking(NetworkSocket s) {}

int set_socket_close_on_exec(NetworkSocket s) {}

void Msgheader::setName(sockaddr_storage* addrStorage, size_t len) {}

void Msgheader::setIovecs(const struct iovec* vec, size_t iovec_len) {}

void Msgheader::setCmsgPtr(char* ctrlBuf) {}

void Msgheader::setCmsgLen(size_t len) {}

void Msgheader::setFlags(int flags) {}

void Msgheader::incrCmsgLen(size_t val) {}

XPLAT_CMSGHDR* Msgheader::getFirstOrNextCmsgHeader(XPLAT_CMSGHDR* cm) {}

XPLAT_MSGHDR* Msgheader::getMsg() {}

XPLAT_CMSGHDR* Msgheader::cmsgNextHrd(XPLAT_CMSGHDR* cm) {}

XPLAT_CMSGHDR* Msgheader::cmsgFirstHrd() {}
} // namespace netops
} // namespace folly