[libvirt] [PATCH 02/10] Provide a simple object for encoding/decoding RPC messages

Daniel P. Berrange berrange at redhat.com
Tue May 24 16:31:30 UTC 2011


This provides a new struct that contains a buffer for the RPC
message header+payload, as well as a decoded copy of the message
header. There is an API for applying a XDR encoding & decoding
of the message headers and payloads. There are also APIs for
maintaining a simple FIFO queue of message instances.

Expected usage scenarios are:

To send a message

   msg = virNetMessageNew()

   ...fill in msg->header fields..
   virNetMessageEncodeHeader(msg)
   ...loook at msg->header fields to determine payload filter
   virNetMessageEncodePayload(msg, xdrfilter, data)
   ...send msg->bufferLength worth of data from buffer

To receive a message

   msg = virNetMessageNew()
   ...read VIR_NET_MESSAGE_LEN_MAX of data into buffer
   virNetMessageDecodeLength(msg)
   ...read msg->bufferLength-msg->bufferOffset of data into buffer
   virNetMessageDecodeHeader(msg)
   ...look at msg->header fields to determine payload filter
   virNetMessageDecodePayload(msg, xdrfilter, data)
   ...run payload processor

* src/Makefile.am: Add to libvirt-net-rpc.la
* src/rpc/virnetmessage.c, src/rpc/virnetmessage.h: Internal
  message handling API.
* testutils.c, testutils.h: Helper for printing binary differences
* virnetmessagetest.c: Validate all XDR encoding/decoding
---
 cfg.mk                    |    1 +
 po/POTFILES.in            |    1 +
 src/Makefile.am           |    1 +
 src/rpc/virnetmessage.c   |  365 +++++++++++++++++++++++++++++++++
 src/rpc/virnetmessage.h   |   82 ++++++++
 tests/.gitignore          |    1 +
 tests/Makefile.am         |    9 +-
 tests/testutils.c         |   62 ++++++
 tests/testutils.h         |    4 +
 tests/virnetmessagetest.c |  496 +++++++++++++++++++++++++++++++++++++++++++++
 10 files changed, 1021 insertions(+), 1 deletions(-)
 create mode 100644 src/rpc/virnetmessage.c
 create mode 100644 src/rpc/virnetmessage.h
 create mode 100644 tests/virnetmessagetest.c

diff --git a/cfg.mk b/cfg.mk
index 3a10186..cf30929 100644
--- a/cfg.mk
+++ b/cfg.mk
@@ -125,6 +125,7 @@ useless_free_options =				\
   --name=virInterfaceProtocolDefFree		\
   --name=virJSONValueFree			\
   --name=virLastErrFreeData			\
+  --name=virNetMessageFree                      \
   --name=virNWFilterDefFree			\
   --name=virNWFilterEntryFree			\
   --name=virNWFilterHashTableFree		\
diff --git a/po/POTFILES.in b/po/POTFILES.in
index dd44da2..a6048ec 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -67,6 +67,7 @@ src/qemu/qemu_monitor_text.c
 src/qemu/qemu_process.c
 src/remote/remote_client_bodies.h
 src/remote/remote_driver.c
+src/rpc/virnetmessage.c
 src/secret/secret_driver.c
 src/security/security_apparmor.c
 src/security/security_dac.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 45905fa..b24f319 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1157,6 +1157,7 @@ EXTRA_DIST += $(LIBVIRT_QEMU_SYMBOL_FILE)
 noinst_LTLIBRARIES += libvirt-net-rpc.la
 
 libvirt_net_rpc_la_SOURCES = \
+	rpc/virnetmessage.h rpc/virnetmessage.c \
 	rpc/virnetprotocol.h rpc/virnetprotocol.c
 libvirt_net_rpc_la_CFLAGS = \
 			$(AM_CFLAGS)
