]> git.ipfire.org Git - thirdparty/open-vm-tools.git/commitdiff
Update to common source code; does not affect open-vm-tools.
authorOliver Kurth <okurth@vmware.com>
Mon, 3 Jun 2019 20:39:44 +0000 (13:39 -0700)
committerOliver Kurth <okurth@vmware.com>
Mon, 3 Jun 2019 20:39:44 +0000 (13:39 -0700)
Allow an asyncWebSocket to connect through an existing socket descriptor

Because Windows does not allow unprivileged processes to specify
DSCP (formerly ToS) values on TCP traffic, in order to support
DSCP for Blast WebSockets we must obtain a preconfigured socket
descriptor from a privileged process and then build the WebSocket
connection over that socket.

This changeset extends the asyncWebSocket API by adding a Connect
function that can accept and use an existing socket descriptor in
addition to the usual collection of WebSocket Connect parameters.

Because the asyncWebSocket is built over an asyncTCPSocket, this
change ripples down into the asyncTCPSocket API which also gets a
new Connect variant with a socket descriptor parameter.

To avoid duplicating existing logic, the old Connect variants
are modified to do their work by invoking the new API with
a distinguished socket descriptor value of -1.        This value
indicates that no existing socket descriptor is provided and
that a new socket should be created and used for the connection.

In this changeset, passing in an existing socket is supported
only on the Windows platform.  The feature is not required on
other platforms, where unprivileged processes are permitted to
define DSCP values for their connections.  Attempting to create
a connection over an existing socket on other platforms will
result in a Connect failure.

open-vm-tools/lib/asyncsocket/asyncsocket.c
open-vm-tools/lib/include/asyncsocket.h

