]> git.ipfire.org Git - thirdparty/open-vm-tools.git/commitdiff
Dynamic socket options API for async sockets
authorOliver Kurth <okurth@vmware.com>
Fri, 15 Sep 2017 18:23:42 +0000 (11:23 -0700)
committerOliver Kurth <okurth@vmware.com>
Fri, 15 Sep 2017 18:23:42 +0000 (11:23 -0700)
  lib/asyncsocket/asyncsocket.c:
  lib/asyncsocket/asyncSocketInterface.c:
  lib/asyncsocket/asyncSocketVTable.h:
  lib/include/asyncsocket.h:
  - Add AsyncTCPSocketSetOption(), AsyncTCPSocketGetOption() and
    AsyncSocket_EstablishMinBufferSizes functions.
  - Remove/deprecate specific option set functions to be subsumed
    by ->setOption().
    - ->useNodelay() (TCP_NODELAY), ->setTCPTimeouts (3x TCP_... options),
      ->setBufferSize (SO_{SND|RCV}BUF), ->setSendLowLatencyMode() (non-native
      option regarding buffering/callback behavior).

  lib/rpcIn/rpcin.c:
  services/plugins/grabbitmqProxy/grabbitmqProxyPlugin.c:
  - replace AsyncSocket_SetBufferSizes() calls with
    AsyncSocket_EstablishMinBufferSizes() calls.

Common header file change: not applicable to open-vm-tools.

open-vm-tools/lib/asyncsocket/asyncSocketInterface.c
open-vm-tools/lib/asyncsocket/asyncSocketVTable.h
open-vm-tools/lib/asyncsocket/asyncsocket.c
open-vm-tools/lib/include/asyncsocket.h
open-vm-tools/lib/include/vm_product_versions.h
open-vm-tools/lib/rpcIn/rpcin.c
open-vm-tools/services/plugins/grabbitmqProxy/grabbitmqProxyPlugin.c

index e0e1becc5181241a496a41de0e114f777ccb6cd6..92e2163975dba5c401a7a852f0c0d7a9c0814d74 100644 (file)
  *   generally are NOT virtualized.
  */
 
+#ifdef _WIN32
+#include <winsock2.h>
+#else
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#endif
+
 #include "vmware.h"
 #include "asyncsocket.h"
 #include "asyncSocketBase.h"
@@ -59,8 +67,6 @@
 #include "loglevel_user.h"
 
 
-
-
 /*
  *----------------------------------------------------------------------------
  *
@@ -314,71 +320,257 @@ AsyncSocket_GetPort(AsyncSocket *asock)  // IN
  *
  * AsyncSocket_UseNodelay --
  *
- *      Sets or unset TCP_NODELAY on the socket, which disables or
- *      enables Nagle's algorithm, respectively.
+ *      THIS IS DEPRECATED in favor of AsyncSocket_SetOption(...TCP_NODELAY...).
+ *      It exists for now to avoid having to change all existing calling code.
+ *      TODO: Remove it fully and fix up all calling code accordingly.
+ *
+ *      Sets the setsockopt() value TCP_NODELAY.
+ *      asyncSocket may be an AsyncTCPSocket itself
+ *      or contain one on which the option will be set.
+ *
+ *      This fails if there is no applicable AsyncTCPSocket (asyncSocket or
+ *      one inside it).
  *
  * Results:
  *      ASOCKERR_SUCCESS on success, ASOCKERR_* otherwise.
+ *      There being no applicable AsyncTCPSocket yields ASOCKERR_INVAL.
+ *      OS error when setting value yields ASOCKERR_GENERIC.
  *
- * Side Effects:
- *      Increased bandwidth usage for short messages on this socket
+ * Side effects:
+ *      Possibly increased bandwidth usage for short messages on this socket
  *      due to TCP overhead, in exchange for lower latency.
  *
  *----------------------------------------------------------------------------
  */
 
 int
