Create the sockets portability header
authorChristopher Dykes <cdykes@fb.com>
Wed, 4 May 2016 00:38:24 +0000 (17:38 -0700)
committerFacebook Github Bot 5 <facebook-github-bot-5-bot@fb.com>
Wed, 4 May 2016 00:50:23 +0000 (17:50 -0700)
Summary: The one header that doesn't split well. This is the last piece of the portability headers for Windows and is also the piece that has had the most thought put into it's design so-as to be as unintrusive as possible.

Reviewed By: yfeldblum

Differential Revision: D3046525

fb-gh-sync-id: 761a5e359bc130b95585608ee18fda94a30dae2c
fbshipit-source-id: 761a5e359bc130b95585608ee18fda94a30dae2c

folly/Makefile.am
folly/portability/Sockets.cpp [new file with mode: 0755]
folly/portability/Sockets.h [new file with mode: 0755]

index 173937948da87ad6a30badcee1940ea0acc2956d..f8c616b61d2259a8cb7359220d8dc7fa617c83cf 100644 (file)
@@ -284,6 +284,7 @@ nobase_follyinclude_HEADERS = \
        portability/Malloc.h \
        portability/Memory.h \
        portability/PThread.h \
+       portability/Sockets.h \
        portability/String.h \
        portability/Syslog.h \
        portability/SysFile.h \
@@ -428,6 +429,7 @@ libfolly_la_SOURCES = \
        portability/Libgen.cpp \
        portability/Malloc.cpp \
        portability/Memory.cpp \
+       portability/Sockets.cpp \
        portability/String.cpp \
        portability/SysFile.cpp \
        portability/SysMman.cpp \