index 49524396a676d4e798e1d9693d3a848e9a812fe3..102638cc92ccdc5900200ba7eb1737fd3037ddc0 100644 (file)
@@ -1,5 +1,5 @@
 /*********************************************************
- * Copyright (C) 2003-2018 VMware, Inc. All rights reserved.
+ * Copyright (C) 2003-2019 VMware, Inc. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published
@@ -266,6 +266,7 @@ static unsigned int AsyncTCPSocketGetPortFromAddr(
    struct sockaddr_storage *addr);
 static AsyncTCPSocket *AsyncTCPSocketConnect(struct sockaddr_storage *addr,
                                              socklen_t addrLen,
+                                             int socketFd,
                                              AsyncSocketConnectFn connectFn,
                                              void *clientData,
                                              AsyncSocketConnectFlags flags,
@@ -1657,6 +1658,7 @@ static AsyncTCPSocket *
 AsyncTCPSocketConnectImpl(int socketFamily,                  // IN
                           const char *hostname,              // IN
                           unsigned int port,                 // IN
+                          int tcpSocketFd,                   // IN
                           AsyncSocketConnectFn connectFn,    // IN
                           void *clientData,                  // IN
                           AsyncSocketConnectFlags flags,     // IN
@@ -1688,7 +1690,8 @@ AsyncTCPSocketConnectImpl(int socketFamily,                  // IN
        socketFamily == AF_INET ? "IPv4" : "IPv6", ipString, hostname);
    free(ipString);
 
-   asock = AsyncTCPSocketConnect(&addr, addrLen, connectFn, clientData,
+   asock = AsyncTCPSocketConnect(&addr, addrLen, tcpSocketFd,
+                                 connectFn, clientData,
                                  flags, pollParams, &error);
    if (!asock) {
       Warning(ASOCKPREFIX "%s connection attempt failed: %s\n",
@@ -1736,6 +1739,49 @@ AsyncSocket_Connect(const char *hostname,                // IN
                     AsyncSocketConnectFlags flags,       // IN
                     AsyncSocketPollParams *pollParams,   // IN
                     int *outError)                       // OUT: optional
+{
+   return AsyncSocket_ConnectWithFd(hostname,
+                                    port,
+                                    -1,
+                                    connectFn,
+                                    clientData,
+                                    flags,
+                                    pollParams,
+                                    outError);
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * AsyncSocket_ConnectWithFd --
+ *
+ *      AsyncTCPSocket connect using an existing socket descriptor.
+ *      Connection is attempted with AF_INET socket
+ *      family, when that fails AF_INET6 is attempted.
+ *
+ *      Limitation: The ConnectWithFd functionality is currently Windows only.
+ *                  Non-Windows platforms & windows-UWP are not supported.
+ *
+ * Results:
+ *      AsyncTCPSocket * on success and NULL on failure.
+ *      On failure, error is returned in *outError.
+ *
+ * Side effects:
+ *      Allocates an AsyncTCPSocket, registers a poll callback.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+AsyncSocket *
+AsyncSocket_ConnectWithFd(const char *hostname,                // IN
+                          unsigned int port,                   // IN
+                          int tcpSocketFd,                     // IN
+                          AsyncSocketConnectFn connectFn,      // IN
+                          void *clientData,                    // IN
+                          AsyncSocketConnectFlags flags,       // IN
+                          AsyncSocketPollParams *pollParams,   // IN
+                          int *outError)                       // OUT: optional
 {
    int error = ASOCKERR_CONNECT;
    AsyncTCPSocket *asock = NULL;
@@ -1746,11 +1792,13 @@ AsyncSocket_Connect(const char *hostname,                // IN
       goto error;
    }
 
-   asock = AsyncTCPSocketConnectImpl(AF_INET, hostname, port, connectFn,
-                                  clientData, flags, pollParams, &error);
+   asock = AsyncTCPSocketConnectImpl(AF_INET, hostname, port,
+                                     tcpSocketFd, connectFn, clientData,
+                                     flags, pollParams, &error);
    if (!asock) {
-      asock = AsyncTCPSocketConnectImpl(AF_INET6, hostname, port, connectFn,
-                                     clientData, flags, pollParams, &error);
+      asock = AsyncTCPSocketConnectImpl(AF_INET6, hostname, port,
+                                        tcpSocketFd, connectFn, clientData,
+                                        flags, pollParams, &error);
    }
 
 error:
@@ -1801,7 +1849,7 @@ AsyncSocket_ConnectVMCI(unsigned int cid,                  // IN
    Log(ASOCKPREFIX "creating new socket, connecting to %u:%u\n", cid, port);
 
    asock = AsyncTCPSocketConnect((struct sockaddr_storage *)&addr,
-                                 sizeof addr, connectFn, clientData,
+                                 sizeof addr, -1, connectFn, clientData,
                                  flags, pollParams, outError);
 
    VMCISock_ReleaseAFValueFd(vsockDev);
@@ -1851,7 +1899,7 @@ AsyncSocket_ConnectUnixDomain(const char *path,                  // IN
    Log(ASOCKPREFIX "creating new socket, connecting to %s\n", path);
 
    asock = AsyncTCPSocketConnect((struct sockaddr_storage *)&addr,
-                              sizeof addr, connectFn, clientData,
+                              sizeof addr, -1, connectFn, clientData,
                               flags, pollParams, outError);
 
    return BaseSocket(asock);
@@ -1920,6 +1968,79 @@ AsyncTCPSocketConnectErrorCheck(void *data)  // IN: AsyncTCPSocket *
 }
 
 
+/*
+ *----------------------------------------------------------------------------
+ *
+ * SocketProtocolAndTypeMatches --
+ *
+ *      Discover whether a given socket has the specified protocol family
+ *      (PF_INET, PF_INET6, ...) and data transfer type (SOCK_STREAM,
+ *      SOCK_DGRAM, ...).
+ *
+ *      For now, this is supported only on non-UWP Windows platforms.
+ *      Other platforms always receive a FALSE result.
+ *
+ * Results:
+ *      True if the socket has the specified family and type, false
+ *      otherwise.
+ *
+ * Side effects:
+ *      None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+static Bool
+SocketProtocolAndTypeMatches(int socketFd,   // IN
+                             int protocol,   // IN
+                             int type)       // IN
+{
+#if defined( _WIN32) && !defined(VM_WIN_UWP)
+   int ret;
+   WSAPROTOCOL_INFO protocolInfo;
+   int protocolInfoLen = sizeof protocolInfo;
+
+   ret = getsockopt(socketFd, SOL_SOCKET, SO_PROTOCOL_INFO,
+                    (void*)&protocolInfo, &protocolInfoLen);
+   if (ret != 0) {
+      Warning(ASOCKPREFIX "SO_PROTOCOL_INFO failed on sockFd %d, ",
+              "error 0x%x\n",
+              socketFd, ASOCK_LASTERROR());
+      return FALSE;
+   }
+
+   /*
+    * Windows is confused about protocol families (the "domain" of the
+    * socket, passed as the first argument to the socket() call) and
+    * address families (specified in the xx_family member of a sockaddr_xx
+    * argument passed to bind()).  The protocol family of the socket is
+    * reported in the iAddressFamily of the WSAPROTOCOL_INFO structure.
+    */
+   return ((protocol == protocolInfo.iAddressFamily) &&
+           (type == protocolInfo.iSocketType));
+#else
+
+   /*
+    * If we need to implement this for other platforms then we can use
+    * getsockopt(SO_TYPE) to retrieve the socket type, and on Linux we can
+    * use getsockopt(SO_DOMAIN) to retrieve the protocol family, but other
+    * platforms might not have SO_DOMAIN.  On those platforms we might be
+    * able to infer the protocol family by attempting sockopt calls that
+    * only work on certain families.
+    *
+    * BTW, Linux has thrown in the towel on the distinction between
+    * protocol families and address families.  Its socket() man page shows
+    * AF_* literals being used for the 'domain' argument instead of PF_*
+    * literals.  This works because AF_XX is defined to have the same
+    * numeric value as PF_XX for all values of XX.
+    */
+   Warning(ASOCKPREFIX "discovery of socket protocol and type is "
+           "not implemented on this platform\n");
+   NOT_IMPLEMENTED();
+#endif // defined(_WIN32)
+}
+
+
 /*
  *----------------------------------------------------------------------------
  *
@@ -1939,6 +2060,7 @@ AsyncTCPSocketConnectErrorCheck(void *data)  // IN: AsyncTCPSocket *
 static AsyncTCPSocket *
 AsyncTCPSocketConnect(struct sockaddr_storage *addr,         // IN
                       socklen_t addrLen,                     // IN
+                      int socketFd,                          // IN: optional
                       AsyncSocketConnectFn connectFn,        // IN
                       void *clientData,                      // IN
                       AsyncSocketConnectFlags flags,         // IN
@@ -1960,9 +2082,33 @@ AsyncTCPSocketConnect(struct sockaddr_storage *addr,         // IN
    }
 
    /*
-    * Create a new IP socket
+    * If we were given a socket, verify that it is of the required
+    * protocol family and type before using it.  If no socket was given,
+    * create a new socket of the appropriate family.  (For the sockets
+    * we care about, the required protocol family is numerically the
+    * same as the address family provided in the given destination
+    * sockaddr, so we can use addr->ss_family whenever we need to
+    * specify a protocol family.)
+    *
+    * For now, passing in a socket is supported only on non-UWP Windows
+    * platforms.  The SocketProtocolAndTypeMatches() call will fail on
+    * other platforms.
     */