diff --git a/src/rpc/virnetmessage.c b/src/rpc/virnetmessage.c
new file mode 100644
index 0000000..1cd3ab3
--- /dev/null
+++ b/src/rpc/virnetmessage.c
@@ -0,0 +1,365 @@
+/*
+ * virnetmessage.c: basic RPC message encoding/decoding
+ *
+ * Copyright (C) 2010-2011 Red Hat, Inc.
+ *
+ * This library 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; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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 GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ */
+
+#include <config.h>
+
+#include <stdlib.h>
+
+#include "virnetmessage.h"
+#include "memory.h"
+#include "virterror_internal.h"
+#include "logging.h"
+
+#define VIR_FROM_THIS VIR_FROM_RPC
+#define virNetError(code, ...)                                    \
+    virReportErrorHelper(VIR_FROM_THIS, code, __FILE__,           \
+                         __FUNCTION__, __LINE__, __VA_ARGS__)
+
+virNetMessagePtr virNetMessageNew(void)
+{
+    virNetMessagePtr msg;
+
+    if (VIR_ALLOC(msg) < 0) {
+        virReportOOMError();
+        return NULL;
+    }
+
+    VIR_DEBUG("msg=%p", msg);
+
+    return msg;
+}
+
+void virNetMessageFree(virNetMessagePtr msg)
+{
+    if (!msg)
+        return;
+
+    VIR_DEBUG("msg=%p", msg);
+
+    VIR_FREE(msg);
+}
+
+void virNetMessageQueuePush(virNetMessagePtr *queue, virNetMessagePtr msg)
+{
+    virNetMessagePtr tmp = *queue;
+
+    if (tmp) {
+        while (tmp->next)
+            tmp = tmp->next;
+        tmp->next = msg;
+    } else {
+        *queue = msg;
+    }
+}
+
+
+virNetMessagePtr virNetMessageQueueServe(virNetMessagePtr *queue)
+{
+    virNetMessagePtr tmp = *queue;
+
+    if (tmp) {
+        *queue = tmp->next;
+        tmp->next = NULL;
+    }
+
+    return tmp;
+}
+
+
+int virNetMessageDecodeLength(virNetMessagePtr msg)
+{
+    XDR xdr;
+    unsigned int len;
+    int ret = -1;
+
+    xdrmem_create(&xdr, msg->buffer,
+                  msg->bufferLength, XDR_DECODE);
+    if (!xdr_u_int(&xdr, &len)) {
+        virNetError(VIR_ERR_RPC, "%s", _("Unable to decode message length"));
+        goto cleanup;
+    }
+    msg->bufferOffset = xdr_getpos(&xdr);
+
+    if (len < VIR_NET_MESSAGE_LEN_MAX) {
+        virNetError(VIR_ERR_RPC, "%s",
+                    _("packet received from server too small"));
+        goto cleanup;
+    }
+
+    /* Length includes length word - adjust to real length to read. */
+    len -= VIR_NET_MESSAGE_LEN_MAX;
+
+    if (len > VIR_NET_MESSAGE_MAX) {
+        virNetError(VIR_ERR_RPC, "%s",
+                    _("packet received from server too large"));
+        goto cleanup;
+    }
+
+    /* Extend our declared buffer length and carry
+       on reading the header + payload */
+    msg->bufferLength += len;
+
+    VIR_DEBUG("Got length, now need %zu total (%u more)",
+              msg->bufferLength, len);
+
+    ret = 0;
+
+cleanup:
+    xdr_destroy(&xdr);
+    return ret;
+}
+
+
+/*
+ * @msg: the complete incoming message, whose header to decode
+ *
+ * Decodes the header part of the message, but does not
+ * validate the decoded fields in the header. It expects
+ * bufferLength to refer to length of the data packet. Upon
+ * return bufferOffset will refer to the amount of the packet
+ * consumed by decoding of the header.
+ *
+ * returns 0 if successfully decoded, -1 upon fatal error
+ */
+int virNetMessageDecodeHeader(virNetMessagePtr msg)
+{
+    XDR xdr;
+    int ret = -1;
+
+    msg->bufferOffset = VIR_NET_MESSAGE_LEN_MAX;
+
+    /* Parse the header. */
+    xdrmem_create(&xdr,
+                  msg->buffer + msg->bufferOffset,
+                  msg->bufferLength - msg->bufferOffset,
+                  XDR_DECODE);
+
+    if (!xdr_virNetMessageHeader(&xdr, &msg->header)) {
+        virNetError(VIR_ERR_RPC, "%s", _("Unable to decode message header"));
+        goto cleanup;
+    }
+
+    msg->bufferOffset += xdr_getpos(&xdr);
+
+    ret = 0;
+
+cleanup:
+    xdr_destroy(&xdr);
+    return ret;
+}
+
+
+/*
+ * @msg: the outgoing message, whose header to encode
+ *
+ * Encodes the length word and header of the message, setting the
+ * message offset ready to encode the payload. Leaves space
+ * for the length field later. Upon return bufferLength will
+ * refer to the total available space for message, while
+ * bufferOffset will refer to current space used by header
+ *
+ * returns 0 if successfully encoded, -1 upon fatal error
+ */
+int virNetMessageEncodeHeader(virNetMessagePtr msg)
+{
+    XDR xdr;
+    int ret = -1;
+    unsigned int len = 0;
+
+    msg->bufferLength = sizeof(msg->buffer);
+    msg->bufferOffset = 0;
+
+    /* Format the header. */
+    xdrmem_create(&xdr,
+                  msg->buffer,
+                  msg->bufferLength,
+                  XDR_ENCODE);
+
+    /* The real value is filled in shortly */
+    if (!xdr_u_int(&xdr, &len)) {
+        virNetError(VIR_ERR_RPC, "%s", _("Unable to encode message length"));
+        goto cleanup;
+    }
+
+    if (!xdr_virNetMessageHeader(&xdr, &msg->header)) {
+        virNetError(VIR_ERR_RPC, "%s", _("Unable to encode message header"));
+        goto cleanup;
+    }
+
+    len = xdr_getpos(&xdr);
+    xdr_setpos(&xdr, 0);
+
+    /* Fill in current length - may be re-written later
+     * if a payload is added
+     */
+    if (!xdr_u_int(&xdr, &len)) {
+        virNetError(VIR_ERR_RPC, "%s", _("Unable to re-encode message length"));
+        goto cleanup;
+    }
+
+    msg->bufferOffset += len;
+
+    ret = 0;
+
+cleanup:
+    xdr_destroy(&xdr);
+    return ret;
+}
+
+
+int virNetMessageEncodePayload(virNetMessagePtr msg,
+                               xdrproc_t filter,
+                               void *data)
+{
+    XDR xdr;
+    unsigned int msglen;
+
+    /* Serialise payload of the message. This assumes that
+     * virNetMessageEncodeHeader has already been run, so
+     * just appends to that data */
+    xdrmem_create(&xdr, msg->buffer + msg->bufferOffset,
+                  msg->bufferLength - msg->bufferOffset, XDR_ENCODE);
+
+    if (!(*filter)(&xdr, data)) {
+        virNetError(VIR_ERR_RPC, "%s", _("Unable to encode message payload"));
+        goto error;
+    }
+
+    /* Get the length stored in buffer. */
+    msg->bufferOffset += xdr_getpos(&xdr);
+    xdr_destroy(&xdr);
+
+    /* Re-encode the length word. */
+    VIR_DEBUG("Encode length as %zu", msg->bufferOffset);
+    xdrmem_create(&xdr, msg->buffer, VIR_NET_MESSAGE_HEADER_XDR_LEN, XDR_ENCODE);
+    msglen = msg->bufferOffset;
+    if (!xdr_u_int(&xdr, &msglen)) {
+        virNetError(VIR_ERR_RPC, "%s", _("Unable to encode message length"));
+        goto error;
+    }
+    xdr_destroy(&xdr);
+
+    msg->bufferLength = msg->bufferOffset;
+    msg->bufferOffset = 0;
+    return 0;
+
+error:
+    xdr_destroy(&xdr);
+    return -1;
+}
+
+
+int virNetMessageDecodePayload(virNetMessagePtr msg,
+                               xdrproc_t filter,
+                               void *data)
+{
+    XDR xdr;
+
+    /* Deserialise payload of the message. This assumes that
+     * virNetMessageDecodeHeader has already been run, so
+     * just start from after that data */
+    xdrmem_create(&xdr, msg->buffer + msg->bufferOffset,
+                  msg->bufferLength - msg->bufferOffset, XDR_DECODE);
+
+    if (!(*filter)(&xdr, data)) {
+        virNetError(VIR_ERR_RPC, "%s", _("Unable to decode message payload"));
+        goto error;
+    }
+
+    /* Get the length stored in buffer. */
+    msg->bufferLength += xdr_getpos(&xdr);
+    xdr_destroy(&xdr);
+    return 0;
+
+error:
+    xdr_destroy(&xdr);
+    return -1;
+}
+
+
+int virNetMessageEncodePayloadRaw(virNetMessagePtr msg,
+                                  const char *data,
+                                  size_t len)
+{
+    XDR xdr;
+    unsigned int msglen;
+
+    if ((msg->bufferLength - msg->bufferOffset) < len) {
+        virNetError(VIR_ERR_RPC,
+                    _("Stream data too long to send (%zu bytes needed, %zu bytes available)"),
+                    len, (msg->bufferLength - msg->bufferOffset));
+        return -1;
+    }
+
+    memcpy(msg->buffer + msg->bufferOffset, data, len);
+    msg->bufferOffset += len;
+
+    /* Re-encode the length word. */
+    VIR_DEBUG("Encode length as %zu", msg->bufferOffset);
+    xdrmem_create(&xdr, msg->buffer, VIR_NET_MESSAGE_HEADER_XDR_LEN, XDR_ENCODE);
+    msglen = msg->bufferOffset;
+    if (!xdr_u_int(&xdr, &msglen)) {
+        virNetError(VIR_ERR_RPC, "%s", _("Unable to encode message length"));
+        goto error;
+    }
+    xdr_destroy(&xdr);
+
+    msg->bufferLength = msg->bufferOffset;
+    msg->bufferOffset = 0;
+    return 0;
+
+error:
+    xdr_destroy(&xdr);
+    return -1;
+}
+
+
+void virNetMessageSaveError(virNetMessageErrorPtr rerr)
+{
+    /* This func may be called several times & the first
+     * error is the one we want because we don't want
+     * cleanup code overwriting the first one.
+     */
+    if (rerr->code != VIR_ERR_OK)
+        return;
+
+    virErrorPtr verr = virGetLastError();
+    if (verr) {
+        rerr->code = verr->code;
+        rerr->domain = verr->domain;
+        rerr->message = verr->message ? malloc(sizeof(char*)) : NULL;
+        if (rerr->message) *rerr->message = strdup(verr->message);
+        rerr->level = verr->level;
+        rerr->str1 = verr->str1 ? malloc(sizeof(char*)) : NULL;
+        if (rerr->str1) *rerr->str1 = strdup(verr->str1);
+        rerr->str2 = verr->str2 ? malloc(sizeof(char*)) : NULL;
+        if (rerr->str2) *rerr->str2 = strdup(verr->str2);
+        rerr->str3 = verr->str3 ? malloc(sizeof(char*)) : NULL;
+        if (rerr->str3) *rerr->str3 = strdup(verr->str3);
+        rerr->int1 = verr->int1;
+        rerr->int2 = verr->int2;
+    } else {
+        rerr->code = VIR_ERR_INTERNAL_ERROR;
+        rerr->domain = VIR_FROM_RPC;
+        rerr->message = malloc(sizeof(char*));
+        if (rerr->message) *rerr->message = strdup(_("Library function returned error but did not set virError"));
+        rerr->level = VIR_ERR_ERROR;
+    }
+}
diff --git a/src/rpc/virnetmessage.h b/src/rpc/virnetmessage.h
new file mode 100644
index 0000000..fbeb257
--- /dev/null
+++ b/src/rpc/virnetmessage.h
@@ -0,0 +1,82 @@
+/*
+ * virnetmessage.h: basic RPC message encoding/decoding
+ *
+ * Copyright (C) 2010-2011 Red Hat, Inc.
+ *
+ * This library 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; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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 GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ */
+
+#ifndef __VIR_NET_MESSAGE_H__
+# define __VIR_NET_MESSAGE_H__
+
+# include <stdbool.h>
+
+# include "virnetprotocol.h"
+
+typedef struct virNetMessageHeader *virNetMessageHeaderPtr;
+typedef struct virNetMessageError *virNetMessageErrorPtr;
+
+typedef struct _virNetMessage virNetMessage;
+typedef virNetMessage *virNetMessagePtr;
+
+/* Never allocate this (huge) buffer on the stack. Always
+ * use virNetMessageNew() to allocate on the heap
+ */
+struct _virNetMessage {
+    char buffer[VIR_NET_MESSAGE_MAX + VIR_NET_MESSAGE_LEN_MAX];
+    size_t bufferLength;
+    size_t bufferOffset;
+
+    virNetMessageHeader header;
+
+    virNetMessagePtr next;
+};
+
+
+virNetMessagePtr virNetMessageNew(void);
+
+void virNetMessageFree(virNetMessagePtr msg);
+
+virNetMessagePtr virNetMessageQueueServe(virNetMessagePtr *queue)
+    ATTRIBUTE_NONNULL(1);
+void virNetMessageQueuePush(virNetMessagePtr *queue,
+                            virNetMessagePtr msg)
+    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
+
+int virNetMessageEncodeHeader(virNetMessagePtr msg)
+    ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK;
+int virNetMessageDecodeLength(virNetMessagePtr msg)
+    ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK;
+int virNetMessageDecodeHeader(virNetMessagePtr msg)
+    ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK;
+
+int virNetMessageEncodePayload(virNetMessagePtr msg,
+                               xdrproc_t filter,
+                               void *data)
+    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK;
+int virNetMessageDecodePayload(virNetMessagePtr msg,
+                               xdrproc_t filter,
+                               void *data)
+    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK;
+
+int virNetMessageEncodePayloadRaw(virNetMessagePtr msg,
+                                  const char *buf,
+                                  size_t len)
+    ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK;
+
+void virNetMessageSaveError(virNetMessageErrorPtr rerr)
+    ATTRIBUTE_NONNULL(1);
+
+#endif /* __VIR_NET_MESSAGE_H__ */
diff --git a/tests/.gitignore b/tests/.gitignore
index e3906f0..36115ea 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -30,6 +30,7 @@ statstest
 storagepoolxml2xmltest
 storagevolxml2xmltest
 virbuftest