-AsyncSocket_UseNodelay(AsyncSocket *asock,  // IN/OUT:
-                       Bool nodelay)        // IN:
+AsyncSocket_UseNodelay(AsyncSocket *asyncSocket,  // IN/OUT
+                       Bool noDelay)              // IN
 {
+   const int noDelayNative = noDelay ? 1 : 0;
+   return AsyncSocket_SetOption(asyncSocket,
+                                IPPROTO_TCP, TCP_NODELAY,
+                                &noDelayNative, sizeof noDelayNative);
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * AsyncSocket_SetTCPTimeouts --
+ *
+ *      Sets setsockopt() TCP_KEEP{INTVL|IDLE|CNT} if available in the OS.
+ *      asyncSocket may be an AsyncTCPSocket itself
+ *      or contain one on which the option will be set.
+ *
+ *      This fails if there is no applicable AsyncTCPSocket (asyncSocket or
+ *      one inside it).
+ *
+ * Results:
+ *      ASOCKERR_SUCCESS if no error, or OS doesn't support options.
+ *      There being no applicable AsyncTCPSocket yields ASOCKERR_INVAL.
+ *      OS error when setting any one value yields ASOCKERR_GENERIC.
+ *
+ * Side effects:
+ *      None.
+ *      Note that in case of error ASOCKERR_GENERIC, 0, 1, or 2 of the values
+ *      may have still been successfully set (the successful changes are
+ *      not rolled back).
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int
+AsyncSocket_SetTCPTimeouts(AsyncSocket *asyncSocket,  // IN/OUT
+                           int keepIdleSec,           // IN
+                           int keepIntvlSec,          // IN
+                           int keepCnt)               // IN
+{
+   /*
+    * This function is NOT deprecated like the nearby setOption()-wrapping
+    * functions. It's valuable because it: enapsulates OS-dependent logic; and
+    * performs one lock before settong all applicable options together.
+    */
+
+#if defined(__linux__) || defined(VMX86_SERVER)
+   /*
+    * Tempting to call AsyncSocket_SetOption() x 3 instead of worrying about
+    * locking and VT() ourselves, but this way we can reduce amount of
+    * locking/unlocking at the cost of code verbosity.
+    *
+    * Reason for bailing on first error instead of trying all three:
+    * it's what the original code (that this adapts) did. TODO: Find out from
+    * author, explain here.
+    */
+
    int ret;
-   if (VALID(asock, useNodelay)) {
-      AsyncSocketLock(asock);
-      ret = VT(asock)->useNodelay(asock, nodelay);
-      AsyncSocketUnlock(asock);
+   if (VALID(asyncSocket, setOption)) {
+      AsyncSocketLock(asyncSocket);
+
+      ret = VT(asyncSocket)->setOption
+               (asyncSocket,
+                IPPROTO_TCP, TCP_KEEPIDLE,
+                &keepIdleSec, sizeof keepIdleSec);
+      if (ret == ASOCKERR_SUCCESS) {
+         ret = VT(asyncSocket)->setOption
+                  (asyncSocket,
+                   IPPROTO_TCP, TCP_KEEPINTVL,
+                   &keepIntvlSec, sizeof keepIntvlSec);
+         if (ret == ASOCKERR_SUCCESS) {
+            ret = VT(asyncSocket)->setOption
+                     (asyncSocket,
+                      IPPROTO_TCP, TCP_KEEPCNT,
+                      &keepCnt, sizeof keepCnt);
+         }
+      }
+
+      AsyncSocketUnlock(asyncSocket);
    } else {
       ret = ASOCKERR_INVAL;
    }
    return ret;
+#else // #ifndef __linux__
+   return ASOCKERR_SUCCESS;
+#endif
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * AsyncSocket_EstablishMinBufferSizes --
+ *
+ *      Meant to be invoked around socket creation time, this tries to ensure
+ *      that SO_{SND|RCV}BUF setsockopt() values are set to at least the values
+ *      provided as arguments. That is, it sets the given buffer size but only
+ *      if the current value reported by the OS is smaller.
+ *
+ *      This fails unless asyncSocket is of the "applicable socket type."
+ *      Being of "applicable socket type" is defined as supporting the
+ *      option:
+ *
+ *        layer = SOL_SOCKET,
+ *        optID = SO_{SND|RCV}BUF.
+ *
+ *      As of this writing, only AsyncTCPSockets (or derivations thereof)
+ *      are supported, but (for example) UDP sockets could be added over time.
+ *
+ * Results:
+ *      TRUE: on success; FALSE: on failure.
+ *      Determining that no setsockopt() is required is considered success.
+ *
+ * Side effects:
+ *      None.
+ *      Note that in case of a setsockopt() failing, 0 or 1 of the values
+ *      may have still been successfully set (the successful changes are
+ *      not rolled back).
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+Bool
+AsyncSocket_EstablishMinBufferSizes(AsyncSocket *asyncSocket,  // IN/OUT
+                                    int sendSz,                // IN
+                                    int recvSz)                // IN
+{
+   Bool ok;
+
+   if (VALID(asyncSocket, setOption)) {
+      int curSendSz;
+      socklen_t curSendSzSz = sizeof curSendSz;
+      int curRecvSz;
+      socklen_t curRecvSzSz = sizeof curRecvSz;
+
+      AsyncSocketLock(asyncSocket);
+
+      /*
+       * For each buffer size, see if the current reported size is already
+       * at least as large (in which case we needn't do anything for that one).
+       * Bail out the moment anything fails, but don't worry about undoing any
+       * change already made (as advertised in doc comment).
+       *
+       * Reason for bailing on first error instead of trying everything:
+       * it's what the original code (that this adapts) did. TODO: Find out from
+       * author, explain here.
+       *
+       * Note that depending on the type of socket and the particular
+       * implementation (e.g., the TCP stack), asking for buffer size N might
+       * result in an even larger buffer, like a multiple 2N. It's not an exact
+       * science.
+       */
+
+      ok = (VT(asyncSocket)->getOption(asyncSocket,
+                                       SOL_SOCKET, SO_SNDBUF,
+                                       &curSendSz, &curSendSzSz) ==
+            ASOCKERR_SUCCESS) &&
+           (VT(asyncSocket)->getOption(asyncSocket,
+                                       SOL_SOCKET, SO_RCVBUF,
+                                       &curRecvSz, &curRecvSzSz) ==
+            ASOCKERR_SUCCESS);
+      if (ok && (curSendSz < sendSz)) {
+         ok = VT(asyncSocket)->setOption(asyncSocket,
+                                         SOL_SOCKET, SO_SNDBUF,
+                                         &sendSz, sizeof sendSz) ==
+              ASOCKERR_SUCCESS;
+      }
+      if (ok && (curRecvSz < recvSz)) {
+         ok = VT(asyncSocket)->setOption(asyncSocket,
+                                         SOL_SOCKET, SO_RCVBUF,
+                                         &recvSz, sizeof recvSz) ==
+              ASOCKERR_SUCCESS;
+      }
+
+      AsyncSocketUnlock(asyncSocket);
+   } else {
+      ok = FALSE;
+   }
+
+   return ok;
 }
 
 
 /*
  *----------------------------------------------------------------------------
  *
- * AsyncSocket_SetTCPTimeouts --
+ * AsyncSocket_SetSendLowLatencyMode --
+ *
+ *      THIS IS DEPRECATED in favor of
+ *      AsyncSocket_SetOption(ASYNC_SOCKET_OPT_SEND_LOW_LATENCY_MODE).
+ *      It exists for now to avoid having to change all existing calling code.
+ *      TODO: Remove it fully and fix up all calling code accordingly.
  *
- *      Allow caller to set a number of TCP-specific timeout
- *      parameters on the socket for the active connection.
+ *      Sets the aforementioned value. See doc comment on
+ *      ASYNC_SOCKET_OPT_SEND_LOW_LATENCY_MODE for more info.
  *
- *      Parameters:
- *      keepIdle --  The number of seconds a TCP connection must be idle before
- *                   keep-alive probes are sent.
- *      keepIntvl -- The number of seconds between TCP keep-alive probes once
- *                   they are being sent.
- *      keepCnt   -- The number of keep-alive probes to send before killing
- *                   the connection if no response is received from the peer.
+ *      This fails unless asyncSocket is of the "applicable socket type."
+ *      Being of "applicable socket type" is defined as supporting the
+ *      option:
+ *
+ *        layer = ASYNC_SOCKET_OPTS_LAYER_BASE,
+ *        optID = ASYNC_SOCKET_OPT_SEND_LOW_LATENCY_MODE.
  *
  * Results:
  *      ASOCKERR_SUCCESS on success, ASOCKERR_* otherwise.
+ *      asyncSocket being of inapplicable socket type yields ASOCKERR_INVAL.
  *
- * Side Effects:
- *      None.
+ * Side effects:
+ *      See ASYNC_SOCKET_OPT_SEND_LOW_LATENCY_MODE doc comment.
  *
  *----------------------------------------------------------------------------
  */
 
 int
-AsyncSocket_SetTCPTimeouts(AsyncSocket *asock,  // IN/OUT:
-                           int keepIdle,        // IN
-                           int keepIntvl,       // IN
-                           int keepCnt)         // IN
+AsyncSocket_SetSendLowLatencyMode(AsyncSocket *asyncSocket,  // IN
+                                  Bool enable)               // IN
 {
    int ret;
-   if (VALID(asock, setTCPTimeouts)) {
-      AsyncSocketLock(asock);
-      ret = VT(asock)->setTCPTimeouts(asock, keepIdle, keepIntvl, keepCnt);
-      AsyncSocketUnlock(asock);
+   if (VALID(asyncSocket, setOption)) {
+      AsyncSocketLock(asyncSocket);
+      ret = VT(asyncSocket)->setOption
+               (asyncSocket, ASYNC_SOCKET_OPTS_LAYER_BASE,
+                ASYNC_SOCKET_OPT_SEND_LOW_LATENCY_MODE,
+                &enable, sizeof enable);
+      AsyncSocketUnlock(asyncSocket);
    } else {
       ret = ASOCKERR_INVAL;
    }
@@ -387,72 +579,117 @@ AsyncSocket_SetTCPTimeouts(AsyncSocket *asock,  // IN/OUT:
 
 
 /*
- *-----------------------------------------------------------------------------
+ *----------------------------------------------------------------------------
  *
- * AsyncSocket_SetBufferSizes --
+ * AsyncSocket_SetOption --
  *
- *    Set socket level recv/send buffer sizes if they are less than given sizes
+ *      Sets the value of the given socket option belonging to the given
+ *      option layer to the given value. The socket options mechanism is
+ *      discussed in more detail in asyncsocket.h.
  *
- * Result
- *    TRUE: on success
- *    FALSE: on failure
+ *      The exact behavior and supported options are dependent on the socket
+ *      type. See the doc header for the specific implementation for details.
+ *      If ->setOption is NULL, all options are invalid for that socket.
+ *      Setting an invalid layer+option results in a no-op + error result.
  *
- * Side-effects
- *    None
+ *      For native options, layer = setsockopt() level,
+ *      optID = setsockopt() option_name.
  *
- *-----------------------------------------------------------------------------
+ *      For non-native options, optID is obtained as follows: it is converted
+ *      from an enum option ID value for your socket type; for example,
+ *      from ASYNC_TCP_SOCKET_OPT_ALLOW_DECREASING_BUFFER_SIZE,
+ *      where the latter is of type AsyncTCPSocket_OptID.
+ *
+ *      The option's value must reside at the buffer valuePtr that is
+ *      inBufLen long. If inBufLen does not match the expected size for
+ *      the given option, behavior is undefined.
+ *
+ * Results:
+ *      ASOCKERR_SUCCESS on success, ASOCKERR_* otherwise.
+ *      Invalid option+layer yields ASOCKERR_INVAL.
+ *      Failure to set a native OS option yields ASOCKERR_GENERIC.
+ *      inBufLen being wrong (for the given option) yields undefined behavior.
+ *
+ * Side effects:
+ *      Depends on option.
+ *
+ *----------------------------------------------------------------------------
  */
 
-Bool
-AsyncSocket_SetBufferSizes(AsyncSocket *asock,  // IN
-                           int sendSz,          // IN
-                           int recvSz)          // IN
+int
+AsyncSocket_SetOption(AsyncSocket *asyncSocket,     // IN/OUT
+                      AsyncSocketOpts_Layer layer,  // IN
+                      AsyncSocketOpts_ID optID,     // IN
+                      const void *valuePtr,         // IN
+                      socklen_t inBufLen)           // IN
 {
-   Bool ret;
-   if (VALID(asock, setBufferSizes)) {
-      AsyncSocketLock(asock);
-      ret = VT(asock)->setBufferSizes(asock, sendSz, recvSz);
-      AsyncSocketUnlock(asock);
+   int ret;
+   /*
+    * Lacking a setOption() implementation is conceptually the same as
+    * ->setOption() existing but determining layer+optID to be invalid
+    * (ASOCKERR_INVAL results).
+    */
+   if (VALID(asyncSocket, setOption)) {
+      AsyncSocketLock(asyncSocket);
+      ret = VT(asyncSocket)->setOption(asyncSocket, layer, optID,
+                                       valuePtr, inBufLen);
+      AsyncSocketUnlock(asyncSocket);
    } else {
-      ret = FALSE;
+      ret = ASOCKERR_INVAL;
    }
    return ret;
 }
 
 
 /*
- *-----------------------------------------------------------------------------
+ *----------------------------------------------------------------------------
  *
- * AsyncSocket_SetSendLowLatencyMode --
+ * AsyncSocket_GetOption --
  *
- *    Put the socket into a mode where we attempt to issue sends
- *    directly from within AsyncSocket_Send().  Ordinarily, we would
- *    set up a Poll callback from within AsyncSocket_Send(), which
- *    introduces some non-zero latency to the send path.  In
- *    low-latency-send mode, that delay is potentially avoided.  This
- *    does introduce a behavioural change; the send completion
- *    callback may be triggered before the call to Send() returns.  As
- *    not all clients may be expecting this, we don't enable this mode
- *    unless requested by the client.
+ *      Gets the value of the given socket option belonging to the given
+ *      option layer. The socket options mechanism is
+ *      discussed in more detail in asyncsocket.h.
+ *      This is generally symmetrical to ..._SetOption(); most comments applying
+ *      to that function apply to this one in common-sense ways.
+ *      In particular a layer+optID combo is supported here if and only if it
+ *      is supported for ..._SetOption().
  *
- * Result
- *    ASOCKERR_SUCCESS or ASOCKERR_*
+ *      The length of the output buffer at valuePtr must reside at *outBufLen
+ *      at entry to this function. If *outBufLen does not match or exceed the
+ *      expected size for the given option, behavior is undefined.
+ *      At successful return from function, *outBufLen will be set to the
+ *      length of the value written to at valuePtr.
  *
- * Side-effects
- *    See description above.
+ * Results:
+ *      ASOCKERR_SUCCESS on success, ASOCKERR_* otherwise.
+ *      Invalid option+layer yields ASOCKERR_INVAL.
+ *      Failure to get a native OS option yields ASOCKERR_GENERIC.
+ *      *outBufLen being wrong (for the given option) yields undefined behavior.
  *
- *-----------------------------------------------------------------------------
+ * Side effects:
+ *      None.
+ *
+ *----------------------------------------------------------------------------
  */
 
 int
-AsyncSocket_SetSendLowLatencyMode(AsyncSocket *asock,  // IN
-                                  Bool enable)         // IN
+AsyncSocket_GetOption(AsyncSocket *asyncSocket,     // IN/OUT
+                      AsyncSocketOpts_Layer layer,  // IN
+                      AsyncSocketOpts_ID optID,     // IN
+                      void *valuePtr,               // OUT
+                      socklen_t *outBufLen)         // IN/OUT
 {
    int ret;
-   if (VALID(asock, setSendLowLatencyMode)) {
-      AsyncSocketLock(asock);
-      ret = VT(asock)->setSendLowLatencyMode(asock, enable);
-      AsyncSocketUnlock(asock);
+   /*
+    * Lacking a getOption() implementation is conceptually the same as
+    * ->getOption() existing but determining layer+optID to be invalid
+    * (ASOCKERR_INVAL results).
+    */
+   if (VALID(asyncSocket, getOption)) {
+      AsyncSocketLock(asyncSocket);
+      ret = VT(asyncSocket)->getOption(asyncSocket, layer, optID,
+                                       valuePtr, outBufLen);
+      AsyncSocketUnlock(asyncSocket);
    } else {
       ret = ASOCKERR_INVAL;
    }
index c1216e03987c90bbf079f441eead6c5bfb1e3223..afb5e544b1b05b299035a465bc654c7154ac93bc 100644 (file)
  */
 typedef struct AsyncSocketVTable {
    AsyncSocketState (*getState)(AsyncSocket *sock);
+
+   /*
+    * The socket options mechanism is discussed in asyncsocket.h.
+    * If you're considering adding a new virtual function table entry whose
+    * effect is to call setsockopt() and/or save a value inside the socket
+    * structure and/or forward such a call to a contained AsyncSocket,
+    * strongly consider using this setOption() mechanism instead.
+    * Your life is likely to be made easier by this.
+    */
+   int (*setOption)(AsyncSocket *asyncSocket,
+                    AsyncSocketOpts_Layer layer,
+                    AsyncSocketOpts_ID optID,
+                    const void *valuePtr,
+                    socklen_t inBufLen);
+   /*
+    * A setOption() implementation must have a symmetrical getOption()
+    * counterpart.
+    */
+   int (*getOption)(AsyncSocket *asyncSocket,
+                    AsyncSocketOpts_Layer layer,
+                    AsyncSocketOpts_ID optID,
+                    void *valuePtr,
+                    socklen_t *outBufLen);
+
    int (*getGenericErrno)(AsyncSocket *s);
    int (*getFd)(AsyncSocket *asock);
    int (*getRemoteIPStr)(AsyncSocket *asock, const char **ipStr);
    int (*getINETIPStr)(AsyncSocket *asock, int socketFamily, char **ipRetStr);
    unsigned int (*getPort)(AsyncSocket *asock);
-   int (*useNodelay)(AsyncSocket *asock, Bool nodelay);
-   int (*setTCPTimeouts)(AsyncSocket *asock, int keepIdle, int keepIntvl,
-                         int keepCnt);
-   Bool (*setBufferSizes)(AsyncSocket *asock, int sendSz, int recvSz);
-   int (*setSendLowLatencyMode)(AsyncSocket *asock, Bool enable);
    int (*setCloseOptions)(AsyncSocket *asock, int flushEnabledMaxWaitMsec,
                            AsyncSocketCloseFn closeCb);
    Bool (*connectSSL)(AsyncSocket *asock, struct _SSLVerifyParam *verifyParam,
index a997a2f3c8a78d8472e989b57a885527174b54f7..d2086fc670fb8a5c57af4afccf4c5ed3d19a73b7 100644 (file)
 #define ADDR_STRING_LEN (INET6_ADDRSTRLEN + 2 + PORT_STRING_LEN)
 
 
+/* Local types. */
+
 /*
  * Output buffer list data type, for the queue of outgoing buffers
  */
@@ -307,13 +309,6 @@ static int AsyncTCPSocketGetRemoteIPStr(AsyncSocket *asock, const char **ipStr);
 static int AsyncTCPSocketGetINETIPStr(AsyncSocket *asock, int socketFamily,
                                       char **ipRetStr);
 static unsigned int AsyncTCPSocketGetPort(AsyncSocket *asock);
-static int AsyncTCPSocketUseNodelay(AsyncSocket *asock, Bool nodelay);
-static int AsyncTCPSocketSetTCPTimeouts(AsyncSocket *asock, int keepIdle,
-                                        int keepIntvl, int keepCnt);
-static Bool AsyncTCPSocketSetBufferSizes(AsyncSocket *asock,
-                                         int sendSz, int recvSz);
-static int AsyncTCPSocketSetSendLowLatencyMode(AsyncSocket *asock,
-                                                Bool enable);
 static Bool AsyncTCPSocketConnectSSL(AsyncSocket *asock,
                                      struct _SSLVerifyParam *verifyParam,
                                      void *sslContext);
@@ -359,19 +354,29 @@ static int AsyncTCPSocketSendBlocking(AsyncSocket *s, void *buf, int len,
 static int AsyncTCPSocketDoOneMsg(AsyncSocket *s, Bool read, int timeoutMS);
 static int AsyncTCPSocketWaitForReadMultiple(AsyncSocket **asock, int numSock,
                                              int timeoutMS, int *outIdx);
+static int AsyncTCPSocketSetOption(AsyncSocket *asyncSocket,
+                                   AsyncSocketOpts_Layer layer,
+                                   AsyncSocketOpts_ID optID,
+                                   const void *valuePtr,
+                                   socklen_t inBufLen);
+static int AsyncTCPSocketGetOption(AsyncSocket *asyncSocket,
+                                   AsyncSocketOpts_Layer layer,
+                                   AsyncSocketOpts_ID optID,
+                                   void *valuePtr,
+                                   socklen_t *outBufLen);
+
 
+/* Local constants. */
 
 static const AsyncSocketVTable asyncTCPSocketVTable = {
    AsyncSocketGetState,
+   AsyncTCPSocketSetOption,
+   AsyncTCPSocketGetOption,
    AsyncTCPSocketGetGenericErrno,
    AsyncTCPSocketGetFd,
    AsyncTCPSocketGetRemoteIPStr,
    AsyncTCPSocketGetINETIPStr,
    AsyncTCPSocketGetPort,
-   AsyncTCPSocketUseNodelay,
-   AsyncTCPSocketSetTCPTimeouts,
-   AsyncTCPSocketSetBufferSizes,
-   AsyncTCPSocketSetSendLowLatencyMode,
    AsyncTCPSocketSetCloseOptions,
    AsyncTCPSocketConnectSSL,
    AsyncTCPSocketStartSslConnect,
@@ -405,6 +410,8 @@ static const AsyncSocketVTable asyncTCPSocketVTable = {
 };
 
 
+/* Function bodies. */
+
 /*
  *----------------------------------------------------------------------
  *
@@ -2256,114 +2263,6 @@ AsyncSocket_AttachToSSLSock(SSLSock sslSock,                   // IN
 }
 
 
-/*
- *----------------------------------------------------------------------------
- *
- * AsyncTCPSocketUseNodelay --
- *
- *      Sets or unset TCP_NODELAY on the socket, which disables or
- *      enables Nagle's algorithm, respectively.
- *
- * Results:
- *      ASOCKERR_SUCCESS on success, ASOCKERR_GENERIC otherwise.
- *
- * Side Effects:
- *      Increased bandwidth usage for short messages on this socket
- *      due to TCP overhead, in exchange for lower latency.
- *
- *----------------------------------------------------------------------------
- */
-
-static int
-AsyncTCPSocketUseNodelay(AsyncSocket *base,  // IN/OUT:
-                         Bool nodelay)       // IN:
-{
-   AsyncTCPSocket *asock = TCPSocket(base);
-   int flag = nodelay ? 1 : 0;
-
-   ASSERT(AsyncTCPSocketIsLocked(asock));
-   if (setsockopt(asock->fd, IPPROTO_TCP, TCP_NODELAY,
-                  (const void *) &flag, sizeof(flag)) != 0) {
-      asock->genericErrno = Err_Errno();
-      LOG(0, (ASOCKPREFIX "could not set TCP_NODELAY, error %d: %s\n",
-              Err_Errno(), Err_ErrString()));
-      return ASOCKERR_GENERIC;
-   } else {
-      return ASOCKERR_SUCCESS;
-   }
-}
-
-
-/*
- *----------------------------------------------------------------------------
- *
- * AsyncTCPSocketSetTCPTimeouts --
- *
- *      Allow caller to set a number of TCP-specific timeout
- *      parameters on the socket for the active connection.
- *
- *      Parameters:
- *      keepIdle --  The number of seconds a TCP connection must be idle before
- *                   keep-alive probes are sent.
- *      keepIntvl -- The number of seconds between TCP keep-alive probes once
- *                   they are being sent.
- *      keepCnt   -- The number of keep-alive probes to send before killing
- *                   the connection if no response is received from the peer.
- *
- * Results:
- *      ASOCKERR_SUCCESS on success, ASOCKERR_GENERIC otherwise.
- *
- * Side Effects:
- *      None.
- *
- *----------------------------------------------------------------------------
- */
-
-static int
-AsyncTCPSocketSetTCPTimeouts(AsyncSocket *base,  // IN/OUT:
-                             int keepIdle,       // IN
-                             int keepIntvl,      // IN
-                             int keepCnt)        // IN
-{
-#ifdef VMX86_SERVER
-   AsyncTCPSocket *asock = TCPSocket(base);
-   int val;
-   int opt;
-
-   ASSERT(AsyncTCPSocketIsLocked(asock));
-
-   val = keepIdle;
-   opt = TCP_KEEPIDLE;
-   if (setsockopt(asock->fd, IPPROTO_TCP, opt,
-                  &val, sizeof val) != 0) {
-      goto error;
-   }
-
-   val = keepIntvl;
-   opt = TCP_KEEPINTVL;
-   if (setsockopt(asock->fd, IPPROTO_TCP, opt,
-                  &val, sizeof val) != 0) {
-      goto error;
-   }
-
-   val = keepCnt;
-   opt = TCP_KEEPCNT;
-   if (setsockopt(asock->fd, IPPROTO_TCP, opt,
-                  &val, sizeof val) != 0) {
-      goto error;
-   }
-
-   return ASOCKERR_SUCCESS;
-
-error:
-   asock->genericErrno = Err_Errno();
-   LOG(0, (ASOCKPREFIX "could not set TCP Timeout %d, error %d: %s\n",
-           opt, Err_Errno(), Err_ErrString()));
-#endif
-   return ASOCKERR_GENERIC;
-}
-
-
 /*
  *----------------------------------------------------------------------------
  *
@@ -3535,7 +3434,7 @@ AsyncTCPSocketDispatchSentBuffer(AsyncTCPSocket *s)         // IN
  *      for the socket.
  *
  * Results:
- *      ASOCKERR_SUCESS if everything worked, else ASOCKERR_GENERIC.
+ *      ASOCKERR_SUCCESS if everything worked, else ASOCKERR_GENERIC.
  *
  * Side effects:
  *      None.
@@ -5655,110 +5554,258 @@ AsyncTCPSocketStartSslAccept(AsyncSocket *base,                  // IN
 
 
 /*
- *-----------------------------------------------------------------------------
+ *----------------------------------------------------------------------------
  *
- * AsyncTCPSocketSetBufferSizes --
+ * AsyncTCPSocketSetOption --
  *
- *    Set socket level recv/send buffer sizes if they are less than given sizes.
+ *      This implementation of ->setOption() supports the following
+ *      options. Exact behavior of each cited optID is documented in the
+ *      comment header for that enum value declaration (for non-native options),
+ *      or `man setsockopt`/equivalent (for native options).
  *
- * Result
- *    TRUE: on success
- *    FALSE: on failure
+ *         - layer = SOL_SOCKET, optID =
+ *           SO_SNDBUF, SO_RCVBUF.
  *
- * Side-effects
- *    None
+ *         - layer = IPPROTO_TCP, optID =
+ *           TCP_NODELAY, TCP_KEEPINTVL, TCP_KEEPIDLE, TCP_KEEPCNT.
  *
- *-----------------------------------------------------------------------------
+ *         - layer = ASYNC_SOCKET_OPTS_LAYER_BASE, optID (type) =
+ *           ASYNC_SOCKET_OPT_SEND_LOW_LATENCY_MODE (Bool).
+ *
+ * Results:
+ *      ASOCKERR_SUCCESS on success, ASOCKERR_* otherwise.
+ *      Invalid option+layer yields ASOCKERR_INVAL.
+ *      Failure to set a native OS option yields ASOCKERR_GENERIC.
+ *      inBufLen being wrong (for the given option) yields undefined behavior.
+ *
+ * Side effects:
+ *      Depends on option.
+ *
+ *----------------------------------------------------------------------------
  */
 
-static Bool
-AsyncTCPSocketSetBufferSizes(AsyncSocket *base,   // IN
-                             int sendSz,          // IN
-                             int recvSz)          // IN
+static int
+AsyncTCPSocketSetOption(AsyncSocket *asyncSocket,     // IN/OUT
+                        AsyncSocketOpts_Layer layer,  // IN
+                        AsyncSocketOpts_ID optID,     // IN
+                        const void *valuePtr,         // IN
+                        socklen_t inBufLen)           // IN
 {
-   AsyncTCPSocket *asock = TCPSocket(base);
-   int err;
-   int buffSz;
-   int len = sizeof buffSz;
-   int sysErr;
-   int fd;
+   /* Maintenance: Keep this in sync with ...GetOption(). */
 
-   fd = asock->fd;
+   AsyncTCPSocket *tcpSocket = TCPSocket(asyncSocket);
+   Bool isSupported;
 
-   err = getsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *)&buffSz, &len);
-   if (err) {
-      sysErr = ASOCK_LASTERROR();
-      Warning(ASOCKPREFIX "Could not get recv buffer size for socket %d, "
-              "error %d: %s\n", fd, sysErr, Err_Errno2String(sysErr));
-      return FALSE;
+   switch ((int)layer)
+   {
+   case SOL_SOCKET:
+   case IPPROTO_TCP:
+   case ASYNC_SOCKET_OPTS_LAYER_BASE:
+      break;
+   default:
+      TCPSOCKLG0(tcpSocket,
+                 ("%s: Option layer [%d] (option [%d]) is not "
+                     "supported for TCP socket.\n",
+                  __FUNCTION__, (int)layer, optID));
+      return ASOCKERR_INVAL;
    }
 
-   if (buffSz < recvSz) {
-      buffSz = recvSz;
-      err = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *)&buffSz, len);
-      if (err) {
-         sysErr = ASOCK_LASTERROR();
-         Warning(ASOCKPREFIX "Could not set recv buffer size for socket %d "
-                 "to %d, error %d: %s\n", fd, buffSz,
-                 sysErr, Err_Errno2String(sysErr));
-         return FALSE;
+   /*
+    * layer is supported.
+    * Handle non-native options first.
+    */
+
+   if ((layer == ASYNC_SOCKET_OPTS_LAYER_BASE) &&
+       (optID == ASYNC_SOCKET_OPT_SEND_LOW_LATENCY_MODE)) {
+      ASSERT(inBufLen == sizeof(Bool));
+      tcpSocket->sendLowLatency = *((const Bool *)valuePtr);
+      TCPSOCKLG0(tcpSocket,
+                 ("%s: sendLowLatencyMode set to [%d].\n",
+                  __FUNCTION__, (int)tcpSocket->sendLowLatency));
+      return ASOCKERR_SUCCESS;
+   }
+
+   /*
+    * Handle native (setsockopt()) options from this point on.
+    *
+    * We need the level and option_name arguments for that call.
+    * Our design dictates that, for native options, simply option_name=optID.
+    * So just determine level from our layer enum (for native layers, the enum's
+    * ordinal value is set to the corresponding int level value). Therefore,
+    * level=layer.
+    *
+    * level and option_name are known. However, we only allow the setting of
+    * certain specific options. Anything else is an error.
+    */
+   isSupported = FALSE;
+   if (layer == SOL_SOCKET) {
+      switch (optID) {
+      case SO_SNDBUF:
+      case SO_RCVBUF:
+         isSupported = TRUE;
+      }
+   } else {
+      ASSERT((int)layer == IPPROTO_TCP);
+
+      switch (optID) {
+         /*
+          * Note: All but TCP_KEEPIDLE are available in Mac OS X (at least
+          * 10.11). iOS and Android are TBD. For now, let's keep it simple and
+          * make all these available in the two known OS where all 3 exist
+          * together, as they're typically often set as a group.
+          * TODO: Possibly enable for other OS in more fine-grained fashion.
+          */
+#if defined(__linux__) || defined(VMX86_SERVER)
+      case TCP_KEEPIDLE:
+      case TCP_KEEPINTVL:
+      case TCP_KEEPCNT:
+#endif
+      case TCP_NODELAY:
+         isSupported = TRUE;
       }
    }
 
-   err =  getsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *)&buffSz, &len);
-   if (err) {
-      sysErr = ASOCK_LASTERROR();
-      Warning(ASOCKPREFIX "Could not get send buffer size for socket %d, "
-              "error %d: %s\n", fd, sysErr, Err_Errno2String(sysErr));
-      return FALSE;
+   if (!isSupported) {
+      TCPSOCKLG0(tcpSocket,
+                 ("%s: Option layer/level [%d], option/name [%d]: "
+                     "could not set OS option for TCP socket; "
+                     "option not supported.\n",
+                  __FUNCTION__, (int)layer, optID));
+      return ASOCKERR_INVAL;
    }
 
-   if (buffSz < sendSz) {
-      buffSz = sendSz;
-      err = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *)&buffSz, len);
-      if (err) {
-         sysErr = ASOCK_LASTERROR();
-         Warning(ASOCKPREFIX "Could not set send buffer size for socket %d "
-                 "to %d, error %d: %s\n", fd, buffSz,
-                 sysErr, Err_Errno2String(sysErr));
-         return FALSE;
-      }
+   /* All good. Ready to actually set the OS option. */
+
+   if (setsockopt(tcpSocket->fd, layer, optID,
+                  valuePtr, inBufLen) != 0) {
+      tcpSocket->genericErrno = Err_Errno();
+      TCPSOCKLG0(tcpSocket,
+                 ("%s: Option layer/level [%d], option/name [%d]: "
+                     "could not set OS option for TCP socket; "
+                     "error [%d: %s].\n",
+                  __FUNCTION__, (int)layer, optID,
+                  tcpSocket->genericErrno,
+                  Err_Errno2String(tcpSocket->genericErrno)));
+      return ASOCKERR_GENERIC;
    }
 
-   return TRUE;
+   TCPSOCKLG0(tcpSocket,
+              ("%s: Option layer/level [%d], option/name [%d]: successfully "
+                  "set OS option for TCP socket.\n",
+               __FUNCTION__, (int)layer, optID));
+
+   return ASOCKERR_SUCCESS;
 }
 
 
 /*
- *-----------------------------------------------------------------------------
+ *----------------------------------------------------------------------------
  *
- * AsyncTCPSocketSetSendLowLatencyMode --
+ * AsyncTCPSocketGetOption --
  *
- *    Put the socket into a mode where we attempt to issue sends
- *    directly from within AsyncTCPSocket_Send().  Ordinarily, we would
- *    set up a Poll callback from within AsyncTCPSocket_Send(), which
- *    introduces some non-zero latency to the send path.  In
- *    low-latency-send mode, that delay is potentially avoided.  This
- *    does introduce a behavioural change; the send completion
- *    callback may be triggered before the call to Send() returns.  As
- *    not all clients may be expecting this, we don't enable this mode
- *    unless requested by the client.
+ *      This is the reverse of AsyncTCPSocketSetOption().
  *
- * Result
- *    ASOCKERR_*
+ * Results:
+ *      ASOCKERR_SUCCESS on success, ASOCKERR_* otherwise.
+ *      Invalid option+layer yields ASOCKERR_INVAL.
+ *      Failure to get a native OS option yields ASOCKERR_GENERIC.
+ *      *outBufLen being wrong (for the given option) at entry to function
+ *      yields undefined behavior.
  *
- * Side-effects
- *    See description above.
+ * Side effects:
+ *      None.
  *
- *-----------------------------------------------------------------------------
+ *----------------------------------------------------------------------------
  */
 
 static int
-AsyncTCPSocketSetSendLowLatencyMode(AsyncSocket *base,  // IN
-                                    Bool enable)        // IN
+AsyncTCPSocketGetOption(AsyncSocket *asyncSocket,     // IN/OUT
+                        AsyncSocketOpts_Layer layer,  // IN
+                        AsyncSocketOpts_ID optID,     // IN
+                        void *valuePtr,               // OUT
+                        socklen_t *outBufLen)         // IN/OUT
 {
-   AsyncTCPSocket *asock = TCPSocket(base);
-   asock->sendLowLatency = enable;
+   /*
+    * Maintenance: Keep this in sync with ...GetOption().
+    * Substantive comments are kept light to avoid redundancy (refer to the
+    * other function).
+    */
+
+   AsyncTCPSocket *tcpSocket = TCPSocket(asyncSocket);
+   Bool isSupported;
+
+   switch ((int)layer) {
+   case SOL_SOCKET:
+   case IPPROTO_TCP:
+   case ASYNC_SOCKET_OPTS_LAYER_BASE:
+      break;
+   default:
+      TCPSOCKLG0(tcpSocket,
+                 ("%s: Option layer [%d] (option [%d]) is not "
+                     "supported for TCP socket.\n",
+                  __FUNCTION__, (int)layer, optID));
+      return ASOCKERR_INVAL;
+   }
+
+   if ((layer == ASYNC_SOCKET_OPTS_LAYER_BASE) &&
+       (optID == ASYNC_SOCKET_OPT_SEND_LOW_LATENCY_MODE)) {
+      ASSERT(*outBufLen >= sizeof(Bool));
+      *outBufLen = sizeof(Bool);
+      *((Bool *)valuePtr) = tcpSocket->sendLowLatency;
+      TCPSOCKLG0(tcpSocket,
+                 ("%s: sendLowLatencyMode is [%d].\n",
+                  __FUNCTION__, (int)tcpSocket->sendLowLatency));
+      return ASOCKERR_SUCCESS;
+   }
+
+   isSupported = FALSE;
+   if (layer == SOL_SOCKET) {
+      switch (optID) {
+      case SO_SNDBUF:
+      case SO_RCVBUF:
+         isSupported = TRUE;
+      }
+   } else {
+      ASSERT((int)layer == IPPROTO_TCP);
+
+      switch (optID) {
+#ifdef __linux__
+      case TCP_KEEPIDLE:
+      case TCP_KEEPINTVL:
+      case TCP_KEEPCNT:
+#endif
+      case TCP_NODELAY:
+         isSupported = TRUE;
+      }
+   }
+
+   if (!isSupported) {
+      TCPSOCKLG0(tcpSocket,
+                 ("%s: Option layer/level [%d], option/name [%d]: "
+                     "could not get OS option for TCP socket; "
+                     "option not supported.\n",
+                  __FUNCTION__, (int)layer, optID));
+      return ASOCKERR_INVAL;
+   }
+
+   if (getsockopt(tcpSocket->fd, layer, optID,
+                  valuePtr, outBufLen) != 0) {
+      tcpSocket->genericErrno = Err_Errno();
+      TCPSOCKLG0(tcpSocket,
+                 ("%s: Option layer/level [%d], option/name [%d]: "
+                     "could not get OS option for TCP socket; "
+                     "error [%d: %s].\n",
+                  __FUNCTION__, (int)layer, optID,
+                  tcpSocket->genericErrno,
+                  Err_Errno2String(tcpSocket->genericErrno)));
+      return ASOCKERR_GENERIC;
+   }
+
+   TCPSOCKLG0(tcpSocket,
+              ("%s: Option layer/level [%d], option/name [%d]: successfully "
+                  "got OS option for TCP socket.\n",
+               __FUNCTION__, (int)layer, optID));
+
    return ASOCKERR_SUCCESS;
 }
 
index 7939b54adbb381bd779c78cad4021b5422dfa528..533d61428a514409d093501666bd96c5f751c302 100644 (file)
 
 #define INCLUDE_ALLOW_VMCORE
 #define INCLUDE_ALLOW_USERLEVEL
+
+#ifdef _WIN32
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#else
+#include <sys/socket.h>
+#endif
+
 #include "includeCheck.h"
 
 #if defined(__cplusplus)
@@ -191,6 +199,154 @@ typedef struct AsyncSocketNetworkStats {
    double packetLossPercent;     /* packet loss percentage */
 } AsyncSocketNetworkStats;
 
+
+/*
+ * The following covers all facilities involving dynamic socket options w.r.t.
+ * various async sockets, excluding the async socket options API on the
+ * sockets themselves, which can be found in asyncSocketVTable.h and those
+ * files implementing that API.
+ *
+ * Potential related future work is covered in
+ * asyncsocket/README-asyncSocketOptions-future-work.txt.
+ *
+ * Summary of dynamic socket options:
+ *
+ * Dynamic socket option = setting settable by the async socket API user
+ * including during the lifetime of the socket. An interface spiritually
+ * similar to setsockopt()'s seemed appropriate.
+ *
+ * The option-setting API looks as follows:
+ *
+ *   int ...SetOption(AsyncSocket *asyncSocket,
+ *                    AsyncSocketOpts_Layer layer, // enum type
+ *                    AsyncSocketOpts_ID optID, // an integer type
+ *                    const void *valuePtr,
+ *                    size_t inBufLen)
+ *
+ * Both native (setsockopt()) and non-native (usually, struct member)
+ * options are supported. layer and optID arguments are conceptually similar
+ * to setsockopt() level and option_name arguments, respectively.
+ *
+ * FOR NATIVE (setsockopt()) OPTIONS:
+ *    layer = setsockopt() level value.
+ *    optID = setsockopt() option_name value.
+ *
+ * FOR NON-NATIVE (struct member inside socket impl.) OPTIONS:
+ *    layer = ..._BASE, ..._TCP, ..._FEC, etc.
+ *       (pertains to the various AsyncSocket types);
+ *    optID = value from enum type appropriate to the chosen layer.
+ *
+ * Examples (prefixes omitted for space):
+ *
+ *    -- NATIVE OPTIONS --
+ *    optID          | layer       | <= | ssopt() level | ssopt() option_name
+ *    ---------------+-------------+----+---------------+--------------------
+ *    == option_name | == level    | <= | SOL_SOCKET    | SO_SNDBUF
+ *    == option_name | == level    | <= | IPPROTO_TCP   | TCP_NODELAY
+ *
+ *    -- NON-NATIVE OPTIONS --
+ *    optID                          | layer | <= | AsyncSocket type(s)
+ *    -------------------------------+-------+----+--------------------
+ *    _SEND_LOW_LATENCY_MODE         | _BASE | <= | any
+ *       (enum AsyncSocket_OptID)    |       |    |
+ *    _ALLOW_DECREASING_BUFFER_SIZE  | _TCP  | <= | AsyncTCPSocket
+ *       (enum AsyncTCPSocket_OptID) |       |    |
+ *    _MAX_CWND                      | _FEC  | <= | FECAsyncSocket
+ *       (enum FECAsyncSocket_OptID) |       |    |
+ *
+ * Socket option lists for each non-native layer are just enums. Each socket
+ * type should declare its own socket option enum in its own .h file; e.g., see
+ * AsyncTCPSocket_OptID in this file. Some option lists apply to all async
+ * sockets; these are also here in asyncsocket.h.
+ *
+ * The only way in which different socket option layers coexist in the same
+ * file is the layer enum, AsyncSocketOpts_Layer, in the present file,
+ * which enumerates all possible layers.
+ *
+ * The lack of any other cross-pollution between different non-native option
+ * lists' containing files is a deliberate design choice.
+ */
+
+/*
+ * Integral type used for the optID argument to ->setOption() async socket API.
+ *
+ * For a non-native option, use an enum value for your socket type.
+ * (Example: ASYNC_TCP_SOCKET_OPT_ALLOW_DECREASING_BUFFER_SIZE
+ * of type AsyncTCPSocket_OptID, which would apply to TCP sockets only.)
+ *
+ * For a native (setsockopt()) option, use the setsockopt() integer directly.
+ * (Example: TCP_NODELAY.)
+ *
+ * Let's use a typedef as a small bit of abstraction and to be able to easily
+ * change it to size_t, if (for example) we start indexing arrays with this
+ * thing.
+ */
+typedef int AsyncSocketOpts_ID;
+
+/*
+ * Enum type used for the layer argument to ->setOption() async socket API.
+ * As explained in the summary comment above, this
+ * informs the particular ->setOption() implementation how to interpret
+ * the accompanying optID integer value, as it may refer to one of several
+ * option lists; and possible different socket instances (not as of this
+ * writing).
+ *
+ * If editing, see summary comment above first for background.
+ *
+ * The values explicitly in this enum are for non-native options.
+ * For native options, simply use the level value as for setsockopt().
+ *
+ * Ordinal values for all these non-native layers must not clash
+ * with the native levels; hence the `LEVEL + CONSTANT` trick
+ * just below.
+ */
+typedef enum {
+
+   /*
+    * Used when optID applies to a non-native socket option applicable to ANY
+    * async socket type.
+    */
+   ASYNC_SOCKET_OPTS_LAYER_BASE = SOL_SOCKET + 1000,
+
+   /*
+    * Next enums must follow the above ordinally, so just:
+    *    ASYNC_SOCKET_OPTS_LAYER_<layer name 1>,
+    *    ASYNC_SOCKET_OPTS_LAYER_<layer name 2>, ...
+    */
+} AsyncSocketOpts_Layer;
+
+/*
+ * Enum type used for the OptId argument to ->setOption() async socket API,
+ * when optID refers to a non-native option of any AsyncSocket regardless
+ * of type.
+ */
+typedef enum {
+   /*
+    * Bool indicating whether to put the socket into a mode where we attempt
+    * to issue sends directly from within ->send(). Ordinarily
+    * (FALSE), we would set up a Poll callback from within ->send(),
+    * which introduces some non-zero latency to the send path. In
+    * low-latency-send mode (TRUE), that delay is potentially avoided. This
+    * does introduce a behavioral change; the send completion
+    * callback may be triggered before the call to ->send() returns. As
+    * not all clients may be expecting this, we don't enable this mode
+    * unless requested by the client.
+    *
+    * Default: FALSE.
+    */
+   ASYNC_SOCKET_OPT_SEND_LOW_LATENCY_MODE
+} AsyncSocket_OptID;
+
+/*
+ * Note: If you need to add a non-native option that applies to AsyncTCPSockets
+ * only, you'd probably introduce an enum here named AsyncTCPSocket_OptID; and
+ * at least one layer named ASYNC_SOCKET_OPTS_LAYER_TCP in the enum
+ * AsyncSocketOpts_Layer.
+ */
+
+
+/* API functions for all AsyncSockets. */
+
 AsyncSocketState AsyncSocket_GetState(AsyncSocket *sock);
 
 const char * AsyncSocket_Err2String(int err);
@@ -404,16 +560,21 @@ AsyncSocket *AsyncSocket_AttachToSSLSock(struct SSLSockStruct *sslSock,
                                          AsyncSocketPollParams *pollParams,
                                          int *error);
 
-/*
- * Enable or disable TCP_NODELAY on this AsyncSocket.
- */
-int AsyncSocket_UseNodelay(AsyncSocket *asock, Bool nodelay);
-
-/*
- * Set TCP timeout values on this AsyncSocket.
- */
-int AsyncSocket_SetTCPTimeouts(AsyncSocket *asock, int keepIdle,
-                               int keepIntvl, int keepCnt);
+int AsyncSocket_UseNodelay(AsyncSocket *asyncSocket, Bool nodelay);
+int AsyncSocket_SetTCPTimeouts(AsyncSocket *asyncSocket,
+                               int keepIdleSec, int keepIntvlSec, int keepCnt);
+Bool AsyncSocket_EstablishMinBufferSizes(AsyncSocket *asyncSocket,
+                                         int sendSz,
+                                         int recvSz);
+int AsyncSocket_SetSendLowLatencyMode(AsyncSocket *asyncSocket, Bool enable);
+int AsyncSocket_SetOption(AsyncSocket *asyncSocket,
+                          AsyncSocketOpts_Layer layer,
+                          AsyncSocketOpts_ID optID,
+                          const void *valuePtr, socklen_t inBufLen);
+int AsyncSocket_GetOption(AsyncSocket *asyncSocket,
+                          AsyncSocketOpts_Layer layer,
+                          AsyncSocketOpts_ID optID,
+                          void *valuePtr, socklen_t *outBufLen);
 
 /*
  * Waits until at least one packet is received or times out.
@@ -500,13 +661,6 @@ int AsyncSocket_CancelCbForClose(AsyncSocket *asock);
 int AsyncSocket_SetErrorFn(AsyncSocket *asock, AsyncSocketErrorFn errorFn,
                            void *clientData);
 
-/*
- * Set socket level recv/send buffer sizes if they are less than given sizes.
- */
-Bool AsyncSocket_SetBufferSizes(AsyncSocket *asock,  // IN
-                                int sendSz,    // IN
-                                int recvSz);   // IN
-
 /*
  * Set optional AsyncSocket_Close() behaviors.
  */
@@ -542,11 +696,6 @@ int AsyncSocket_SetWebSocketCookie(AsyncSocket *asock,         // IN
  */
 uint16 AsyncSocket_GetWebSocketCloseStatus(AsyncSocket *asock);
 
-/*
- * Set low-latency mode for sends:
- */
-int AsyncSocket_SetSendLowLatencyMode(AsyncSocket *asock, Bool enable);
-
 /*
  * Get negotiated websocket protocol
  */
index d45ee7528b05b2209b40fadd9fd413917e6efe6c..6460a73d1e110055696b6941619d5d3e33bd0523 100644 (file)
@@ -56,7 +56,7 @@
 // VMX86_DESKTOP must be last because it is the default and is always defined.
 #elif defined(VMX86_DESKTOP)
    // WORKSTATION_VERSION_NUMBER below has to match this
-   #define PRODUCT_VERSION    13,0,0,PRODUCT_BUILD_NUMBER_NUMERIC
+   #define PRODUCT_VERSION    14,0,0,PRODUCT_BUILD_NUMBER_NUMERIC
 #else
    /* Generic catch-all. */
    #define PRODUCT_VERSION    0,0,0,PRODUCT_BUILD_NUMBER_NUMERIC
  * ALSO, leave FOO_VERSION at e.x.p on all EXCEPT release branches.
  * lmclient.h has a FLEX_VERSION struct so the versionPrefix can't be FLEX
  */
-#define WORKSTATION_VERSION_NUMBER "13.0.0" /* this version number should always match real WS version number */
+#define WORKSTATION_VERSION_NUMBER "14.0.0" /* this version number should always match real WS version number */
 #define WORKSTATION_VERSION "e.x.p"
-#define PLAYER_VERSION_NUMBER "13.0.0" /* this version number should always match real Player version number */
+#define PLAYER_VERSION_NUMBER "14.0.0" /* this version number should always match real Player version number */
 #define PLAYER_VERSION "e.x.p"
 #define VMRC_VERSION_NUMBER "10.0.0" /* this version number should always match real VMRC version number */
 #define VMRC_VERSION "10.0.0"
 #define SSO_VERSION "1.0.0"
 #define WBC_VERSION "5.1.0"
 #define SDK_VERSION "4.1.0"
-#define FOUNDRY_VERSION "1.15.0"
-#define FOUNDRY_FILE_VERSION 1,15,0,PRODUCT_BUILD_NUMBER_NUMERIC
+#define FOUNDRY_VERSION "1.17.0"
+#define FOUNDRY_FILE_VERSION 1,17,0,PRODUCT_BUILD_NUMBER_NUMERIC
 #define VMLS_VERSION "e.x.p"
 #define VLICENSE_VERSION "1.1.5"
 #define DDK_VERSION "e.x.p"
 #    if defined(__APPLE__)
 #      define PRODUCT_LICENSE_VERSION PRODUCT_MAC_DESKTOP_VERSION_STRING_FOR_LICENSE
 #    else
-#      define PRODUCT_LICENSE_VERSION "12.0"
+#      define PRODUCT_LICENSE_VERSION "14.0"
 #    endif
 #  else
 #    define PRODUCT_LICENSE_VERSION "0.0"
index 1a1aa3fc93b8a4458c916afa4711f5da6f01678c..f5a9974b87b848327c70f33ad81a8f0133e4248e 100644 (file)
@@ -1107,9 +1107,8 @@ RpcInConnectDone(AsyncSocket *asock,   // IN
       goto exit;
    }
 
-
-   if (!AsyncSocket_SetBufferSizes(asock, RPCIN_MIN_SEND_BUF_SIZE,
-                                   RPCIN_MIN_RECV_BUF_SIZE)) {
+   if (!AsyncSocket_EstablishMinBufferSizes(asock, RPCIN_MIN_SEND_BUF_SIZE,
+                                            RPCIN_MIN_RECV_BUF_SIZE)) {
       goto exit;
    }
 
index f0fc0e105e4c55fee1648af6c13d44dd63225f1e..f7fb0f8e4eaefcfcc2e71997a2e1e28c2d5b1af0 100644 (file)
@@ -1012,8 +1012,8 @@ VmxListenSockConnectedCb(AsyncSocket *asock,    // IN
       goto exit;
    }
 
-   if (!AsyncSocket_SetBufferSizes(asock, sendBufSize, recvBufSize)) {
-      g_info("Cannot set VSOCK buffer sizes, closing socket %d\n", fd);
+   if (!AsyncSocket_EstablishMinBufferSizes(asock, sendBufSize, recvBufSize)) {
+      g_info("Cannot set VSOCK buffer size minima, closing socket %d\n", fd);
       goto exit;
    }
 
@@ -1419,7 +1419,7 @@ RmqListenSockConnectedCb(AsyncSocket *asock,    // IN
       goto exit;
    }
 
-   if (!AsyncSocket_SetBufferSizes(asock, sendBufSize, recvBufSize)) {
+   if (!AsyncSocket_EstablishMinBufferSizes(asock, sendBufSize, recvBufSize)) {
       g_info("Closing socket %d due to error.\n", fd);
       goto exit;
    }