]> git.ipfire.org Git - thirdparty/open-vm-tools.git/commitdiff
Secure GuestRPC channels
authorVMware, Inc <>
Wed, 18 Sep 2013 03:13:02 +0000 (20:13 -0700)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Sun, 22 Sep 2013 01:27:21 +0000 (18:27 -0700)
Use VMCI/Vsocket instead of backdoor for GuestRPC RPCI channels.  For
privileged channels, the guest side can bind port to less than 1024, VMX
can then verify the binding and enforce privileged commands can only be
ran by privileged users.

Signed-off-by: Dmitry Torokhov <dtor@vmware.com>
14 files changed:
open-vm-tools/lib/include/vmware/guestrpc/tclodefs.h
open-vm-tools/lib/include/vmware/tools/guestrpc.h
open-vm-tools/lib/rpcChannel/Makefile.am
open-vm-tools/lib/rpcChannel/bdoorChannel.c
open-vm-tools/lib/rpcChannel/rpcChannel.c
open-vm-tools/lib/rpcChannel/rpcChannelInt.h
open-vm-tools/lib/rpcChannel/simpleSocket.c [new file with mode: 0644]
open-vm-tools/lib/rpcChannel/simpleSocket.h [new file with mode: 0644]
open-vm-tools/lib/rpcChannel/vsockChannel.c [new file with mode: 0644]
open-vm-tools/lib/rpcIn/rpcin.c
open-vm-tools/services/plugins/dndcp/copyPasteDnDX11.cpp
open-vm-tools/services/vmtoolsd/toolsRpc.c
open-vm-tools/tests/vmrpcdbg/Makefile.am
open-vm-tools/tests/vmrpcdbg/debugChannel.c

index e53f16a26f223593b915ad6f1d7cbda9f0cd6f25..e88d169d952b12e71f85fff58cc4d8e3cdd2da0f 100644 (file)
@@ -50,7 +50,8 @@
 /** Reply from host when the command is not recognized. */
 #define RPCI_UNKNOWN_COMMAND      "Unknown command"
 
-#define GUESTRPC_VSOCK_LISTEN_PORT      975
+#define GUESTRPC_TCLO_VSOCK_LISTEN_PORT      975
+#define GUESTRPC_RPCI_VSOCK_LISTEN_PORT      976
 
 /*
  * Tools options.
 #define  RPCIN_TCLO_PING                 0x1
 
 enum {
-   RPCINPKT_TYPE_DATA = 1,
-   RPCINPKT_TYPE_PING
+   GUESTRPCPKT_TYPE_DATA = 1,
+   GUESTRPCPKT_TYPE_PING
 };
 
 enum {
-   RPCINPKT_FIELD_TYPE = 1,
-   RPCINPKT_FIELD_PAYLOAD
+   GUESTRPCPKT_FIELD_TYPE = 1,
+   GUESTRPCPKT_FIELD_PAYLOAD
 };
 
 #endif /* _TCLODEFS_H_ */
index a12767b5fe232641f4ce761adbb5b5d29e75c4ff..4cb6907d0db96fd72ed2ad0da3d2ace2bcb28570 100644 (file)
@@ -45,7 +45,7 @@ G_BEGIN_DECLS
 #define RPCIN_SETRETVALS  RpcChannel_SetRetVals
 #define RPCIN_SETRETVALSF RpcChannel_SetRetValsF
 