+virnetmessagetest
 virshtest
 vmx2xmltest
 xencapstest
diff --git a/tests/Makefile.am b/tests/Makefile.am
index bc171d2..f80b98f 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -78,7 +78,7 @@ EXTRA_DIST =		\
 check_PROGRAMS = virshtest conftest sockettest \
 	nodeinfotest qparamtest virbuftest \
 	commandtest commandhelper seclabeltest \
-	hashtest
+	hashtest virnetmessagetest
 
 if WITH_XEN
 check_PROGRAMS += xml2sexprtest sexpr2xmltest \
@@ -180,6 +180,7 @@ TESTS = virshtest \
 	commandtest \
 	seclabeltest \
 	hashtest \
+	virnetmessagetest \
 	$(test_scripts)
 
 if WITH_XEN
@@ -382,6 +383,12 @@ commandhelper_SOURCES = \
 commandhelper_CFLAGS = -Dabs_builddir="\"`pwd`\""
 commandhelper_LDADD = $(LDADDS)
 
+virnetmessagetest_SOURCES = \
+	virnetmessagetest.c testutils.h testutils.c
+virnetmessagetest_CFLAGS = -Dabs_builddir="\"$(abs_builddir)\""
+virnetmessagetest_LDADD = $(LDADDS)
+
+
 seclabeltest_SOURCES = \
 	seclabeltest.c
 seclabeltest_LDADD = ../src/libvirt_driver_security.la $(LDADDS)