diff --git a/folly/portability/Sockets.cpp b/folly/portability/Sockets.cpp
new file mode 100755 (executable)
index 0000000..380d386
--- /dev/null
@@ -0,0 +1,313 @@
+/*
+ * Copyright 2016 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/portability/Sockets.h>
+
+#ifdef _MSC_VER
+
+#include <errno.h>
+#include <fcntl.h>
+
+#include <MSWSock.h>
+
+#include <folly/ScopeGuard.h>
+
+namespace folly {
+namespace portability {
+namespace sockets {
+
+// We have to startup WSA.
+static struct FSPInit {
+  FSPInit() {
+    WSADATA dat;
+    WSAStartup(MAKEWORD(2, 2), &dat);
+  }
+  ~FSPInit() { WSACleanup(); }
+} fspInit;
+
+bool is_fh_socket(int fh) {
+  SOCKET h = fd_to_socket(fh);
+  constexpr long kDummyEvents = 0xABCDEF12;
+  WSANETWORKEVENTS e;
+  e.lNetworkEvents = kDummyEvents;
+  WSAEnumNetworkEvents(h, nullptr, &e);
+  return e.lNetworkEvents != kDummyEvents;
+}
+
+SOCKET fd_to_socket(int fd) {
+  // We do this in a roundabout way to allow us to compile even if
+  // we're doing a bit of trickery to ensure that things aren't
+  // being implicitly converted to a SOCKET by temporarily
+  // adjusting the windows headers to define SOCKET as a
+  // structure.
+  static_assert(sizeof(HANDLE) == sizeof(SOCKET), "Handle size mismatch.");
+  HANDLE tmp = (HANDLE)_get_osfhandle(fd);
+  return *(SOCKET*)&tmp;
+}
+
+int socket_to_fd(SOCKET s) {
+  return _open_osfhandle((intptr_t)s, O_RDWR | O_BINARY);
+}
+
+template <class R, class F, class... Args>
+static R wrapSocketFunction(F f, int s, Args... args) {
+  SOCKET h = fd_to_socket(s);
+  R ret = f(h, args...);
+  errno = WSAGetLastError();
+  return ret;
+}
+
+int accept(int s, struct sockaddr* addr, socklen_t* addrlen) {
+  return socket_to_fd(wrapSocketFunction<SOCKET>(::accept, s, addr, addrlen));
+}
+
+int bind(int s, const struct sockaddr* name, socklen_t namelen) {
+  return wrapSocketFunction<int>(::bind, s, name, namelen);
+}
+
+int connect(int s, const struct sockaddr* name, socklen_t namelen) {
+  return wrapSocketFunction<int>(::connect, s, name, namelen);
+}
+
+int getpeername(int s, struct sockaddr* name, socklen_t* namelen) {
+  return wrapSocketFunction<int>(::getpeername, s, name, namelen);
+}
+
+int getsockname(int s, struct sockaddr* name, socklen_t* namelen) {
+  return wrapSocketFunction<int>(::getsockname, s, name, namelen);
+}
+
+int getsockopt(int s, int level, int optname, char* optval, socklen_t* optlen) {
+  return wrapSocketFunction<int>(
+      ::getsockopt, s, level, optname, (char*)optval, optlen);
+}
+
+int getsockopt(int s, int level, int optname, void* optval, socklen_t* optlen) {
+  return wrapSocketFunction<int>(
+      ::getsockopt, s, level, optname, (char*)optval, optlen);
+}
+
+int inet_aton(const char* cp, struct in_addr* inp) {
+  inp->s_addr = inet_addr(cp);
+  return inp->s_addr == INADDR_NONE ? 0 : 1;
+}
+
+const char* inet_ntop(int af, const void* src, char* dst, socklen_t size) {
+  return ::inet_ntop(af, (char*)src, dst, size);
+}
+
+int listen(int s, int backlog) {
+  return wrapSocketFunction<int>(::listen, s, backlog);
+}
+
+int poll(struct pollfd fds[], nfds_t nfds, int timeout) {
+  // TODO: Allow both file descriptors and SOCKETs in this.
+  for (int i = 0; i < nfds; i++) {
+    fds[i].fd = fd_to_socket(fds[i].fd);
+  }
+  return ::WSAPoll(fds, (ULONG)nfds, timeout);
+}
+
+ssize_t recv(int s, void* buf, size_t len, int flags) {
+  return wrapSocketFunction<ssize_t>(::recv, s, (char*)buf, (int)len, flags);
+}
+
+ssize_t recv(int s, char* buf, int len, int flags) {
+  return wrapSocketFunction<ssize_t>(::recv, s, (char*)buf, len, flags);
+}
+
+ssize_t recv(int s, void* buf, int len, int flags) {
+  return wrapSocketFunction<ssize_t>(::recv, s, (char*)buf, len, flags);
+}
+
+ssize_t recvfrom(
+    int s,
+    void* buf,
+    size_t len,
+    int flags,
+    struct sockaddr* from,
+    socklen_t* fromlen) {
+  return wrapSocketFunction<ssize_t>(
+      ::recvfrom, s, (char*)buf, (int)len, flags, from, fromlen);
+}
+
+ssize_t recvfrom(
+    int s,
+    char* buf,
+    int len,
+    int flags,
+    struct sockaddr* from,
+    socklen_t* fromlen) {
+  return wrapSocketFunction<ssize_t>(
+      ::recvfrom, s, (char*)buf, len, flags, from, fromlen);
+}
+
+ssize_t recvfrom(
+    int s,
+    void* buf,
+    int len,
+    int flags,
+    struct sockaddr* from,
+    socklen_t* fromlen) {
+  return wrapSocketFunction<ssize_t>(
+      ::recvfrom, s, (char*)buf, len, flags, from, fromlen);
+}
+
+ssize_t recvmsg(int s, struct msghdr* message, int fl) {
+  SOCKET h = fd_to_socket(s);
+
+  // 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);
+  return res == o ? (ssize_t)bytesReceived : -1;
+}
+
+ssize_t send(int s, const void* buf, size_t len, int flags) {
+  return wrapSocketFunction<ssize_t>(::send, s, (char*)buf, (int)len, flags);
+}
+
+ssize_t send(int s, const char* buf, int len, int flags) {
+  return wrapSocketFunction<ssize_t>(::send, s, (char*)buf, len, flags);
+}
+
+ssize_t send(int s, const void* buf, int len, int flags) {
+  return wrapSocketFunction<ssize_t>(::send, s, (char*)buf, len, flags);
+}
+
+ssize_t sendmsg(int s, const struct msghdr* message, int fl) {
+  SOCKET h = fd_to_socket(s);
+
+  // 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;
+  }
+
+  DWORD bytesSent;
+  int res = WSASendMsg(h, &msg, 0, &bytesSent, nullptr, nullptr);
+  return res == 0 ? (ssize_t)bytesSent : -1;
+}
+
+ssize_t sendto(
+    int s,
+    const void* buf,
+    size_t len,
+    int flags,
+    const sockaddr* to,
+    socklen_t tolen) {
+  return wrapSocketFunction<ssize_t>(
+      ::sendto, s, (char*)buf, (int)len, flags, to, tolen);
+}
+
+ssize_t sendto(
+    int s,
+    const char* buf,
+    int len,
+    int flags,
+    const sockaddr* to,
+    socklen_t tolen) {
+  return wrapSocketFunction<ssize_t>(
+      ::sendto, s, (char*)buf, len, flags, to, tolen);
+}
+
+ssize_t sendto(
+    int s,
+    const void* buf,
+    int len,
+    int flags,
+    const sockaddr* to,
+    socklen_t tolen) {
+  return wrapSocketFunction<ssize_t>(
+      ::sendto, s, (char*)buf, len, flags, to, tolen);
+}
+
+int setsockopt(
+    int s,
+    int level,
+    int optname,
+    const char* optval,
+    socklen_t optlen) {
+  return wrapSocketFunction<int>(
+      ::setsockopt, s, level, optname, (char*)optval, optlen);
+}
+
+int setsockopt(
+    int s,
+    int level,
+    int optname,
+    const void* optval,
+    socklen_t optlen) {
+  return wrapSocketFunction<int>(
+      ::setsockopt, s, level, optname, (char*)optval, optlen);
+}
+
+int shutdown(int s, int how) {
+  return wrapSocketFunction<int>(::shutdown, s, how);
+}
+
+int socket(int af, int type, int protocol) {
+  return socket_to_fd(::socket(af, type, protocol));
+}
+}
+}
+}
+#endif
diff --git a/folly/portability/Sockets.h b/folly/portability/Sockets.h
new file mode 100755 (executable)
index 0000000..de0db0a
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2016 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
+
+#ifdef _WIN32
+#define FOLLY_HAVE_UNIX_SOCKETS 0
+#else
+#define FOLLY_HAVE_UNIX_SOCKETS 1
+#endif
+
+#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>
+#else
+#include <folly/portability/IOVec.h>
+#include <folly/portability/SysTypes.h>
+#include <folly/portability/Windows.h>
+
+#include <WS2tcpip.h>
+
+// We don't actually support either of these flags
+// currently.
+#define MSG_DONTWAIT 1
+#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;
+};
+
+#define SHUT_RD SD_RECEIVE
+#define SHUT_WR SD_SEND
+#define SHUT_RDWR SD_BOTH
+
+using nfds_t = int;
+
+// This is named differently in posix.
+#define sa_family_t ADDRESS_FAMILY
+// Someone thought it would be a good idea
+// to define a field via a macro...
+#undef s_host
+#endif
+
+namespace folly {
+namespace portability {
+namespace sockets {
+#ifndef _WIN32
+using ::bind;
+using ::connect;
+using ::getpeername;
+using ::getsockname;
+using ::getsockopt;
+using ::inet_ntop;
+using ::listen;
+using ::poll;
+using ::recv;
+using ::recvfrom;
+using ::send;
+using ::sendto;
+using ::sendmsg;
+using ::setsockopt;
+using ::shutdown;
+using ::socket;
+#else
+// Some Windows specific helper functions.
+bool is_fh_socket(int fh);
+SOCKET fd_to_socket(int fd);
+int socket_to_fd(SOCKET s);
+
+// These aren't additional overloads, but rather other functions that
+// are referenced that we need to wrap, or, in the case of inet_aton,
+// implement.
+int accept(int s, struct sockaddr* addr, socklen_t* addrlen);
+int inet_aton(const char* cp, struct in_addr* inp);
+
+// Unless you have a case where you would normally have
+// to reference the function as being explicitly in the
+// global scope, then you shouldn't be calling these directly.
+int bind(int s, const struct sockaddr* name, socklen_t namelen);
+int connect(int s, const struct sockaddr* name, socklen_t namelen);
+int getpeername(int s, struct sockaddr* name, socklen_t* namelen);
+int getsockname(int s, struct sockaddr* name, socklen_t* namelen);
+int getsockopt(int s, int level, int optname, void* optval, socklen_t* optlen);
+const char* inet_ntop(int af, const void* src, char* dst, socklen_t size);
+int listen(int s, int backlog);
+int poll(struct pollfd fds[], nfds_t nfds, int timeout);
+ssize_t recv(int s, void* buf, size_t len, int flags);
+ssize_t recvfrom(
+    int s,
+    void* buf,
+    size_t len,
+    int flags,
+    struct sockaddr* from,
+    socklen_t* fromlen);
+ssize_t send(int s, const void* buf, size_t len, int flags);
+ssize_t sendto(
+    int s,
+    const void* buf,
+    size_t len,
+    int flags,
+    const sockaddr* to,
+    socklen_t tolen);
+ssize_t sendmsg(int socket, const struct msghdr* message, int flags);
+int setsockopt(
+    int s,
+    int level,
+    int optname,
+    const void* optval,
+    socklen_t optlen);
+int shutdown(int s, int how);
+
+// This is the only function that _must_ be referenced via the namespace
+// because there is no difference in parameter types to overload
+// on.
+int socket(int af, int type, int protocol);
+
+// Windows needs a few extra overloads of some of the functions in order to
+// resolve to our portability functions rather than the SOCKET accepting
+// ones.
+int getsockopt(int s, int level, int optname, char* optval, socklen_t* optlen);
+ssize_t recv(int s, char* buf, int len, int flags);
+ssize_t recv(int s, void* buf, int len, int flags);
+ssize_t recvfrom(
+    int s,
+    char* buf,
+    int len,
+    int flags,
+    struct sockaddr* from,
+    socklen_t* fromlen);
+ssize_t recvfrom(
+    int s,
+    void* buf,
+    int len,
+    int flags,
+    struct sockaddr* from,
+    socklen_t* fromlen);
+ssize_t recvmsg(int s, struct msghdr* message, int fl);
+ssize_t send(int s, const char* buf, int len, int flags);
+ssize_t send(int s, const void* buf, int len, int flags);
+ssize_t sendto(
+    int s,
+    const char* buf,
+    int len,
+    int flags,
+    const sockaddr* to,
+    socklen_t tolen);
+ssize_t sendto(
+    int s,
+    const void* buf,
+    int len,
+    int flags,
+    const sockaddr* to,
+    socklen_t tolen);
+int setsockopt(
+    int s,
+    int level,
+    int optname,
+    const char* optval,
+    socklen_t optlen);
+#endif
+}
+}
+}
+
+#ifdef _WIN32
+// Add our helpers to the overload set.
+/* using override */ using namespace folly::portability::sockets;
+#endif