-struct RpcChannel;
+typedef struct _RpcChannel RpcChannel;
 
 /** Data structure passed to RPC callbacks. */
 typedef struct RpcInData {
@@ -76,6 +76,12 @@ typedef struct RpcInData {
    void *clientData;
 } RpcInData;
 
+typedef enum RpcChannelType {
+   RPCCHANNEL_TYPE_INACTIVE,
+   RPCCHANNEL_TYPE_BKDOOR,
+   RPCCHANNEL_TYPE_PRIV_VSOCK,
+   RPCCHANNEL_TYPE_UNPRIV_VSOCK
+} RpcChannelType;
 
 /**
  * Type for RpcIn callbacks. The callback function is responsible for
@@ -108,21 +114,6 @@ typedef struct RpcChannelCallback {
    size_t            xdrInSize;
 } RpcChannelCallback;
 
-
-typedef gboolean (*RpcChannelStartFn)(struct RpcChannel *);
-typedef void (*RpcChannelStopFn)(struct RpcChannel *);
-typedef void (*RpcChannelShutdownFn)(struct RpcChannel *);
-typedef gboolean (*RpcChannelSendFn)(struct RpcChannel *,
-                                     char const *data,
-                                     size_t dataLen,
-                                     char **result,
-                                     size_t *resultLen);
-typedef void (*RpcChannelSetupFn)(struct RpcChannel *chan,
-                                  GMainContext *mainCtx,
-                                  const char *appName,
-                                  gpointer appCtx);
-
-
 /**
  * Signature for the callback function called after a channel reset.
  *
@@ -130,80 +121,25 @@ typedef void (*RpcChannelSetupFn)(struct RpcChannel *chan,
  * @param[in]  success  Whether reset was successful.
  * @param[in]  data     Client data.
  */
-typedef void (*RpcChannelResetCb)(struct RpcChannel *chan,
+typedef void (*RpcChannelResetCb)(RpcChannel *chan,
                                   gboolean success,
                                   gpointer data);
 
+gboolean
+RpcChannel_Start(RpcChannel *chan);
 
-/** Defines the interface between the application and the RPC channel. */
-typedef struct RpcChannel {
-   RpcChannelStartFn       start;
-   RpcChannelStopFn        stop;
-   RpcChannelSendFn        send;
-   RpcChannelSetupFn       setup;
-   RpcChannelShutdownFn    shutdown;
-   gpointer                _private;
-} RpcChannel;
-
-
-/**
- * Wrapper for the start function of an RPC channel struct.
- *
- * @param[in]  chan        The RPC channel instance.
- *
- * @return TRUE on success.
- */
-
-G_INLINE_FUNC gboolean
-RpcChannel_Start(RpcChannel *chan)
-{
-   g_return_val_if_fail(chan != NULL, FALSE);
-   g_return_val_if_fail(chan->start != NULL, FALSE);
-
-   return chan->start(chan);
-}
-
-
-/**
- * Wrapper for the stop function of an RPC channel struct.
- *
- * @param[in]  chan        The RPC channel instance.
- */
-
-G_INLINE_FUNC void
-RpcChannel_Stop(RpcChannel *chan)
-{
-   g_return_if_fail(chan != NULL);
-   g_return_if_fail(chan->stop != NULL);
-
-   chan->stop(chan);
-}
-
+void
+RpcChannel_Stop(RpcChannel *chan);
 
-/**
- * Wrapper for the send function of an RPC channel struct.
- *
- * @param[in]  chan        The RPC channel instance.
- * @param[in]  data        Data to send.
- * @param[in]  dataLen     Number of bytes to send.
- * @param[out] result      Response from other side (should be freed by caller).
- * @param[out] resultLen   Number of bytes in response.
- *
- * @return The status from the remote end (TRUE if call was successful).
- */
+RpcChannelType
+RpcChannel_GetType(RpcChannel *chan);
 
-G_INLINE_FUNC gboolean
+gboolean
 RpcChannel_Send(RpcChannel *chan,
                 char const *data,
                 size_t dataLen,
                 char **result,
-                size_t *resultLen)
-{
-   g_return_val_if_fail(chan != NULL, FALSE);
-   g_return_val_if_fail(chan->send != NULL, FALSE);
-
-   return chan->send(chan, data, dataLen, result, resultLen);
-}
+                size_t *resultLen);
 
 gboolean
 RpcChannel_BuildXdrCommand(const char *cmd,
@@ -215,6 +151,9 @@ RpcChannel_BuildXdrCommand(const char *cmd,
 RpcChannel *
 RpcChannel_Create(void);
 
+void
+RpcChannel_Shutdown(RpcChannel *chan);
+
 gboolean
 RpcChannel_Destroy(RpcChannel *chan);
 
@@ -247,7 +186,16 @@ void
 RpcChannel_UnregisterCallback(RpcChannel *chan,
                               RpcChannelCallback *rpc);
 
+gboolean
+RpcChannel_SendOneRaw(const char *data,
+                      size_t dataLen,
+                      char **result,
+                      size_t *resultLen);
+
+RpcChannel *
+RpcChannel_New(void);
 
+/* This is deprecated, call RpcChannel_New instead */
 RpcChannel *
 BackdoorChannel_New(void);
 
index 96e956a9ab6a90daca3d84c6c987820230d5238a..f3d54873a6a92507874f02e12b9fb91458ac399f 100644 (file)
@@ -20,6 +20,8 @@ noinst_LTLIBRARIES = libRpcChannel.la
 libRpcChannel_la_SOURCES =
 libRpcChannel_la_SOURCES += bdoorChannel.c
 libRpcChannel_la_SOURCES += rpcChannel.c
+libRpcChannel_la_SOURCES += vsockChannel.c
+libRpcChannel_la_SOURCES += simpleSocket.c
 
 libRpcChannel_la_CPPFLAGS =
 libRpcChannel_la_CPPFLAGS += @VMTOOLS_CPPFLAGS@
index c3e11a3f7967ce6c1cc39f6a77185546985b10ec..c709c53d09f85b9fb6ed6db397df2dd3ded18d41 100644 (file)
 #include "rpcout.h"
 #include "util.h"
 
-/** Max amount of time (in .01s) that the RpcIn loop will sleep for. */
-#define RPCIN_MAX_DELAY    10
-
 typedef struct BackdoorChannel {
-   GMainContext  *mainCtx;
-   GStaticMutex   outLock;
-   RpcIn         *in;
-   RpcOut        *out;
-   gboolean       inStarted;
-   gboolean       outStarted;
+   RpcOut           *out;
 } BackdoorChannel;
 
 
-/**
- * Initializes internal state for the inbound channel.
- *
- * @param[in]  chan     The RPC channel instance.
- * @param[in]  ctx      Main application context.
- * @param[in]  appName  Unused.
- * @param[in]  appCtx   Unused.
- */
-
-static void
-RpcInSetup(RpcChannel *chan,
-           GMainContext *ctx,
-           const char *appName,
-           gpointer appCtx)
-{
-   BackdoorChannel *bdoor = chan->_private;
-   bdoor->mainCtx = g_main_context_ref(ctx);
-   bdoor->in = RpcIn_Construct(ctx, RpcChannel_Dispatch, chan);
-   ASSERT(bdoor->in != NULL);
-}
-
-
 /**
  * Starts the RpcIn loop and the RpcOut channel.
  *
@@ -75,30 +45,22 @@ RpcInSetup(RpcChannel *chan,
  */
 
 static gboolean
-RpcInStart(RpcChannel *chan)
+BkdoorChannelStart(RpcChannel *chan)
 {
    gboolean ret = TRUE;
    BackdoorChannel *bdoor = chan->_private;
 
-   if (bdoor->outStarted) {
-      /* Already started. Make sure both channels are in sync and return. */
-      ASSERT(bdoor->in == NULL || bdoor->inStarted);
-      return ret;
-   } else {
-      ASSERT(bdoor->in == NULL || !bdoor->inStarted);
-   }
-
-   if (bdoor->in != NULL) {
-      ret = RpcIn_start(bdoor->in, RPCIN_MAX_DELAY, RpcChannel_Error, chan);
-   }
+   ret = chan->in == NULL || chan->inStarted;
    if (ret) {
       ret = RpcOut_start(bdoor->out);
       if (!ret) {
-         RpcIn_stop(bdoor->in);
+         if (chan->inStarted) {
+            RpcIn_stop(chan->in);
+            chan->inStarted = FALSE;
+         }
       }
    }
-   bdoor->inStarted = (bdoor->in != NULL);
-   bdoor->outStarted = TRUE;
+   chan->outStarted = ret;
    return ret;
 }
 
@@ -115,28 +77,17 @@ RpcInStart(RpcChannel *chan)
  */
 
 static void
-RpcInStop(RpcChannel *chan)
+BkdoorChannelStop(RpcChannel *chan)
 {
    BackdoorChannel *bdoor = chan->_private;
 
-   g_static_mutex_lock(&bdoor->outLock);
    if (bdoor->out != NULL) {
-      if (bdoor->outStarted) {
+      if (chan->outStarted) {
          RpcOut_stop(bdoor->out);
       }
-      bdoor->outStarted = FALSE;
+      chan->outStarted = FALSE;
    } else {
-      ASSERT(!bdoor->outStarted);
-   }
-   g_static_mutex_unlock(&bdoor->outLock);
-
-   if (bdoor->in != NULL) {
-      if (bdoor->inStarted) {
-         RpcIn_stop(bdoor->in);
-      }
-      bdoor->inStarted = FALSE;
-   } else {
-      ASSERT(!bdoor->inStarted);
+      ASSERT(!chan->outStarted);
    }
 }
 
@@ -150,19 +101,13 @@ RpcInStop(RpcChannel *chan)
  */
 
 static void
-RpcInShutdown(RpcChannel *chan)
+BkdoorChannelShutdown(RpcChannel *chan)
 {
    BackdoorChannel *bdoor = chan->_private;
-   RpcInStop(chan);
-   if (bdoor->in != NULL) {
-      RpcIn_Destruct(bdoor->in);
-   }
+   BkdoorChannelStop(chan);
    RpcOut_Destruct(bdoor->out);
-   g_static_mutex_free(&bdoor->outLock);
-   if (bdoor->mainCtx != NULL) {
-      g_main_context_unref(bdoor->mainCtx);
-   }
    g_free(bdoor);
+   chan->_private = NULL;
 }
 
 
@@ -179,19 +124,18 @@ RpcInShutdown(RpcChannel *chan)
  */
 
 static gboolean
-RpcInSend(RpcChannel *chan,
-          char const *data,
-          size_t dataLen,
-          char **result,
-          size_t *resultLen)
+BkdoorChannelSend(RpcChannel *chan,
+                  char const *data,
+                  size_t dataLen,
+                  char **result,
+                  size_t *resultLen)
 {
    gboolean ret = FALSE;
    const char *reply;
    size_t replyLen;
    BackdoorChannel *bdoor = chan->_private;
 
-   g_static_mutex_lock(&bdoor->outLock);
-   if (!bdoor->outStarted) {
+   if (!chan->outStarted) {
       goto exit;
    }
 
@@ -225,7 +169,7 @@ RpcInSend(RpcChannel *chan,
       } else {
          g_warning("Couldn't restart RpcOut channel; bad things may happen "
                    "until the RPC channel is reset.\n");
-         bdoor->outStarted = FALSE;
+         chan->outStarted = FALSE;
       }
    }
 
@@ -248,11 +192,50 @@ RpcInSend(RpcChannel *chan,
    }
 
 exit:
-   g_static_mutex_unlock(&bdoor->outLock);
    return ret;
 }
 
 
+/**
+ * Return the channel type.
+ *
+ * @param[in]  chan     RpcChannel
+ *
+ * @return backdoor channel type.
+ */
+
+static RpcChannelType
+BkdoorChannelGetType(RpcChannel *chan)
+{
+   return RPCCHANNEL_TYPE_BKDOOR;
+}
+
+
+/**
+ * Helper function to setup RpcChannel callbacks.
+ *
+ * @param[in]  chan     RpcChannel
+ */
+
+static void
+BackdoorChannelSetCallbacks(RpcChannel *chan)
+{
+   static RpcChannelFuncs funcs = {
+      BkdoorChannelStart,
+      BkdoorChannelStop,
+      BkdoorChannelSend,
+      NULL,
+      BkdoorChannelShutdown,
+      BkdoorChannelGetType,
+      NULL,
+      NULL
+   };
+
+   ASSERT(chan);
+   chan->funcs = &funcs;
+}
+
+
 /**
  * Creates a new RpcChannel channel that uses the backdoor for communication.
  *
@@ -268,20 +251,42 @@ BackdoorChannel_New(void)
    ret = RpcChannel_Create();
    bdoor = g_malloc0(sizeof *bdoor);
 
-   g_static_mutex_init(&bdoor->outLock);
    bdoor->out = RpcOut_Construct();
    ASSERT(bdoor->out != NULL);
 
-   bdoor->inStarted = FALSE;
-   bdoor->outStarted = FALSE;
+   ret->inStarted = FALSE;
+   ret->outStarted = FALSE;
 
-   ret->start = RpcInStart;
-   ret->stop = RpcInStop;
-   ret->send = RpcInSend;
-   ret->setup = RpcInSetup;
-   ret->shutdown = RpcInShutdown;
+   BackdoorChannelSetCallbacks(ret);
    ret->_private = bdoor;
 
    return ret;
 }
 
+
+/**
+ * Fall back to backdoor when another type of RpcChannel fails to start.
+ *
+ * @param[in]  chan     RpcChannel
+ *
+ * @return TRUE on success.
+ */
+
+gboolean
+BackdoorChannel_Fallback(RpcChannel *chan)
+{
+   BackdoorChannel *bdoor;
+
+   ASSERT(chan);
+   ASSERT(chan->_private == NULL);
+
+   bdoor = g_malloc0(sizeof *bdoor);
+   bdoor->out = RpcOut_Construct();
+   ASSERT(bdoor->out != NULL);
+
+   BackdoorChannelSetCallbacks(chan);
+   chan->_private = bdoor;
+
+   return chan->funcs->start(chan);
+}
+
index 8c4b4ef2d211b6dc739538de21ed96d4f0041f30..9ae9393ae09ab04e61a94dd00acaf16b06a9f88c 100644 (file)
@@ -30,6 +30,7 @@
 #include "strutil.h"
 #include "vmxrpc.h"
 #include "xdrutil.h"
+#include "rpcin.h"
 
 /** Internal state of a channel. */
 typedef struct RpcChannelInt {
@@ -46,10 +47,11 @@ typedef struct RpcChannelInt {
    guint                   rpcErrorCount;
 } RpcChannelInt;
 
-
 /** Max number of times to attempt a channel restart. */
 #define RPCIN_MAX_RESTARTS 60
 
+#define LGPFX "RpcChannel: "
+
 static gboolean
 RpcChannelPing(RpcInData *data);
 
@@ -409,8 +411,8 @@ RpcChannel_Destroy(RpcChannel *chan)
    size_t i;
    RpcChannelInt *cdata = (RpcChannelInt *) chan;
 
-   if (cdata->impl.shutdown != NULL) {
-      cdata->impl.shutdown(chan);
+   if (cdata->impl.funcs != NULL && cdata->impl.funcs->shutdown != NULL) {
+      cdata->impl.funcs->shutdown(chan);
    }
 
    RpcChannel_UnregisterCallback(chan, &cdata->resetReg);
@@ -512,8 +514,12 @@ RpcChannel_Setup(RpcChannel *chan,
       RpcChannel_RegisterCallback(chan, &gRpcHandlers[i]);
    }
 
-   if (cdata->impl.setup != NULL) {
-      cdata->impl.setup(&cdata->impl, mainCtx, appName, appCtx);
+   if (chan->funcs != NULL && chan->funcs->setup != NULL) {
+      chan->funcs->setup(chan, mainCtx, appName, appCtx);
+   } else {
+      chan->mainCtx = g_main_context_ref(mainCtx);
+      chan->in = RpcIn_Construct(mainCtx, RpcChannel_Dispatch, chan);
+      ASSERT(chan->in != NULL);
    }
 }
 
@@ -615,3 +621,281 @@ RpcChannel_UnregisterCallback(RpcChannel *chan,
    }
 }
 
+
+/**
+ * Create an RpcChannel instance using a prefered channel implementation,
+ * currently this is VSockChannel.
+ *
+ * @return  RpcChannel
+ */
+
+RpcChannel *
+RpcChannel_New(void)
+{
+   RpcChannel *chan;
+#if defined(linux) || defined(_WIN32)
+   chan = VSockChannel_New();
+#else
+   chan = BackdoorChannel_New();
+#endif
+   if (chan) {
+      g_static_mutex_init(&chan->outLock);
+   }
+   return chan;
+}
+
+
+/**
+ * Wrapper for the shutdown function of an RPC channel struct.
+ *
+ * @param[in]  chan        The RPC channel instance.
+ */
+
+void
+RpcChannel_Shutdown(RpcChannel *chan)
+{
+   if (chan != NULL) {
+      g_static_mutex_free(&chan->outLock);
+   }
+
+   if (chan != NULL && chan->funcs != NULL && chan->funcs->shutdown != NULL) {
+      if (chan->in != NULL) {
+         RpcIn_Destruct(chan->in);
+      }
+      if (chan->mainCtx != NULL) {
+         g_main_context_unref(chan->mainCtx);
+      }
+      chan->funcs->shutdown(chan);
+   }
+}
+
+
+/**
+ * Start an RPC channel. We may fallback to backdoor channel when other type
+ * of channel fails to start.
+ *
+ * @param[in]  chan        The RPC channel instance.
+ *
+ * @return TRUE on success.
+ */
+
+gboolean
+RpcChannel_Start(RpcChannel *chan)
+{
+   gboolean ok;
+   const RpcChannelFuncs *funcs;
+
+   if (chan == NULL || chan->funcs == NULL || chan->funcs->start == NULL) {
+      return FALSE;
+   }
+
+   if (chan->outStarted) {
+      /* Already started. Make sure both channels are in sync and return. */
+      ASSERT(chan->in == NULL || chan->inStarted);
+      return TRUE;
+   } else {
+      ASSERT(chan->in == NULL || !chan->inStarted);
+   }
+
+   if (chan->in != NULL) {
+      ok = RpcIn_start(chan->in, RPCIN_MAX_DELAY, RpcChannel_Error, chan);
+      chan->inStarted = ok;
+   }
+
+   funcs = chan->funcs;
+   ok = funcs->start(chan);
+
+   if (!ok && funcs->onStartErr != NULL) {
+      g_warning(LGPFX "Fallback to backdoor ...\n");
+      funcs->onStartErr(chan);
+      ok = BackdoorChannel_Fallback(chan);
+   }
+
+   return ok;
+}
+
+
+/**
+ * Wrapper for the stop function of an RPC channel struct.
+ *
+ * @param[in]  chan        The RPC channel instance.
+ */
+
+void
+RpcChannel_Stop(RpcChannel *chan)
+{
+   g_return_if_fail(chan != NULL);
+   g_return_if_fail(chan->funcs != NULL);
+   g_return_if_fail(chan->funcs->stop != NULL);
+
+   g_static_mutex_lock(&chan->outLock);
+   chan->funcs->stop(chan);
+
+   if (chan->in != NULL) {
+      if (chan->inStarted) {
+         RpcIn_stop(chan->in);
+      }
+      chan->inStarted = FALSE;
+   } else {
+      ASSERT(!chan->inStarted);
+   }
+   g_static_mutex_unlock(&chan->outLock);
+}
+
+
+/**
+ * Wrapper for get channel type function of an RPC channel struct.
+ *
+ * @param[in]  chan        The RPC channel instance.
+ */
+
+RpcChannelType
+RpcChannel_GetType(RpcChannel *chan)
+{
+   if (chan == NULL || chan->funcs == NULL || chan->funcs->getType == NULL) {
+      return RPCCHANNEL_TYPE_INACTIVE;
+   }
+   return chan->funcs->getType(chan);
+}
+
+
+/**
+ * Send function of an RPC channel struct. Retry once if it fails for
+ * non-backdoor Channels. Backdoor channel already tries inside. A second try
+ * may create a different type of channel.
+ *
+ * @param[in]  chan        The RPC channel instance.
+ * @param[in]  data        Data to send.
+ * @param[in]  dataLen     Number of bytes to send.
+ * @param[out] result      Response from other side (should be freed by caller).
+ * @param[out] resultLen   Number of bytes in response.
+ *
+ * @return The status from the remote end (TRUE if call was successful).
+ */
+
+gboolean
+RpcChannel_Send(RpcChannel *chan,
+                char const *data,
+                size_t dataLen,
+                char **result,
+                size_t *resultLen)
+{
+   gboolean ok;
+   char *res = NULL;
+   size_t resLen = 0;
+   const RpcChannelFuncs *funcs;
+
+   g_debug(LGPFX "Sending: %"FMTSZ"u bytes\n", dataLen);
+
+   ASSERT(chan && chan->funcs);
+
+   g_static_mutex_lock(&chan->outLock);
+
+   funcs = chan->funcs;
+   ASSERT(funcs->send);
+
+   if (result != NULL) {
+      *result = NULL;
+   }
+   if (resultLen != NULL) {
+      *resultLen = 0;
+   }
+
+   ok = funcs->send(chan, data, dataLen, &res, &resLen);
+
+   if (!ok && (funcs->getType(chan) != RPCCHANNEL_TYPE_BKDOOR) &&
+       (funcs->stopRpcOut != NULL)) {
+
+       free(res);
+       res = NULL;
+       resLen = 0;
+
+      /* retry once */
+      g_debug(LGPFX "Stop RpcOut channel and try to send again ...\n");
+      funcs->stopRpcOut(chan);
+      if (RpcChannel_Start(chan)) {
+         ok = funcs->send(chan, data, dataLen, &res, &resLen);
+         goto done;
+      }
+
+      ok = FALSE;
+      goto exit;
+   }
+
+done:
+   if (ok) {
+      g_debug(LGPFX "Recved %"FMTSZ"u bytes\n", resLen);
+   }
+
+   if (result != NULL) {
+      *result = res;
+   }
+   if (resultLen != NULL) {
+      *resultLen = resLen;
+   }
+
+exit:
+   g_static_mutex_unlock(&chan->outLock);
+   return ok;
+}
+
+
+/**
+ * Open/close RpcChannel each time for sending a Rpc message, this is a wrapper
+ * for RpcChannel APIs.
+ *
+ * @param[in]  data        request data
+ * @param[in]  dataLen     data length
+ * @param[in]  result      reply
+ * @param[in]  resultLen   reply length
+
+ * @returns    TRUE on success.
+ */
+
+gboolean
+RpcChannel_SendOneRaw(const char *data,
+                      size_t dataLen,
+                      char **result,
+                      size_t *resultLen)
+{
+   RpcChannel *chan;
+   gboolean status;
+
+   status = FALSE;
+
+   chan = RpcChannel_New();
+   if (chan == NULL) {
+      if (result != NULL) {
+         *result = Util_SafeStrdup("RpcChannel: Unable to create "
+                                   "the RpcChannel object");
+         if (resultLen != NULL) {
+            *resultLen = strlen(*result);
+         }
+      }
+      goto sent;
+   } else if (!RpcChannel_Start(chan)) {
+      if (result != NULL) {
+         *result = Util_SafeStrdup("RpcChannel: Unable to open the "
+                                   "communication channel");
+         if (resultLen != NULL) {
+            *resultLen = strlen(*result);
+         }
+      }
+      goto sent;
+   } else if (!RpcChannel_Send(chan, data, dataLen, result, resultLen)) {
+      /* We already have the description of the error */
+      goto sent;
+   }
+
+   status = TRUE;
+
+sent:
+   g_debug(LGPFX "Request %s: reqlen=%"FMTSZ"u, replyLen=%"FMTSZ"u\n",
+           status ? "OK" : "FAILED", dataLen, *resultLen);
+   if (chan) {
+      RpcChannel_Stop(chan);
+      RpcChannel_Destroy(chan);
+   }
+
+   return status;
+}
index 67aef37a4dca56b9c1c440e00efd503e771cd6a9..ccc48cae285d0e2ab349ea177e4a551c0cffd4cf 100644 (file)
 
 #include "vmware/tools/guestrpc.h"
 
+/** Max amount of time (in .01s) that the RpcIn loop will sleep for. */
+#define RPCIN_MAX_DELAY    10
+
+struct RpcIn;
+
+/** a list of interface functions for a channel implementation */
+typedef struct _RpcChannelFuncs{
+   gboolean (*start)(RpcChannel *);
+   void (*stop)(RpcChannel *);
+   gboolean (*send)(RpcChannel *, char const *data, size_t dataLen,
+                    char **result, size_t *resultLen);
+   void (*setup)(RpcChannel *chan, GMainContext *mainCtx,
+                 const char *appName, gpointer appCtx);
+   void (*shutdown)(RpcChannel *);
+   RpcChannelType (*getType)(RpcChannel *chan);
+   void (*onStartErr)(RpcChannel *);
+   gboolean (*stopRpcOut)(RpcChannel *);
+} RpcChannelFuncs;
+
+/** Defines the interface between the application and the RPC channel. */
+struct _RpcChannel {
+   const RpcChannelFuncs     *funcs;
+   gpointer                  _private;
+   GMainContext              *mainCtx;
+   const char                *appName;
+   gpointer                  appCtx;
+   GStaticMutex              outLock;
+   struct RpcIn              *in;
+   gboolean                  inStarted;
+   gboolean                  outStarted;
+};
+
 void
 RpcChannel_Error(void *_state,
                  char const *status);
+RpcChannel *VSockChannel_New(void);
+RpcChannel *BackdoorChannel_New(void);
+gboolean
+BackdoorChannel_Fallback(RpcChannel *chan);
 
 #endif /* _RPCCHANNELINT_H_ */
 
diff --git a/open-vm-tools/lib/rpcChannel/simpleSocket.c b/open-vm-tools/lib/rpcChannel/simpleSocket.c
new file mode 100644 (file)
index 0000000..2a7d5db
--- /dev/null
@@ -0,0 +1,625 @@
+/*********************************************************
+ * Copyright (C) 2013 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
+ * by the Free Software Foundation version 2.1 and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
+ *
+ *********************************************************/
+
+/*
+ * simpleSocket.c --
+ *
+ *    Simple wrappers for socket.
+ *
+ */
+
+#include <stdlib.h>
+#if defined(linux)
+#include <arpa/inet.h>
+#endif
+
+#include "simpleSocket.h"
+#include "vmci_defs.h"
+#include "vmci_sockets.h"
+#include "dataMap.h"
+#include "err.h"
+
+#define LGPFX "SimpleSock: "
+
+
+static int
+SocketGetLastError(void);
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * SocketStartup --
+ *
+ *      Win32 special socket init.
+ *
+ * Results:
+ *      TRUE on success
+ *
+ * Side effects:
+ *      None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static gboolean
+SocketStartup(void)
+{
+#if defined(_WIN32)
+   int err;
+   WSADATA wsaData;
+
+   err = WSAStartup(MAKEWORD(2, 0), &wsaData);
+   if (err) {
+      g_warning(LGPFX "Error in WSAStartup: %d[%s]\n", err,
+                Err_Errno2String(err));
+      return FALSE;
+   }
+
+   if (2 != LOBYTE(wsaData.wVersion) || 0 != HIBYTE(wsaData.wVersion)) {
+      g_warning(LGPFX "Unsupported Winsock version %d.%d\n",
+                LOBYTE(wsaData.wVersion), HIBYTE(wsaData.wVersion));
+      return FALSE;
+   }
+#endif
+
+   return TRUE;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * SocketCleanup --
+ *
+ *      Win32 special socket cleanup.
+ *
+ * Results:
+ *      TRUE on success, FALSE on failure.
+ *
+ * Side effects:
+ *      None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static gboolean
+SocketCleanup(void)
+{
+#if defined(_WIN32)
+   int err = WSACleanup();
+   if (err) {
+      g_warning(LGPFX "Error in WSACleanup: %d[%s]\n", err,
+                Err_Errno2String(err));
+      return FALSE;
+   }
+#endif
+   return TRUE;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * Socket_Close --
+ *
+ *      wrapper for socket close.
+ *
+ * Results:
+ *      None.
+ *
+ * Side effects:
+ *      None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+Socket_Close(SOCKET sock)
+{
+   int res;
+
+#if defined(_WIN32)
+   res = closesocket(sock);
+#else
+   res = close(sock);
+#endif
+
+   if (res == SOCKET_ERROR) {
+      int err = SocketGetLastError();
+      g_warning(LGPFX "Error in closing socket %d: %d[%s]\n",
+                sock, err, Err_Errno2String(err));
+   }
+
+   SocketCleanup();
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * SocketGetLastError --
+ *
+ *      Get the last error code.
+ *
+ * Results:
+ *      error code.
+ *
+ * Side effects:
+ *      None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static int
+SocketGetLastError(void)
+{
+#if defined(_WIN32)
+   return WSAGetLastError();
+#else
+   return errno;
+#endif
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * Socket_Recv --
+ *
+ *      Block until given number of bytes of data is received or error occurs.
+ *
+ * Results:
+ *      TRUE on success, FALSE on failure.
+ *
+ * Side effects:
+ *      None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+gboolean
+Socket_Recv(SOCKET fd,      // IN
+            char *buf,      // OUT
+            int len)        // IN
+{
+   int remaining = len;
+   int rv;
+   int sysErr;
+
+   while (remaining > 0) {
+      rv = recv(fd, buf , remaining, 0);
+      if (rv == 0) {
+         g_warning(LGPFX "Socket %d closed by peer.", fd);
+         return FALSE;
+      }
+      if (rv == SOCKET_ERROR) {
+         sysErr = SocketGetLastError();
+         if (sysErr == SYSERR_EINTR) {
+            continue;
+         }
+         g_warning(LGPFX "Recv error for socket %d: %d[%s]", fd, sysErr,
+                   Err_Errno2String(sysErr));
+         return FALSE;
+      }
+      remaining -= rv;
+      buf += rv;
+   }
+
+   g_debug(LGPFX "Recved %d bytes from socket %d\n", len, fd);
+   return TRUE;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * Socket_Send --
+ *
+ *      Block until the given number of bytes of data is sent or error occurs.
+ *
+ * Results:
+ *      TRUE on success, FALSE on failure.
+ *
+ * Side effects:
+ *      None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+gboolean
+Socket_Send(SOCKET fd,      // IN
+            char *buf,      // IN
+            int len)        // IN
+{
+   int left = len;
+   int sent = 0;
+   int rv;
+   int sysErr;
+
+   while (left > 0) {
+      rv = send(fd, buf + sent, left, 0);
+      if (rv == SOCKET_ERROR) {
+         sysErr = SocketGetLastError();
+         if (sysErr == SYSERR_EINTR) {
+            continue;
+         }
+         g_warning(LGPFX "Send error for socket %d: %d[%s]", fd, sysErr,
+                   Err_Errno2String(sysErr));
+         return FALSE;
+      }
+      left -= rv;
+      sent += rv;
+   }
+
+   g_debug(LGPFX "Sent %d bytes from socket %d\n", len, fd);
+   return TRUE;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Socket_ConnectVMCI --
+ *
+ *      Connect to VMCI port in blocking mode.
+ *      If isPriv is true, we will try to bind the local port to a port that
+ *      is less than 1024.
+ *
+ * Results:
+ *      returns the raw socket on sucess, otherwise INVALID_SOCKET;
+ *
+ * Side effects:
+ *      None
+ *
+ *----------------------------------------------------------------------------
+ */
+
+SOCKET
+Socket_ConnectVMCI(unsigned int cid,                  // IN
+                   unsigned int port,                 // IN
+                   gboolean isPriv,                   // IN
+                   SockConnError *outError)           // OUT
+{
+   struct sockaddr_vm addr;
+   SOCKET fd;
+   SockConnError error = SOCKERR_GENERIC;
+   int sysErr;
+   socklen_t addrLen = sizeof addr;
+
+   if (outError) {
+      *outError = SOCKERR_SUCCESS;
+   }
+
+   if (!SocketStartup()) {
+      goto error;
+   }
+
+   memset((char *)&addr, 0, sizeof addr);
+   addr.svm_family = VMCISock_GetAFValue();
+   addr.svm_cid = cid;
+   addr.svm_port = port;
+
+   g_debug(LGPFX "creating new socket, connecting to %u:%u\n", cid, port);
+
+   fd = socket(addr.svm_family, SOCK_STREAM, 0);
+   if (fd == INVALID_SOCKET) {
+      sysErr = SocketGetLastError();
+      g_warning(LGPFX "failed to create socket, error %d: %s\n",
+                sysErr, Err_Errno2String(sysErr));
+      goto error;
+   }
+
+   if (isPriv) {
+      struct sockaddr_vm localAddr;
+      gboolean bindOk = FALSE;
+      int localPort;
+
+      memset(&localAddr, 0, sizeof localAddr);
+      localAddr.svm_family = addr.svm_family;
+      localAddr.svm_cid = VMCISock_GetLocalCID();
+
+      /* Try to bind to port 1~1023 for a privileged user. */
+      for (localPort = PRIVILEGED_PORT_MAX;
+           localPort >= PRIVILEGED_PORT_MIN; localPort--) {
+
+         localAddr.svm_port = localPort;
+
+         if (bind(fd, (struct sockaddr *)&localAddr, sizeof localAddr) != 0) {
+            sysErr = SocketGetLastError();
+            if (sysErr == SYSERR_EACCESS) {
+               g_warning(LGPFX "Couldn't bind to privileged port for "
+                         "socket %d\n", fd);
+               error = SOCKERR_EACCESS;
+               Socket_Close(fd);
+               goto error;
+            }
+            if (sysErr == SYSERR_EADDRINUSE) {
+               continue;
+            }
+            g_warning(LGPFX "could not bind socket, error %d: %s\n", sysErr,
+                      Err_Errno2String(sysErr));
+            Socket_Close(fd);
+            error = SOCKERR_BIND;
+            goto error;
+         } else {
+            bindOk = TRUE;
+            break;
+         }
+      }
+
+      if (!bindOk) {
+         g_warning(LGPFX "Failed to bind to privileged port for socket %d, "
+                   "no port available\n", fd);
+         error = SOCKERR_BIND;
+         Socket_Close(fd);
+         goto error;
+      } else {
+         g_debug(LGPFX "Successfully bound to port %d for socket %d\n",
+                 localAddr.svm_port, fd);
+      }
+   }
+
+   if (connect(fd, (struct sockaddr *)&addr, addrLen) != 0) {
+      sysErr = SocketGetLastError();
+      g_warning(LGPFX "socket connect failed, error %d: %s\n",
+                sysErr, Err_Errno2String(sysErr));
+      Socket_Close(fd);
+      error = SOCKERR_CONNECT;
+      goto error;
+   }
+
+   g_debug(LGPFX "socket %d connected\n", fd);
+   return fd;
+
+error:
+   if (outError) {
+      *outError = error;
+   }
+
+   return INVALID_SOCKET;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * Socket_DecodePacket --
+ *
+ *    Helper function to decode received packet in DataMap encoding format.
+ *
+ * Result:
+ *    None
+ *
+ * Side-effects:
+ *    None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static gboolean
+Socket_DecodePacket(const char *recvBuf,       // IN
+                    int fullPktLen,            // IN
+                    char **payload,            // OUT
+                    int32 *payloadLen)         // OUT
+{
+   ErrorCode res;
+   DataMap map;
+   char *buf;
+   int32 len;
+
+   *payload = NULL;
+   *payloadLen = 0;
+
+   /* decoding the packet */
+   res = DataMap_Deserialize(recvBuf, fullPktLen, &map);
+   if (res != DMERR_SUCCESS) {
+      g_debug(LGPFX "Error in dataMap decoding, error=%d\n", res);
+      return FALSE;
+   }
+
+   res = DataMap_GetString(&map, GUESTRPCPKT_FIELD_PAYLOAD, &buf, &len);
+   if (res == DMERR_SUCCESS) {
+      char *tmpPtr = malloc(len + 1);
+      if (tmpPtr == NULL) {
+         g_debug(LGPFX "Error in allocating memory\n");
+         goto error;
+      }
+      memcpy(tmpPtr, buf, len);
+      /* add a trailing 0 for backward compatibility */
+      tmpPtr[len] = '\0';
+
+      *payload = tmpPtr;
+      *payloadLen = len;
+   } else {
+      g_debug(LGPFX "Error in decoding payload, error=%d\n", res);
+      goto error;
+   }
+
+   DataMap_Destroy(&map);
+   return TRUE;
+
+error:
+   DataMap_Destroy(&map);
+   return FALSE;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * Socket_PackSendData --
+ *
+ *    Helper function for building send packet and serialize it.
+ *
+ * Result:
+ *    TRUE on sucess, FALSE otherwise.
+ *
+ * Side-effects:
+ *    None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static gboolean
+Socket_PackSendData(const char *buf,             // IN
+                    int len,                     // IN
+                    char **serBuf,               // OUT
+                    int32 *serBufLen)            // OUT
+{
+   DataMap map;
+   ErrorCode res;
+   char *newBuf;
+   gboolean mapCreated = FALSE;
+   int64 pktType = GUESTRPCPKT_TYPE_DATA;
+
+   res = DataMap_Create(&map);
+   if (res != DMERR_SUCCESS) {
+      goto error;
+   }
+
+   mapCreated = TRUE;
+   res = DataMap_SetInt64(&map, GUESTRPCPKT_FIELD_TYPE,
+                          pktType, TRUE);
+   if (res != DMERR_SUCCESS) {
+      goto error;
+   }
+
+   newBuf = malloc(len);
+   if (newBuf == NULL) {
+      g_debug(LGPFX "Error in allocating memory.\n");
+      goto error;
+   }
+   memcpy(newBuf, buf, len);
+   res = DataMap_SetString(&map, GUESTRPCPKT_FIELD_PAYLOAD, newBuf,
+                           len, TRUE);
+   if (res != DMERR_SUCCESS) {
+      free(newBuf);
+      goto error;
+   }
+
+   res = DataMap_Serialize(&map, serBuf, serBufLen);
+   if (res != DMERR_SUCCESS) {
+      goto error;
+   }
+
+   DataMap_Destroy(&map);
+   return TRUE;
+
+error:
+   if (mapCreated) {
+      DataMap_Destroy(&map);
+   }
+   g_debug(LGPFX "Error in dataMap encoding\n");
+   return FALSE;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * Socket_RecvPacket --
+ *
+ *    Helper function to recv a dataMap packet over the socket.
+ *    The caller has to *free* the payload to avoid memory leak.
+ *
+ * Result:
+ *    TRUE on sucess, FALSE otherwise.
+ *
+ * Side-effects:
+ *    None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+gboolean
+Socket_RecvPacket(SOCKET sock,               // IN
+                  char **payload,            // OUT
+                  int *payloadLen)           // OUT
+{
+   gboolean ok;
+   int32 packetLen;
+   int packetLenSize = sizeof packetLen;
+   int fullPktLen;
+   char *recvBuf;
+   int recvBufLen;
+
+   ok = Socket_Recv(sock, (char *)&packetLen, packetLenSize);
+   if (!ok) {
+      g_debug(LGPFX "error in recving packet header, err=%d\n",
+              SocketGetLastError());
+      return FALSE;
+   }
+
+   fullPktLen = ntohl(packetLen) + packetLenSize;
+   recvBufLen = fullPktLen;
+   recvBuf = malloc(recvBufLen);
+   if (recvBuf == NULL) {
+      g_debug(LGPFX "Could not allocate recv buffer.\n");
+      return FALSE;
+   }
+
+   memcpy(recvBuf, &packetLen, packetLenSize);
+   ok = Socket_Recv(sock, recvBuf + packetLenSize,
+                     fullPktLen - packetLenSize);
+   if (!ok) {
+      g_debug(LGPFX "error in recving packet, err=%d\n",
+              SocketGetLastError());
+      free(recvBuf);
+      return FALSE;
+   }
+
+   ok = Socket_DecodePacket(recvBuf, fullPktLen, payload, payloadLen);
+   free(recvBuf);
+   return ok;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * Socket_SendPacket --
+ *
+ *    Helper function to send a dataMap packet over the socket.
+ *
+ * Result:
+ *    TRUE on sucess, FALSE otherwise.
+ *
+ * Side-effects:
+ *    None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+gboolean
+Socket_SendPacket(SOCKET sock,               // IN
+                  const char *payload,       // IN
+                  int payloadLen)            // IN
+{
+   gboolean ok;
+   char *sendBuf;
+   int sendBufLen;
+
+   if (!Socket_PackSendData(payload, payloadLen, &sendBuf, &sendBufLen)) {
+      return FALSE;
+   }
+
+   ok = Socket_Send(sock, sendBuf, sendBufLen);
+   free(sendBuf);
+
+   return ok;
+}
diff --git a/open-vm-tools/lib/rpcChannel/simpleSocket.h b/open-vm-tools/lib/rpcChannel/simpleSocket.h
new file mode 100644 (file)
index 0000000..cff9df5
--- /dev/null
@@ -0,0 +1,89 @@
+/*********************************************************
+ * Copyright (C) 2013 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
+ * by the Free Software Foundation version 2.1 and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
+ *
+ *********************************************************/
+
+#ifndef _SIMPLESOCKET_H_
+#define _SIMPLESOCKET_H_
+
+/**
+ * @file simpleSocket.h
+ *
+ *    header of simple socket wrappers.
+ */
+
+#include <glib.h>
+
+#if defined(_WIN32)
+#include <winsock2.h>
+#include <winerror.h>
+#else
+#include <errno.h>
+#endif
+
+#include "vmci_defs.h"
+#include "vmware/guestrpc/tclodefs.h"
+
+typedef enum {
+   SOCKERR_SUCCESS,
+   SOCKERR_GENERIC,
+   SOCKERR_CONNECT,
+   SOCKERR_BIND,
+   SOCKERR_EACCESS
+} SockConnError;
+
+#if defined(_WIN32)
+
+#define SYSERR_EADDRINUSE        WSAEADDRINUSE
+#define SYSERR_EACCESS           WSAEACCES
+#define SYSERR_EINTR             WSAEINTR
+
+typedef int socklen_t;
+
+#else  /* !_WIN32 */
+
+#define SYSERR_EADDRINUSE        EADDRINUSE
+#define SYSERR_EACCESS           EACCES
+#define SYSERR_EINTR             EINTR
+
+typedef int SOCKET;
+#define SOCKET_ERROR              (-1)
+#define INVALID_SOCKET            ((SOCKET) -1)
+
+#endif
+
+#define PRIVILEGED_PORT_MAX    1023
+#define PRIVILEGED_PORT_MIN    1
+
+void Socket_Close(SOCKET sock);
+SOCKET Socket_ConnectVMCI(unsigned int cid,
+                          unsigned int port,
+                          gboolean isPriv,
+                          SockConnError *outError);
+gboolean Socket_Recv(SOCKET fd,
+                     char *buf,
+                     int len);
+gboolean Socket_Send(SOCKET fd,
+                     char *buf,
+                     int len);
+gboolean Socket_RecvPacket(SOCKET sock,
+                           char **payload,
+                           int *payloadLen);
+gboolean Socket_SendPacket(SOCKET sock,
+                           const char *payload,
+                           int payloadLen);
+
+#endif /* _SIMPLESOCKET_H_ */
diff --git a/open-vm-tools/lib/rpcChannel/vsockChannel.c b/open-vm-tools/lib/rpcChannel/vsockChannel.c
new file mode 100644 (file)
index 0000000..85a9348
--- /dev/null
@@ -0,0 +1,581 @@
+/*********************************************************
+ * Copyright (C) 2013 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
+ * by the Free Software Foundation version 2.1 and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
+ *
+ *********************************************************/
+
+/*
+ * vsockChannel.c --
+ *
+ *    Implement RpcChannel using vsocket.
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "simpleSocket.h"
+#include "rpcChannelInt.h"
+#include "rpcin.h"
+#include "util.h"
+
+#define LGPFX "VSockChan: "
+
+typedef struct VSockOut {
+   SOCKET fd;
+   char *payload;
+   int payloadLen;
+   RpcChannelType type;
+} VSockOut;
+
+typedef struct VSockChannel {
+   VSockOut          *out;
+} VSockChannel;
+
+static void VSockChannelShutdown(RpcChannel *chan);
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VSockCreateConn --
+ *
+ *      Create vsocket connection. we try a privileged connection first,
+ *      fallback to unprivileged one if that fails.
+ *
+ * Result:
+ *      a valid socket/fd on success or INVALID_SOCKET on failure.
+ *
+ * Side-effects:
+ *      None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static SOCKET
+VSockCreateConn(gboolean *isPriv)        // OUT
+{
+   SockConnError err;
+   SOCKET fd;
+
+   g_debug(LGPFX "Creating privileged vsocket ...\n");
+   fd = Socket_ConnectVMCI(VMCI_HYPERVISOR_CONTEXT_ID,
+                           GUESTRPC_RPCI_VSOCK_LISTEN_PORT,
+                           TRUE, &err);
+
+   if (fd != INVALID_SOCKET) {
+      g_debug(LGPFX "Successfully created priv vsocket %d\n", fd);
+      *isPriv = TRUE;
+      return fd;
+   }
+
+   if (err == SOCKERR_EACCESS) {
+      g_debug(LGPFX "Creating unprivileged vsocket ...\n");
+      fd = Socket_ConnectVMCI(VMCI_HYPERVISOR_CONTEXT_ID,
+                              GUESTRPC_RPCI_VSOCK_LISTEN_PORT,
+                              FALSE, &err);
+      if (fd != INVALID_SOCKET) {
+         g_debug(LGPFX "Successfully created unpriv vsocket %d\n", fd);
+         *isPriv = FALSE;
+         return fd;
+      }
+   }
+
+   g_warning(LGPFX "Failed to create vsocket channel, err=%d\n", err);
+   return INVALID_SOCKET;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VSockOutConstruct --
+ *
+ *      Constructor for the VSockOut object
+ *
+ * Results:
+ *      New VSockOut object.
+ *
+ * Side effects:
+ *      Allocates memory.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static VSockOut *
+VSockOutConstruct(void)
+{
+   VSockOut *out = calloc(1, sizeof *out);
+
+   if (out != NULL) {
+      out->fd = INVALID_SOCKET;
+      out->type = RPCCHANNEL_TYPE_INACTIVE;
+   }
+   return out;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VSockOutDestruct --
+ *
+ *      Destructor for the VSockOut object.
+ *
+ * Results:
+ *      None.
+ *
+ * Side effects:
+ *      Frees VSockOut object memory.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+VSockOutDestruct(VSockOut *out)        // IN
+{
+
+   ASSERT(out);
+   ASSERT(out->fd == INVALID_SOCKET);
+
+   free(out->payload);
+   free(out);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VSockOutStart --
+ *
+ *      Open the channel
+ *
+ * Result:
+ *      TRUE on success
+ *      FALSE on failure
+ *
+ * Side-effects:
+ *      None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static gboolean
+VSockOutStart(VSockOut *out)      // IN
+{
+   gboolean isPriv;
+
+   ASSERT(out);
+   ASSERT(out->fd == INVALID_SOCKET);
+
+   out->fd = VSockCreateConn(&isPriv);
+   if (out->fd != INVALID_SOCKET) {
+      out->type = isPriv ? RPCCHANNEL_TYPE_PRIV_VSOCK :
+                           RPCCHANNEL_TYPE_UNPRIV_VSOCK;
+   }
+   return out->fd != INVALID_SOCKET;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VSockOutStop --
+ *
+ *    Close the channel
+ *
+ * Result
+ *    TRUE on success
+ *    FALSE on failure
+ *
+ * Side-effects
+ *    None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static gboolean
+VSockOutStop(VSockOut *out)    // IN
+{
+   ASSERT(out);
+
+   if (out->fd != INVALID_SOCKET) {
+      Socket_Close(out->fd);
+      out->fd = INVALID_SOCKET;
+   }
+
+   return TRUE;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VSockOutSend --
+ *
+ *    Make VMware synchronously execute a TCLO command
+ *
+ *    Unlike the other send varieties, VSockOutSend requires that the
+ *    caller pass non-NULL reply and repLen arguments.
+ *
+ * Result
+ *    TRUE on success. 'reply' contains the result of the rpc
+ *    FALSE on error. 'reply' will contain a description of the error
+ *
+ *    In both cases, the caller should not free the reply.
+ *
+ * Side-effects
+ *    None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static gboolean
+VSockOutSend(VSockOut *out,        // IN
+             const char *request,  // IN
+             size_t reqLen,        // IN
+             const char **reply,   // OUT
+             size_t *repLen)       // OUT
+{
+   ASSERT(out);
+   ASSERT(out->fd != INVALID_SOCKET);
+
+   *reply = NULL;
+   *repLen = 0;
+
+   g_debug(LGPFX "Sending request for conn %d,  reqLen=%d\n",
+           out->fd, (int)reqLen);
+
+   if (!Socket_SendPacket(out->fd, request, reqLen)) {
+      *reply = "VSockOut: Unable to send data for the RPCI command";
+      goto error;
+   }
+
+   free(out->payload);
+   out->payload = NULL;
+
+   if (!Socket_RecvPacket(out->fd, &out->payload, &out->payloadLen)) {
+      *reply = "VSockOut: Unable to receive the result of the RPCI command";
+      goto error;
+   }
+
+   if (out->payloadLen < 2 ||
+       ((out->payload[0] != '1') && (out->payload[0] != '0')) ||
+       out->payload[1] != ' ') {
+      *reply = "VSockOut: Invalid format for the result of the RPCI command";
+      goto error;
+   }
+
+   *reply = out->payload + 2;
+   *repLen = out->payloadLen - 2;
+
+   g_debug("VSockOut: recved %d bytes for conn %d\n", out->payloadLen, out->fd);
+
+   return out->payload[0] == '1';
+
+error:
+   *repLen = strlen(*reply);
+   return FALSE;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VSockChannelOnStartErr --
+ *
+ *      Callback function to cleanup after channel start failure.
+ *
+ * Results:
+ *      None.
+ *
+ * Side effects:
+ *      None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+VSockChannelOnStartErr(RpcChannel *chan)    // IN
+{
+   VSockChannel *vsock = chan->_private;
+
+   /* destroy VSockOut part only */
+   VSockOutDestruct(vsock->out);
+   chan->_private = NULL;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VSockChannelStart --
+ *
+ *      Starts the RpcIn loop and the VSockOut channel.
+ *
+ * Results:
+ *      TRUE on success.
+ *
+ * Side effects:
+ *      None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static gboolean
+VSockChannelStart(RpcChannel *chan)    // IN
+{
+   gboolean ret = TRUE;
+   VSockChannel *vsock = chan->_private;
+
+   ret = chan->in == NULL || chan->inStarted;
+
+   if (ret) {
+      ret = VSockOutStart(vsock->out);
+      if (!ret) {
+         if (chan->inStarted) {
+            RpcIn_stop(chan->in);
+            chan->inStarted = FALSE;
+         }
+      }
+   }
+   chan->outStarted = ret;
+
+   return ret;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VSockChannelStop --
+ *
+ *      Stops a channel, keeping internal state so that it can be restarted
+ *      later. It's safe to call this function more than once.
+ *
+ * Results:
+ *      None.
+ *
+ * Side effects:
+ *      None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+VSockChannelStop(RpcChannel *chan)   // IN
+{
+   VSockChannel *vsock = chan->_private;
+
+   if (vsock->out != NULL) {
+      if (chan->outStarted) {
+         VSockOutStop(vsock->out);
+      }
+      chan->outStarted = FALSE;
+   } else {
+      ASSERT(!chan->outStarted);
+   }
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VSockChannelShutdown --
+ *
+ *      Shuts down the Rpc channel.
+ *
+ * Results:
+ *      None.
+ *
+ * Side effects:
+ *      None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+VSockChannelShutdown(RpcChannel *chan)    // IN
+{
+   VSockChannel *vsock = chan->_private;
+
+   VSockChannelStop(chan);
+   VSockOutDestruct(vsock->out);
+   g_free(vsock);
+   chan->_private = NULL;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VSockChannelSend --
+ *
+ *      Sends the data using the vsocket channel.
+ *
+ * Result:
+ *      TRUE on success
+ *      FALSE on failure
+ *
+ * Side-effects:
+ *      None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static gboolean
+VSockChannelSend(RpcChannel *chan,      // IN
+                 char const *data,      // IN
+                 size_t dataLen,        // IN
+                 char **result,         // OUT
+                 size_t *resultLen)     // OUT
+{
+   gboolean ret = FALSE;
+   VSockChannel *vsock = chan->_private;
+   const char *reply;
+   size_t replyLen;
+
+   if (!chan->outStarted) {
+      goto exit;
+   }
+
+   ret = VSockOutSend(vsock->out, data, dataLen, &reply, &replyLen);
+
+   if (!ret) {
+      goto exit;
+   }
+
+   if (result != NULL) {
+      if (reply != NULL) {
+         *result = Util_SafeMalloc(replyLen + 1);
+         memcpy(*result, reply, replyLen);
+         (*result)[replyLen] = '\0';
+      } else {
+         *result = NULL;
+      }
+   }
+
+   if (resultLen != NULL) {
+      *resultLen = replyLen;
+   }
+
+exit:
+   return ret;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VSockChannel_GetType --
+ *
+ *      Return the channel type that being used.
+ *
+ * Result:
+ *      return the channel type.
+ *
+ * Side-effects:
+ *      None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static RpcChannelType
+VSockChannelGetType(RpcChannel *chan)
+{
+   VSockChannel *vsock = chan->_private;
+
+   if (vsock->out != NULL) {
+      return vsock->out->type;
+   } else {
+      return RPCCHANNEL_TYPE_INACTIVE;
+   }
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VSockChannelStopRpcOut --
+ *
+ *      Stop the RpcOut channel
+ *
+ * Result:
+ *      return TRUE on success.
+ *
+ * Side-effects:
+ *      None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static gboolean
+VSockChannelStopRpcOut(RpcChannel *chan)
+{
+   VSockChannel *vsock = chan->_private;
+   return VSockOutStop(vsock->out);
+}
+
+
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VSockChannel_New --
+ *
+ *      Creates a new RpcChannel channel that uses the vsocket for
+ *      communication.
+ *
+ * Result:
+ *      return A new channel instance (never NULL).
+ *
+ * Side-effects:
+ *      None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+RpcChannel *
+VSockChannel_New(void)
+{
+   RpcChannel *chan;
+   VSockChannel *vsock;
+
+   static RpcChannelFuncs funcs = {
+      VSockChannelStart,
+      VSockChannelStop,
+      VSockChannelSend,
+      NULL,
+      VSockChannelShutdown,
+      VSockChannelGetType,
+      VSockChannelOnStartErr,
+      VSockChannelStopRpcOut
+   };
+
+   chan = RpcChannel_Create();
+   vsock = g_malloc0(sizeof *vsock);
+
+   vsock->out = VSockOutConstruct();
+   ASSERT(vsock->out != NULL);
+
+   chan->inStarted = FALSE;
+   chan->outStarted = FALSE;
+
+   chan->_private = vsock;
+   chan->funcs = &funcs;
+
+   return chan;
+}
index 183528ca161721595ec2a6dbcf97487f376800a0..b9534283c7578be7930606544ec657fe23176fd8 100644 (file)
@@ -625,7 +625,7 @@ RpcInPackSendData(int fd,                      // IN
    char *newBuf;
    gboolean mapCreated = FALSE;
    int64 pktType = (flags & RPCIN_TCLO_PING) ?
-                   RPCINPKT_TYPE_PING : RPCINPKT_TYPE_DATA;
+                   GUESTRPCPKT_TYPE_PING : GUESTRPCPKT_TYPE_DATA;
 
    res = DataMap_Create(&map);
    if (res != DMERR_SUCCESS) {
@@ -633,7 +633,7 @@ RpcInPackSendData(int fd,                      // IN
    }
 
    mapCreated = TRUE;
-   res = DataMap_SetInt64(&map, RPCINPKT_FIELD_TYPE,
+   res = DataMap_SetInt64(&map, GUESTRPCPKT_FIELD_TYPE,
                           pktType, TRUE);
    if (res != DMERR_SUCCESS) {
       goto quit;
@@ -646,9 +646,10 @@ RpcInPackSendData(int fd,                      // IN
          goto quit;
       }
       memcpy(newBuf, buf, len);
-      res = DataMap_SetString(&map, RPCINPKT_FIELD_PAYLOAD, newBuf,
+      res = DataMap_SetString(&map, GUESTRPCPKT_FIELD_PAYLOAD, newBuf,
                               len, TRUE);
       if (res != DMERR_SUCCESS) {
+         free(newBuf);
          goto quit;
       }
    }
@@ -854,7 +855,7 @@ RpcInDecodePacket(ConnInfo *conn,       // IN
       return FALSE;
    }
 
-   res = DataMap_GetString(&map, RPCINPKT_FIELD_PAYLOAD, &buf, &len);
+   res = DataMap_GetString(&map, GUESTRPCPKT_FIELD_PAYLOAD, &buf, &len);
    if (res == DMERR_SUCCESS) {
       char *tmpPtr = (char *)malloc(len + 1);
       if (tmpPtr == NULL) {
@@ -1621,7 +1622,7 @@ RpcInOpenChannel(RpcIn *in,                 // IN
       }
       in->conn->in = in;
       asock = AsyncSocket_ConnectVMCI(VMCI_HYPERVISOR_CONTEXT_ID,
-                                      GUESTRPC_VSOCK_LISTEN_PORT,
+                                      GUESTRPC_TCLO_VSOCK_LISTEN_PORT,
                                       RpcInConnectDone,
                                       in->conn, 0, NULL, &res);
       if (asock == NULL) {
index 8c97508c43eee661bc2f5792784daedda88ec8da..b5ab529031283edc42c8baf26f860abc92ae7a5a 100644 (file)
@@ -160,8 +160,7 @@ BlockService::ShutdownSignalHandler(const siginfo_t *siginfo,
 
    /* shutdown rpc channel to free up VMCI/VSOCKET module usage */
    if (ctx->rpc != NULL) {
-      ASSERT(ctx->rpc->shutdown != NULL);
-      ctx->rpc->shutdown(ctx->rpc);
+      RpcChannel_Shutdown(ctx->rpc);
       ctx->rpc = NULL;
    }
 
index e8a0bdc69945fa6702c9b45a500ac0d390e6873b..b2974922112f35f4f359e3f7c4b8c40d0db65161 100644 (file)
@@ -45,7 +45,7 @@
  */
 
 static void
-ToolsCoreCheckReset(struct RpcChannel *chan,
+ToolsCoreCheckReset(RpcChannel *chan,
                     gboolean success,
                     gpointer _state)
 {
@@ -245,7 +245,7 @@ ToolsCore_InitRpc(ToolsServiceState *state)
                    state->name);
          state->ctx.rpc = NULL;
       } else {
-         state->ctx.rpc = BackdoorChannel_New();
+         state->ctx.rpc = RpcChannel_New();
       }
       app = ToolsCore_GetTcloName(state);
       if (app == NULL) {
index 8d5f2e5b0aef26fddc40ca2750da0bb8cff649f3..ca55ef22fa45d25bf00e6fcafed973a21c0354de 100644 (file)
@@ -21,6 +21,7 @@ libvmrpcdbg_la_CPPFLAGS =
 libvmrpcdbg_la_CPPFLAGS += @CUNIT_CPPFLAGS@
 libvmrpcdbg_la_CPPFLAGS += @GMODULE_CPPFLAGS@
 libvmrpcdbg_la_CPPFLAGS += @VMTOOLS_CPPFLAGS@
+libvmrpcdbg_la_CPPFLAGS += -I$(top_builddir)/lib/rpcChannel
 
 libvmrpcdbg_la_LDFLAGS =
 libvmrpcdbg_la_LDFLAGS += @PLUGIN_LDFLAGS@
index 3f8727f6b89edaa00cef647e025a155ac2f1fe78..7b74dfb9ef271deee37b1a5499651d97fcddbb80 100644 (file)
@@ -32,6 +32,7 @@
 #include "strutil.h"
 #include "util.h"
 #include "vmrpcdbgInt.h"
+#include "rpcChannelInt.h"
 #include "vmxrpc.h"
 #include "xdrutil.h"
 #include "vmware/tools/utils.h"
@@ -313,14 +314,20 @@ RpcDebug_NewDebugChannel(ToolsAppCtx *ctx,
 {
    DbgChannelData *cdata;
    RpcChannel *ret;
+   static RpcChannelFuncs funcs = {
+      RpcDebugStart,
+      RpcDebugStop,
+      RpcDebugSend,
+      RpcDebugSetup,
+      RpcDebugShutdown,
+      NULL,
+      NULL,
+      NULL
+   };
 
    ASSERT(data != NULL);
    ret = RpcChannel_Create();
-   ret->start = RpcDebugStart;
-   ret->stop = RpcDebugStop;
-   ret->send = RpcDebugSend;
-   ret->setup = RpcDebugSetup;
-   ret->shutdown = RpcDebugShutdown;
+   ret->funcs = &funcs;
 
    cdata = g_malloc0(sizeof *cdata);
    cdata->plugin = data->debugPlugin;