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 | 495 +++++++++++++++++++++++++++++++++++++++++++++ 10 files changed, 1020 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 5d3ef26..a47c90e 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 81de425..8b5fa17 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -69,6 +69,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 93f39fb..c5fab97 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1190,6 +1190,7 @@ endif 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 7ae50a2..2b1aa6b 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 \ @@ -184,6 +184,7 @@ TESTS = virshtest \ commandtest \ seclabeltest \ hashtest \ + virnetmessagetest \ $(test_scripts) if WITH_XEN @@ -400,6 +401,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..e707b67 --- /dev/null +++ b/tests/virnetmessagetest.c @@ -0,0 +1,495 @@ +/* + * 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@xxxxxxxxxx> + */ + +#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 "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 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list