-   if ((fd = socket(addr->ss_family, SOCK_STREAM, 0)) == -1) {
+   if (-1 != socketFd) {
+      int protocolFamily = addr->ss_family;
+      // XXX Logging here is excessive, remove after testing
+      if (SocketProtocolAndTypeMatches(socketFd, protocolFamily,
+                                       SOCK_STREAM)) {
+         Warning(ASOCKPREFIX "using passed-in socket, family %d\n",
+                 protocolFamily);
+         fd = socketFd;
+      } else {
+         Warning(ASOCKPREFIX "rejecting passed-in socket, wanted family %d\n",
+                 protocolFamily);
+         error = ASOCKERR_INVAL;
+         goto error;
+      }
+   } else if ((fd = socket(addr->ss_family, SOCK_STREAM, 0)) == -1) {
       sysErr = ASOCK_LASTERROR();
       Warning(ASOCKPREFIX "failed to create socket, error %d: %s\n",
               sysErr, Err_Errno2String(sysErr));
index fe066045d5a55759536b4922d10e867d1558056c..95a5e46435735b85b8612cb7cd0cd7cac4ee95ad 100644 (file)
@@ -1,5 +1,5 @@
 /*********************************************************
- * Copyright (C) 2003-2018 VMware, Inc. All rights reserved.
+ * Copyright (C) 2003-2019 VMware, Inc. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published
@@ -492,6 +492,14 @@ AsyncSocket *AsyncSocket_Connect(const char *hostname,
                                  AsyncSocketConnectFlags flags,
                                  AsyncSocketPollParams *pollParams,
                                  int *error);
+AsyncSocket *AsyncSocket_ConnectWithFd(const char *hostname,
+                                       unsigned int port,
+                                       int tcpSocketFd,
+                                       AsyncSocketConnectFn connectFn,
+                                       void *clientData,
+                                       AsyncSocketConnectFlags flags,
+                                       AsyncSocketPollParams *pollParams,
+                                       int *error);
 AsyncSocket *AsyncSocket_ConnectVMCI(unsigned int cid, unsigned int port,
                                      AsyncSocketConnectFn connectFn,
                                      void *clientData,