diff --git a/tests/testutils.c b/tests/testutils.c
index bc89690..d87347d 100644
--- a/tests/testutils.c
+++ b/tests/testutils.c
@@ -370,6 +370,68 @@ int virtTestDifference(FILE *stream,
     return 0;
 }
 
+/**
+ * @param stream: output stream write to differences to
+ * @param expect: expected output text
+ * @param actual: actual output text
+ *
+ * Display expected and actual output text, trimmed to
+ * first and last characters at which differences occur
+ */
+int virtTestDifferenceBin(FILE *stream,
+                          const char *expect,
+                          const char *actual,
+                          size_t length)
+{
+    size_t start = 0, end = length;
+    ssize_t i;
+
+    if (!virTestGetDebug())
+        return 0;
+
+    if (virTestGetDebug() < 2) {
+        /* Skip to first character where they differ */
+        for (i = 0 ; i < length ; i++) {
+            if (expect[i] != actual[i]) {
+                start = i;
+                break;
+            }
+        }
+
+        /* Work backwards to last character where they differ */
+        for (i = (length -1) ; i >= 0 ; i--) {
+            if (expect[i] != actual[i]) {
+                end = i;
+                break;
+            }
+        }
+    }
+    /* Round to nearest boundary of 4 */
+    start -= (start % 4);
+    end += 4 - (end % 4);
+
+    /* Show the trimmed differences */
+    fprintf(stream, "\nExpect [ Region %d-%d", (int)start, (int)end);
+    for (i = start; i < end ; i++) {
+        if ((i % 4) == 0)
+            fprintf(stream, "\n    ");
+        fprintf(stream, "0x%02x, ", ((int)expect[i])&0xff);
+    }
+    fprintf(stream, "]\n");
+    fprintf(stream, "Actual [ Region %d-%d", (int)start, (int)end);
+    for (i = start; i < end ; i++) {
+        if ((i % 4) == 0)
+            fprintf(stream, "\n    ");
+        fprintf(stream, "0x%02x, ", ((int)actual[i])&0xff);
+    }
+    fprintf(stream, "]\n");
+
+    /* Pad to line up with test name ... in virTestRun */
+    fprintf(stream, "                                                                      ... ");
+
+    return 0;
+}
+
 #if TEST_OOM
 static void
 virtTestErrorFuncQuiet(void *data ATTRIBUTE_UNUSED,
diff --git a/tests/testutils.h b/tests/testutils.h
index e8f4153..03d8dc6 100644
--- a/tests/testutils.h
+++ b/tests/testutils.h
@@ -36,6 +36,10 @@ int virtTestClearLineRegex(const char *pattern,
 int virtTestDifference(FILE *stream,
                        const char *expect,
                        const char *actual);
+int virtTestDifferenceBin(FILE *stream,
+                          const char *expect,
+                          const char *actual,
+                          size_t length);
 
 unsigned int virTestGetDebug(void);
 unsigned int virTestGetVerbose(void);
diff --git a/tests/virnetmessagetest.c b/tests/virnetmessagetest.c
new file mode 100644
index 0000000..384ab43
--- /dev/null
+++ b/tests/virnetmessagetest.c
@@ -0,0 +1,496 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This library 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; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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 GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ *
+ * Author: Daniel P. Berrange <berrange at redhat.com>
+ */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <signal.h>
+
+#include "testutils.h"
+#include "util.h"
+#include "virterror_internal.h"
+#include "memory.h"
+#include "logging.h"
+#include "ignore-value.h"
+
+#include "rpc/virnetmessage.h"
+
+#define VIR_FROM_THIS VIR_FROM_RPC
+
+static int testMessageHeaderEncode(const void *args ATTRIBUTE_UNUSED)
+{
+    static virNetMessage msg;
+    static const char expect[] = {
+        0x00, 0x00, 0x00, 0x1c,  /* Length */
+        0x11, 0x22, 0x33, 0x44,  /* Program */
+        0x00, 0x00, 0x00, 0x01,  /* Version */
+        0x00, 0x00, 0x06, 0x66,  /* Procedure */
+        0x00, 0x00, 0x00, 0x00,  /* Type */
+        0x00, 0x00, 0x00, 0x99,  /* Serial */
+        0x00, 0x00, 0x00, 0x00,  /* Status */
+    };
+    memset(&msg, 0, sizeof(msg));
+
+    msg.header.prog = 0x11223344;
+    msg.header.vers = 0x01;
+    msg.header.proc = 0x666;
+    msg.header.type = VIR_NET_CALL;
+    msg.header.serial = 0x99;
+    msg.header.status = VIR_NET_OK;
+
+    if (virNetMessageEncodeHeader(&msg) < 0)
+        return -1;
+
+    if (ARRAY_CARDINALITY(expect) != msg.bufferOffset) {
+        VIR_DEBUG("Expect message offset %zu got %zu",
+                  sizeof(expect), msg.bufferOffset);
+        return -1;
+    }
+
+    if (msg.bufferLength != sizeof(msg.buffer)) {
+        VIR_DEBUG("Expect message offset %zu got %zu",
+                  sizeof(msg.buffer), msg.bufferLength);
+        return -1;
+    }
+
+    if (memcmp(expect, msg.buffer, sizeof(expect)) != 0) {
+        virtTestDifferenceBin(stderr, expect, msg.buffer, sizeof(expect));
+        return -1;
+    }
+
+    return 0;
+}
+
+static int testMessageHeaderDecode(const void *args ATTRIBUTE_UNUSED)
+{
+    static virNetMessage msg = {
+        .bufferOffset = 0,
+        .bufferLength = 0x4,
+        .buffer = {
+            0x00, 0x00, 0x00, 0x1c,  /* Length */
+            0x11, 0x22, 0x33, 0x44,  /* Program */
+            0x00, 0x00, 0x00, 0x01,  /* Version */
+            0x00, 0x00, 0x06, 0x66,  /* Procedure */
+            0x00, 0x00, 0x00, 0x01,  /* Type */
+            0x00, 0x00, 0x00, 0x99,  /* Serial */
+            0x00, 0x00, 0x00, 0x01,  /* Status */
+        },
+        .header = { 0, 0, 0, 0, 0, 0 },
+    };
+
+    msg.header.prog = 0x11223344;
+    msg.header.vers = 0x01;
+    msg.header.proc = 0x666;
+    msg.header.type = VIR_NET_CALL;
+    msg.header.serial = 0x99;
+    msg.header.status = VIR_NET_OK;
+
+    if (virNetMessageDecodeLength(&msg) < 0) {
+        VIR_DEBUG("Failed to decode message header");
+        return -1;
+    }
+
+    if (msg.bufferOffset != 0x4) {
+        VIR_DEBUG("Expecting offset %zu got %zu",
+                  (size_t)4, msg.bufferOffset);
+        return -1;
+    }
+
+    if (msg.bufferLength != 0x1c) {
+        VIR_DEBUG("Expecting length %zu got %zu",
+                  (size_t)0x1c, msg.bufferLength);
+        return -1;
+    }
+
+    if (virNetMessageDecodeHeader(&msg) < 0) {
+        VIR_DEBUG("Failed to decode message header");
+        return -1;
+    }
+
+    if (msg.bufferOffset != msg.bufferLength) {
+        VIR_DEBUG("Expect message offset %zu got %zu",
+                  msg.bufferOffset, msg.bufferLength);
+        return -1;
+    }
+
+    if (msg.header.prog != 0x11223344) {
+        VIR_DEBUG("Expect prog %d got %d",
+                  0x11223344, msg.header.prog);
+        return -1;
+    }
+    if (msg.header.vers != 0x1) {
+        VIR_DEBUG("Expect vers %d got %d",
+                  0x11223344, msg.header.vers);
+        return -1;
+    }
+    if (msg.header.proc != 0x666) {
+        VIR_DEBUG("Expect proc %d got %d",
+                  0x666, msg.header.proc);
+        return -1;
+    }
+    if (msg.header.type != VIR_NET_REPLY) {
+        VIR_DEBUG("Expect type %d got %d",
+                  VIR_NET_REPLY, msg.header.type);
+        return -1;
+    }
+    if (msg.header.serial != 0x99) {
+        VIR_DEBUG("Expect serial %d got %d",
+                  0x99, msg.header.serial);
+        return -1;
+    }
+    if (msg.header.status != VIR_NET_ERROR) {
+        VIR_DEBUG("Expect status %d got %d",
+                  VIR_NET_ERROR, msg.header.status);
+        return -1;
+    }
+
+    return 0;
+}
+
+static int testMessagePayloadEncode(const void *args ATTRIBUTE_UNUSED)
+{
+    virNetMessageError err;
+    static virNetMessage msg;
+    static const char expect[] = {
+        0x00, 0x00, 0x00, 0x74,  /* Length */
+        0x11, 0x22, 0x33, 0x44,  /* Program */
+        0x00, 0x00, 0x00, 0x01,  /* Version */
+        0x00, 0x00, 0x06, 0x66,  /* Procedure */
+        0x00, 0x00, 0x00, 0x02,  /* Type */
+        0x00, 0x00, 0x00, 0x99,  /* Serial */
+        0x00, 0x00, 0x00, 0x01,  /* Status */
+
+        0x00, 0x00, 0x00, 0x01,  /* Error code */
+        0x00, 0x00, 0x00, 0x07,  /* Error domain */
+        0x00, 0x00, 0x00, 0x01,  /* Error message pointer */
+        0x00, 0x00, 0x00, 0x0b,  /* Error message length */
+        'H', 'e', 'l', 'l',  /* Error message string */
+        'o', ' ', 'W', 'o',
+        'r', 'l', 'd', '\0',
+        0x00, 0x00, 0x00, 0x02,  /* Error level */
+        0x00, 0x00, 0x00, 0x00,  /* Error domain pointer */
+        0x00, 0x00, 0x00, 0x01,  /* Error str1 pointer */
+        0x00, 0x00, 0x00, 0x03,  /* Error str1 length */
+        'O', 'n', 'e', '\0',  /* Error str1 message */
+        0x00, 0x00, 0x00, 0x01,  /* Error str2 pointer */
+        0x00, 0x00, 0x00, 0x03,  /* Error str2 length */
+        'T', 'w', 'o', '\0',  /* Error str2 message */
+        0x00, 0x00, 0x00, 0x01,  /* Error str3 pointer */
+        0x00, 0x00, 0x00, 0x05,  /* Error str3 length */
+        'T', 'h', 'r', 'e',  /* Error str3 message */
+        'e', '\0', '\0', '\0',
+        0x00, 0x00, 0x00, 0x01,  /* Error int1 */
+        0x00, 0x00, 0x00, 0x02,  /* Error int2 */
+        0x00, 0x00, 0x00, 0x00,  /* Error network pointer */
+    };
+    memset(&msg, 0, sizeof(msg));
+    memset(&err, 0, sizeof(err));
+
+    err.code = VIR_ERR_INTERNAL_ERROR;
+    err.domain = VIR_FROM_RPC;
+    if (VIR_ALLOC(err.message) < 0)
+        return -1;
+    *err.message = strdup("Hello World");
+    err.level = VIR_ERR_ERROR;
+    if (VIR_ALLOC(err.str1) < 0)
+        return -1;
+    *err.str1 = strdup("One");
+    if (VIR_ALLOC(err.str2) < 0)
+        return -1;
+    *err.str2 = strdup("Two");
+    if (VIR_ALLOC(err.str3) < 0)
+        return -1;
+    *err.str3 = strdup("Three");
+    err.int1 = 1;
+    err.int2 = 2;
+
+    msg.header.prog = 0x11223344;
+    msg.header.vers = 0x01;
+    msg.header.proc = 0x666;
+    msg.header.type = VIR_NET_MESSAGE;
+    msg.header.serial = 0x99;
+    msg.header.status = VIR_NET_ERROR;
+
+    if (virNetMessageEncodeHeader(&msg) < 0)
+        return -1;
+
+    if (virNetMessageEncodePayload(&msg, (xdrproc_t)xdr_virNetMessageError, &err) < 0)
+        return -1;
+
+    if (ARRAY_CARDINALITY(expect) != msg.bufferLength) {
+        VIR_DEBUG("Expect message length %zu got %zu",
+                  sizeof(expect), msg.bufferLength);
+        return -1;
+    }
+
+    if (msg.bufferOffset != 0) {
+        VIR_DEBUG("Expect message offset 0 got %zu",
+                  msg.bufferOffset);
+        return -1;
+    }
+
+    if (memcmp(expect, msg.buffer, sizeof(expect)) != 0) {
+        virtTestDifferenceBin(stderr, expect, msg.buffer, sizeof(expect));
+        return -1;
+    }
+
+    return 0;
+}
+
+static int testMessagePayloadDecode(const void *args ATTRIBUTE_UNUSED)
+{
+    virNetMessageError err;
+    static virNetMessage msg = {
+        .bufferOffset = 0,
+        .bufferLength = 0x4,
+        .buffer = {
+            0x00, 0x00, 0x00, 0x74,  /* Length */
+            0x11, 0x22, 0x33, 0x44,  /* Program */
+            0x00, 0x00, 0x00, 0x01,  /* Version */
+            0x00, 0x00, 0x06, 0x66,  /* Procedure */
+            0x00, 0x00, 0x00, 0x02,  /* Type */
+            0x00, 0x00, 0x00, 0x99,  /* Serial */
+            0x00, 0x00, 0x00, 0x01,  /* Status */
+
+            0x00, 0x00, 0x00, 0x01,  /* Error code */
+            0x00, 0x00, 0x00, 0x07,  /* Error domain */
+            0x00, 0x00, 0x00, 0x01,  /* Error message pointer */
+            0x00, 0x00, 0x00, 0x0b,  /* Error message length */
+            'H', 'e', 'l', 'l',  /* Error message string */
+            'o', ' ', 'W', 'o',
+            'r', 'l', 'd', '\0',
+            0x00, 0x00, 0x00, 0x02,  /* Error level */
+            0x00, 0x00, 0x00, 0x00,  /* Error domain pointer */
+            0x00, 0x00, 0x00, 0x01,  /* Error str1 pointer */
+            0x00, 0x00, 0x00, 0x03,  /* Error str1 length */
+            'O', 'n', 'e', '\0',  /* Error str1 message */
+            0x00, 0x00, 0x00, 0x01,  /* Error str2 pointer */
+            0x00, 0x00, 0x00, 0x03,  /* Error str2 length */
+            'T', 'w', 'o', '\0',  /* Error str2 message */
+            0x00, 0x00, 0x00, 0x01,  /* Error str3 pointer */
+            0x00, 0x00, 0x00, 0x05,  /* Error str3 length */
+            'T', 'h', 'r', 'e',  /* Error str3 message */
+            'e', '\0', '\0', '\0',
+            0x00, 0x00, 0x00, 0x01,  /* Error int1 */
+            0x00, 0x00, 0x00, 0x02,  /* Error int2 */
+            0x00, 0x00, 0x00, 0x00,  /* Error network pointer */
+        },
+        .header = { 0, 0, 0, 0, 0, 0 },
+    };
+    memset(&err, 0, sizeof(err));
+
+    if (virNetMessageDecodeLength(&msg) < 0) {
+        VIR_DEBUG("Failed to decode message header");
+        return -1;
+    }
+
+    if (msg.bufferOffset != 0x4) {
+        VIR_DEBUG("Expecting offset %zu got %zu",
+                  (size_t)4, msg.bufferOffset);
+        return -1;
+    }
+
+    if (msg.bufferLength != 0x74) {
+        VIR_DEBUG("Expecting length %zu got %zu",
+                  (size_t)0x74, msg.bufferLength);
+        return -1;
+    }
+
+    if (virNetMessageDecodeHeader(&msg) < 0) {
+        VIR_DEBUG("Failed to decode message header");
+        return -1;
+    }
+
+    if (msg.bufferOffset != 28) {
+        VIR_DEBUG("Expect message offset %zu got %zu",
+                  msg.bufferOffset, (size_t)28);
+        return -1;
+    }
+
+    if (msg.bufferLength != 0x74) {
+        VIR_DEBUG("Expecting length %zu got %zu",
+                  (size_t)0x1c, msg.bufferLength);
+        return -1;
+    }
+
+    if (virNetMessageDecodePayload(&msg, (xdrproc_t)xdr_virNetMessageError, &err) < 0) {
+        VIR_DEBUG("Failed to decode message payload");
+        return -1;
+    }
+
+    if (err.code != VIR_ERR_INTERNAL_ERROR) {
+        VIR_DEBUG("Expect code %d got %d",
+                  VIR_ERR_INTERNAL_ERROR, err.code);
+        return -1;
+    }
+
+    if (err.domain != VIR_FROM_RPC) {
+        VIR_DEBUG("Expect domain %d got %d",
+                  VIR_ERR_RPC, err.domain);
+        return -1;
+    }
+
+    if (err.message == NULL ||
+        STRNEQ(*err.message, "Hello World")) {
+        VIR_DEBUG("Expect str1 'Hello World' got %s",
+                  err.message ? *err.message : "(null)");
+        return -1;
+    }
+
+    if (err.dom != NULL) {
+        VIR_DEBUG("Expect NULL dom");
+        return -1;
+    }
+
+    if (err.level != VIR_ERR_ERROR) {
+        VIR_DEBUG("Expect leve %d got %d",
+                  VIR_ERR_ERROR, err.level);
+        return -1;
+    }
+
+    if (err.str1 == NULL ||
+        STRNEQ(*err.str1, "One")) {
+        VIR_DEBUG("Expect str1 'One' got %s",
+                  err.str1 ? *err.str1 : "(null)");
+        return -1;
+    }
+
+    if (err.str2 == NULL ||
+        STRNEQ(*err.str2, "Two")) {
+        VIR_DEBUG("Expect str3 'Two' got %s",
+                  err.str2 ? *err.str2 : "(null)");
+        return -1;
+    }
+
+    if (err.str3 == NULL ||
+        STRNEQ(*err.str3, "Three")) {
+        VIR_DEBUG("Expect str3 'Three' got %s",
+                  err.str3 ? *err.str3 : "(null)");
+        return -1;
+    }
+
+    if (err.int1 != 1) {
+        VIR_DEBUG("Expect int1 1 got %d",
+                  err.int1);
+        return -1;
+    }
+
+    if (err.int2 != 2) {
+        VIR_DEBUG("Expect int2 2 got %d",
+                  err.int2);
+        return -1;
+    }
+
+    if (err.net != NULL) {
+        VIR_DEBUG("Expect NULL network");
+        return -1;
+    }
+
+    xdr_free((xdrproc_t)xdr_virNetMessageError, (void*)&err);
+    return 0;
+}
+
+static int testMessagePayloadStreamEncode(const void *args ATTRIBUTE_UNUSED)
+{
+    char stream[] = "The quick brown fox jumps over the lazy dog";
+    static virNetMessage msg;
+    static const char expect[] = {
+        0x00, 0x00, 0x00, 0x47,  /* Length */
+        0x11, 0x22, 0x33, 0x44,  /* Program */
+        0x00, 0x00, 0x00, 0x01,  /* Version */
+        0x00, 0x00, 0x06, 0x66,  /* Procedure */
+        0x00, 0x00, 0x00, 0x03,  /* Type */
+        0x00, 0x00, 0x00, 0x99,  /* Serial */
+        0x00, 0x00, 0x00, 0x02,  /* Status */
+
+        'T', 'h', 'e', ' ',
+        'q', 'u', 'i', 'c',
+        'k', ' ', 'b', 'r',
+        'o', 'w', 'n', ' ',
+        'f', 'o', 'x', ' ',
+        'j', 'u', 'm', 'p',
+        's', ' ', 'o', 'v',
+        'e', 'r', ' ', 't',
+        'h', 'e', ' ', 'l',
+        'a', 'z', 'y', ' ',
+        'd', 'o', 'g',
+    };
+    memset(&msg, 0, sizeof(msg));
+
+    msg.header.prog = 0x11223344;
+    msg.header.vers = 0x01;
+    msg.header.proc = 0x666;
+    msg.header.type = VIR_NET_STREAM;
+    msg.header.serial = 0x99;
+    msg.header.status = VIR_NET_CONTINUE;
+
+    if (virNetMessageEncodeHeader(&msg) < 0)
+        return -1;
+
+    if (virNetMessageEncodePayloadRaw(&msg, stream, strlen(stream)) < 0)
+        return -1;
+
+    if (ARRAY_CARDINALITY(expect) != msg.bufferLength) {
+        VIR_DEBUG("Expect message length %zu got %zu",
+                  sizeof(expect), msg.bufferLength);
+        return -1;
+    }
+
+    if (msg.bufferOffset != 0) {
+        VIR_DEBUG("Expect message offset 0 got %zu",
+                  msg.bufferOffset);
+        return -1;
+    }
+
+    if (memcmp(expect, msg.buffer, sizeof(expect)) != 0) {
+        virtTestDifferenceBin(stderr, expect, msg.buffer, sizeof(expect));
+        return -1;
+    }
+
+    return 0;
+}
+
+
+static int
+mymain(void)
+{
+    int ret = 0;
+
+    signal(SIGPIPE, SIG_IGN);
+
+    if (virtTestRun("Message Header Encode", 1, testMessageHeaderEncode, NULL) < 0)
+        ret = -1;
+
+    if (virtTestRun("Message Header Decode", 1, testMessageHeaderDecode, NULL) < 0)
+        ret = -1;
+
+    if (virtTestRun("Message Payload Encode", 1, testMessagePayloadEncode, NULL) < 0)
+        ret = -1;
+
+    if (virtTestRun("Message Payload Decode", 1, testMessagePayloadDecode, NULL) < 0)
+        ret = -1;
+
+    if (virtTestRun("Message Payload Stream Encode", 1, testMessagePayloadStreamEncode, NULL) < 0)
+        ret = -1;
+
+    return (ret==0 ? EXIT_SUCCESS : EXIT_FAILURE);
+}
+
+VIRT_TEST_MAIN(mymain)
-- 
1.7.4.4




More information about the libvir-list mailing list