[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH 2/3] asn1 ber visitors
From: |
jschopp |
Subject: |
[Qemu-devel] [PATCH 2/3] asn1 ber visitors |
Date: |
Tue, 26 Feb 2013 17:03:56 -0600 |
User-agent: |
quilt/0.60-1 |
These patches implement asn1 ber visitors for encoding and decoding data.
References: <address@hidden>
Content-Disposition: inline; filename=asn1_all.diff
Signed-off-by: Stefan Berger <address@hidden>
Signed-off-by: Joel Schopp <address@hidden>
---
Makefile.objs | 10
ber/Makefile.objs | 2
ber/ber-common.c | 56 ++
ber/ber-input-visitor.c | 969 ++++++++++++++++++++++++++++++++++++++++++++
ber/ber-input-visitor.h | 30 +
ber/ber-output-visitor.c | 563 +++++++++++++++++++++++++
ber/ber-output-visitor.h | 28 +
ber/ber.h | 85 +++
include/qapi/qmp/qerror.h | 6
include/qapi/visitor-impl.h | 23 +
include/qapi/visitor.h | 10
qapi/qapi-visit-core.c | 30 +
12 files changed, 1811 insertions(+), 1 deletion(-)
Index: b/Makefile.objs
===================================================================
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -57,7 +57,12 @@ common-obj-$(CONFIG_POSIX) += os-posix.o
common-obj-$(CONFIG_LINUX) += fsdev/
common-obj-y += migration.o migration-tcp.o
-common-obj-y += qemu-char.o qemu-file.o #aio.o
+
+#ber has to be linked against qemu-file.o so we must add them on the same line
+ber-nested-y = ber-input-visitor.o ber-output-visitor.o ber-common.o
+ber-obj-y = $(addprefix ber/, $(ber-nested-y))
+
+common-obj-y += qemu-char.o qemu-file.o $(ber-obj-y) #aio.o
common-obj-y += block-migration.o
common-obj-y += page_cache.o xbzrle.o
@@ -116,4 +121,7 @@ nested-vars += \
qga-obj-y \
block-obj-y \
common-obj-y
+# \
+# ber-obj-y
+
dummy := $(call unnest-vars)
Index: b/ber/ber-common.c
===================================================================
--- /dev/null
+++ b/ber/ber-common.c
@@ -0,0 +1,56 @@
+/*
+ * ASN.1 Basic Encoding Rules Common functions
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Stefan Berger <address@hidden>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include <stdint.h>
+
+#include "ber.h"
+
+static const char *ber_type_names[] = {
+ "BER_TYPE_EOC",
+ "BER_TYPE_BOOLEAN",
+ "BER_TYPE_INTEGER",
+ "BER_TYPE_BIT_STRING",
+ "BER_TYPE_OCTET_STRING",
+ "BER_TYPE_NULL",
+ "BER_TYPE_OBJECT_ID",
+ "BER_TYPE_OBJECT_DESC",
+ "BER_TYPE_EXTERNAL",
+ "BER_TYPE_REAL",
+ "BER_TYPE_ENUMERATED",
+ "BER_TYPE_EMBEDDED",
+ "BER_TYPE_UTF8_STRING",
+ "BER_TYPE_RELATIVE_OID",
+ "BER_TYPE_UNUSED_0xE",
+ "BER_TYPE_UNUSED_0xF",
+ "BER_TYPE_SEQUENCE",
+ "BER_TYPE_SET",
+ "BER_TYPE_NUMERIC_STRING",
+ "BER_TYPE_PRINTABLE_STRING",
+ "BER_TYPE_T61STRING",
+ "BER_TYPE_VIDEOTEX_STRING",
+ "BER_TYPE_IA5_STRING",
+ "BER_TYPE_UTCTIME",
+ "BER_TYPE_GENERALIZED_TIME",
+ "BER_TYPE_GRAPHIC_STRING",
+ "BER_TYPE_VISIBLE_STRING",
+ "BER_TYPE_GENERAL_STRING",
+ "BER_TYPE_UNIVERSAL_STRING",
+ "BER_TYPE_CHARACTER_STRING"
+ "BER_TYPE_BMP_STRING",
+ "BER_TYPE_LONG_FORM",
+};
+
+const char *ber_type_to_str(uint8_t ber_type)
+{
+ return ber_type_names[ber_type & BER_TYPE_TAG_MASK];
+}
Index: b/ber/ber-input-visitor.c
===================================================================
--- /dev/null
+++ b/ber/ber-input-visitor.c
@@ -0,0 +1,969 @@
+/*
+ * BER Input Visitor
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Anthony Liguori <address@hidden>
+ * Stefan Berger <address@hidden>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include "qemu-common.h"
+#include "ber-input-visitor.h"
+#include "qemu/queue.h"
+#include "qemu-common.h"
+#include "hw/hw.h"
+#include "ber.h"
+#include "include/qapi/qmp/qerror.h"
+#include "migration/qemu-file.h"
+#include "qapi/visitor-impl.h"
+
+#define AIV_STACK_SIZE 1024
+
+/* whether to allow the parsing of primitives that are fragmented */
+#define BER_ALLOW_FRAGMENTED_PRIMITIVES
+
+/* #define BER_DEBUG */
+
+typedef struct StackEntry {
+ uint64_t cur_pos;
+} StackEntry;
+
+struct BERInputVisitor {
+ Visitor visitor;
+ QEMUFile *qfile;
+ uint64_t cur_pos;
+ StackEntry stack[AIV_STACK_SIZE];
+ int nb_stack;
+ uint64_t max_allowed_buffer_size;
+ uint64_t largest_needed_buffer;
+};
+
+static BERInputVisitor *to_biv(Visitor *v)
+{
+ return container_of(v, BERInputVisitor, visitor);
+}
+
+static void ber_input_push(BERInputVisitor *aiv,
+ uint64_t cur_pos, Error **errp)
+{
+ aiv->stack[aiv->nb_stack].cur_pos = cur_pos;
+ aiv->nb_stack++;
+
+ if (aiv->nb_stack >= AIV_STACK_SIZE) {
+ error_set(errp, QERR_BUFFER_OVERRUN);
+ return;
+ }
+}
+
+static uint64_t ber_input_pop(BERInputVisitor *aiv, Error **errp)
+{
+ aiv->nb_stack--;
+
+ if (aiv->nb_stack < 0) {
+ error_set(errp, QERR_BUFFER_OVERRUN);
+ return 0;
+ }
+
+ return aiv->stack[aiv->nb_stack].cur_pos;
+}
+
+/*
+ * Read a type tag from the stream. Up-to 32 bit type tags are supported
+ * for reading and otherwise an error is returned. Anything larger than that
+ * would not be reasonable and could only be abused.
+ */
+static uint32_t ber_read_type(BERInputVisitor *aiv, uint8_t *ber_type_flags,
+ Error **errp)
+{
+ uint32_t type;
+ uint8_t byte;
+ uint8_t ctr = 0;
+ char buf[128];
+
+ if (qemu_read_bytes(aiv->qfile, &byte, 1) != 1) {
+ error_set(errp, QERR_QEMUFILE_ERROR,
+ "Error while reading type");
+ return 0;
+ }
+ aiv->cur_pos++;
+ type = byte;
+
+ *ber_type_flags = type & (BER_TYPE_P_C_MASK | BER_TYPE_CLASS_MASK);
+
+ if ((type & BER_TYPE_TAG_MASK) == BER_TYPE_LONG_FORM) {
+ type = 0;
+ while (true) {
+ type <<= 7;
+ if (qemu_read_bytes(aiv->qfile, &byte, 1) != 1) {
+ error_set(errp, QERR_QEMUFILE_ERROR,
+ "Error while reading long type");
+ return 0;
+ }
+ aiv->cur_pos++;
+
+ type |= (byte & 0x7f);
+ if ((byte & 0x80) == 0) {
+ break;
+ }
+ ctr += 7; /* read 7 bits */
+ if (ctr >= (sizeof(type) * 8)) {
+ /* only support 32 bit length identifiers */
+ snprintf(buf, sizeof(buf),
+ "type tag is larger than 32 bit (offset %" PRIu64
+ ")", aiv->cur_pos);
+ error_set(errp, QERR_INVALID_STREAM,
+ buf);
+ return 0;
+ }
+ }
+ } else {
+ type &= BER_TYPE_TAG_MASK;
+ }
+
+ return type;
+}
+
+static uint64_t ber_read_length(BERInputVisitor *aiv, bool *is_indefinite,
+ Error **errp)
+{
+ uint8_t byte, c, int_len;
+ uint64_t len = 0;
+ QEMUFile *qfile = aiv->qfile;
+ char buf[128];
+
+ *is_indefinite = false;
+
+ if (qemu_read_bytes(qfile, &byte, 1) != 1) {
+ error_set(errp, QERR_QEMUFILE_ERROR,
+ "Error while reading length indicator");
+ return ~0x0ULL;
+ }
+ aiv->cur_pos++;
+
+ if (byte == BER_LENGTH_INDEFINITE) {
+ *is_indefinite = true;
+ return ~0x0ULL;
+ }
+
+ if (!(byte & BER_LENGTH_LONG)) {
+ len = byte;
+ } else {
+ int_len = byte & BER_LENGTH_MASK;
+ if (int_len > 8) {
+ snprintf(buf, sizeof(buf), "ASN.1 integer length field %d > 8",
+ int_len);
+ /* Length can be up to 127 byte, but it seems
+ * safe to assume any input will be < 1TB in length. */
+ error_set(errp, QERR_INVALID_PARAMETER, buf);
+ return ~0x0ULL;
+ }
+ for (c = 0; c < int_len; c++) {
+ len <<= 8;
+ if (qemu_read_bytes(qfile, &byte, 1) != 1) {
+ error_set(errp, QERR_QEMUFILE_ERROR,
+ "Error while reading length");
+ return ~0x0ULL;
+ }
+ len |= byte;
+ }
+ aiv->cur_pos += int_len;
+ }
+
+ if (len > aiv->max_allowed_buffer_size) {
+ snprintf(buf, sizeof(buf),
+ "Length indicator (%"PRIu64") in input byte stream "
+ "exceeds maximum allowed length (%"PRIu64").",
+ len, aiv->max_allowed_buffer_size);
+ error_set(errp, QERR_INVALID_STREAM, buf);
+ return ~0x0ULL;
+ }
+
+ if (len > aiv->largest_needed_buffer) {
+ aiv->largest_needed_buffer = len;
+ }
+
+ return len;
+}
+
+static uint64_t ber_peek_is_eoc(BERInputVisitor *biv, Error **errp)
+{
+ uint8_t buf[2];
+ QEMUFile *qfile = biv->qfile;
+
+ if (qemu_peek_bytes(qfile, buf, 2, 0) != 2) {
+ error_set(errp, QERR_QEMUFILE_ERROR,
+ "Error while peeking for EOC");
+ return ~0x0ULL;
+ }
+
+ if (buf[0] == BER_TYPE_EOC && buf[1] == 0) {
+ return 1;
+ }
+
+ return 0;
+}
+
+static void ber_skip_bytes(BERInputVisitor *aiv, uint64_t to_skip,
+ Error **errp)
+{
+ uint8_t buf[1024];
+ uint32_t skip;
+
+ /* skip length bytes */
+ while (to_skip > 0) {
+ skip = MIN(to_skip, sizeof(buf));
+ if (qemu_read_bytes(aiv->qfile, buf, skip) != skip) {
+ error_set(errp, QERR_QEMUFILE_ERROR,
+ "Error while skipping over bytes");
+ return;
+ }
+ aiv->cur_pos += skip;
+ to_skip -= skip;
+ }
+}
+
+static void ber_skip_until_eoc(BERInputVisitor *aiv, Error **errp)
+{
+ uint32_t ber_type_tag;
+ uint64_t length;
+ bool is_indefinite;
+ uint8_t ber_type_flags;
+ uint64_t indefinite_nesting = 1;
+ char buf[128];
+
+ while (!error_is_set(errp)) {
+ ber_type_tag = ber_read_type(aiv, &ber_type_flags, errp);
+ if (error_is_set(errp)) {
+ return;
+ }
+
+ length = ber_read_length(aiv, &is_indefinite, errp);
+ if (error_is_set(errp)) {
+ return;
+ }
+ if (ber_type_tag == BER_TYPE_EOC) {
+ if (length) {
+ snprintf(buf, sizeof(buf),
+ "ASN.1 EOC length field at offset %" PRIu64
+ " is invalid", aiv->cur_pos);
+ error_set(errp, QERR_INVALID_PARAMETER, buf);
+ return;
+ }
+ if (!indefinite_nesting) {
+ snprintf(buf, sizeof(buf),
+ "ASN.1 EOC at offset %" PRIu64
+ "not within an indefinite length",
+ aiv->cur_pos);
+ error_set(errp, QERR_INVALID_PARAMETER, buf);
+ return;
+ }
+#ifdef BER_DEBUG
+ fprintf(stderr, "found end! nesting=%" PRIdMAX
+ ", pos=%" PRIu64 "\n",
+ indefinite_nesting, aiv->cur_pos);
+#endif
+ if (!--indefinite_nesting) {
+ return;
+ }
+ }
+ if (is_indefinite) {
+ if ((ber_type_flags & BER_TYPE_P_C_MASK) == BER_TYPE_PRIMITIVE) {
+ snprintf(buf, sizeof(buf),
+ "ASN.1 indefinite length in a primitive type "
+ "at offset %" PRIu64,
+ aiv->cur_pos);
+ error_set(errp, QERR_INVALID_PARAMETER, buf);
+ return;
+ }
+ if (indefinite_nesting == ~0x0ULL) {
+ snprintf(buf, sizeof(buf),
+ "ASN.1 indefinite nesting level is too large "
+ "(offset %" PRIu64 ")",
+ aiv->cur_pos);
+ error_set(errp, QERR_INVALID_PARAMETER, buf);
+ return;
+ }
+ ++indefinite_nesting;
+ } else {
+#ifdef BER_DEBUG
+ fprintf(stderr, "skipping type '%s' of length "
+ "%" PRIu64 " at %" PRIu64 ".\n",
+ ber_type_to_str(ber_type_tag), length, aiv->cur_pos);
+#endif
+ ber_skip_bytes(aiv, length, errp);
+ }
+ }
+}
+
+static void ber_input_start_constructed(Visitor *v, uint32_t exp_ber_type,
+ uint8_t exp_ber_flags, void **obj,
+ const char *kind, const char *name,
+ size_t size, Error **errp)
+{
+ BERInputVisitor *aiv = to_biv(v);
+ uint32_t ber_type_tag;
+ uint8_t ber_type_flags;
+ int64_t len;
+ bool is_indefinite;
+ char buf[128];
+
+ ber_type_tag = ber_read_type(aiv, &ber_type_flags, errp);
+ if (error_is_set(errp)) {
+ return;
+ }
+
+ if (ber_type_tag != exp_ber_type || ber_type_flags != exp_ber_flags) {
+ sprintf(buf, "%s at offset %" PRIu64 "\n",
+ ber_type_to_str(exp_ber_type), aiv->cur_pos);
+
+ error_set(errp, QERR_INVALID_PARAMETER_TYPE,
+ ber_type_to_str(ber_type_tag),
+ buf);
+ return;
+ }
+
+ if ((ber_type_flags & BER_TYPE_P_C_MASK) == BER_TYPE_PRIMITIVE) {
+ snprintf(buf, sizeof(buf), "primitive type (%s)",
+ ber_type_to_str(ber_type_tag));
+ error_set(errp, QERR_INVALID_PARAMETER_TYPE,
+ buf, "constructed type");
+ return;
+ }
+
+ len = ber_read_length(aiv, &is_indefinite, errp);
+ if (error_is_set(errp)) {
+ return;
+ }
+
+ if (!is_indefinite) {
+#ifdef BER_DEBUG
+ fprintf(stderr, "structure/set len: %" PRIi64 "\n", len);
+#endif
+ ber_input_push(aiv, aiv->cur_pos + len, errp);
+ } else {
+#ifdef BER_DEBUG
+ fprintf(stderr, "indefinite length encoding!\n");
+#endif
+ ber_input_push(aiv, 0, errp);
+ }
+
+ if (error_is_set(errp)) {
+ return;
+ }
+
+ if (size > 0 && *obj == NULL) {
+ *obj = g_malloc0(size);
+#ifdef BER_DEBUG
+ fprintf(stderr, "for type '%s' allocated buffer at %p, size = %zu\n",
+ ber_type_to_str(ber_type_tag), *obj, size);
+#endif
+ }
+}
+
+static void ber_input_end_constructed(Visitor *v, Error **errp)
+{
+ uint64_t new_pos;
+ BERInputVisitor *aiv = to_biv(v);
+
+ new_pos = ber_input_pop(aiv, errp);
+
+ if (new_pos != 0) {
+#ifdef BER_DEBUG
+ fprintf(stderr, "new_pos = %" PRIu64 "\n", new_pos);
+#endif
+ aiv->cur_pos = new_pos;
+ } else {
+#ifdef BER_DEBUG
+ fprintf(stderr, "searching for end...\n");
+ fprintf(stderr, "cur_pos = %" PRIu64 "\n", aiv->cur_pos);
+#endif
+ ber_skip_until_eoc(aiv, errp);
+ }
+}
+
+static void ber_input_start_struct(Visitor *v, void **obj, const char *kind,
+ const char *name, size_t size, Error **errp)
+{
+ ber_input_start_constructed(v, BER_TYPE_SEQUENCE, BER_TYPE_CONSTRUCTED,
+ obj, kind, name, size, errp);
+}
+
+static void ber_input_end_struct(Visitor *v, Error **errp)
+{
+ ber_input_end_constructed(v, errp);
+}
+
+static void ber_input_start_array(Visitor *v, void **obj,
+ const char *name, size_t elem_count,
+ size_t elem_size, Error **errp)
+{
+ ber_input_start_constructed(v, BER_TYPE_SET, BER_TYPE_CONSTRUCTED,
+ obj, NULL, name,
+ elem_count * elem_size, errp);
+}
+
+static void ber_input_next_array(Visitor *v, Error **errp)
+{
+ /* nothing to do here */
+}
+
+static void ber_input_end_array(Visitor *v, Error **errp)
+{
+ ber_input_end_constructed(v, errp);
+}
+
+static void ber_input_start_list(Visitor *v, const char *name,
+ Error **errp)
+{
+ void *obj = NULL;
+ ber_input_start_constructed(v, BER_TYPE_CUSTOM_LIST, BER_TYPE_CONSTRUCTED,
+ obj, NULL, name, 0, errp);
+ g_free(obj);
+}
+
+static GenericList *ber_input_next_list(Visitor *v, GenericList **list,
+ Error **errp)
+{
+ BERInputVisitor *biv = to_biv(v);
+ GenericList *entry;
+ StackEntry *se = &biv->stack[biv->nb_stack - 1];
+
+ if (se->cur_pos == 0) {
+ /* indefinite lenght encoding is used */
+ se = &biv->stack[biv->nb_stack];
+ if (ber_peek_is_eoc(biv, errp) != 0) {
+ return NULL;
+ }
+ } else if (se->cur_pos <= biv->cur_pos) {
+ return NULL;
+ }
+
+ entry = g_malloc0(sizeof(*entry));
+ if (*list) {
+ (*list)->next = entry;
+ }
+
+ return entry;
+}
+
+static void ber_input_end_list(Visitor *v, Error **errp)
+{
+ ber_input_end_constructed(v, errp);
+}
+
+static void ber_input_integer(Visitor *v, uint8_t *obj, uint8_t maxbytes,
+ Error **errp)
+{
+ BERInputVisitor *aiv = to_biv(v);
+ uint32_t ber_type_tag;
+ uint8_t ber_type_flags, byte;
+ bool is_indefinite;
+ uint64_t len;
+ uint64_t val = 0;
+ int c;
+ char buf[128];
+
+#ifdef BER_DEBUG
+ fprintf(stderr, "reading int to %p\n", obj);
+#endif
+
+ ber_type_tag = ber_read_type(aiv, &ber_type_flags, errp);
+ if (error_is_set(errp)) {
+ return;
+ }
+
+#ifdef BER_DEBUG
+ fprintf(stderr, "%s: got type: 0x%02x, expected 0x%02x\n",
+ __func__, ber_type_tag, BER_TYPE_INTEGER);
+#endif
+
+ if (ber_type_tag != BER_TYPE_INTEGER || ber_type_flags != 0) {
+ error_set(errp, QERR_INVALID_PARAMETER_TYPE,
+ ber_type_to_str(ber_type_tag),
+ ber_type_to_str(BER_TYPE_INTEGER));
+ return;
+ }
+ len = ber_read_length(aiv, &is_indefinite, errp);
+ if (error_is_set(errp)) {
+ return;
+ }
+#ifdef BER_DEBUG
+ fprintf(stderr, "pos: %" PRIu64 " int len: %" PRIi64 "\n",
+ aiv->cur_pos, len);
+#endif
+
+ if (is_indefinite) {
+ error_set(errp, QERR_INVALID_PARAMETER_VALUE,
+ "ASN.1 int indicator is indefinite",
+ "[1..8]");
+ return;
+ }
+ if (len > maxbytes) {
+ snprintf(buf, sizeof(buf), "ASN.1 integer length indicator %" PRIi64
+ " is larger than expected (%u bytes)\n",
+ len, maxbytes);
+ error_set(errp, QERR_INVALID_PARAMETER_VALUE,
+ buf, "[1..8]");
+ return;
+ }
+
+ for (c = 0; c < len ; c++) {
+ val <<= 8;
+ if (qemu_read_bytes(aiv->qfile, &byte, 1) != 1) {
+ error_set(errp, QERR_QEMUFILE_ERROR,
+ "Error while reading integer");
+ return;
+ }
+ val |= byte;
+ if (c == 0 && (val & 0x80) == 0x80) {
+ /* sign extend */
+ val |= 0xFFFFFFFFFFFFFF00ULL;
+ }
+ }
+ aiv->cur_pos += len;
+#ifdef BER_DEBUG
+ fprintf(stderr, "pos: %" PRIu64 " int: %" PRIu64 "\n", aiv->cur_pos, val);
+#endif
+
+ memcpy(obj, &val, maxbytes);
+}
+
+static void ber_input_type_int(Visitor *v, int64_t *obj, const char *name,
+ Error **errp)
+{
+ ber_input_integer(v, (uint8_t *)obj, sizeof(*obj), errp);
+}
+
+static void ber_input_type_uint8_t(Visitor *v, uint8_t *obj,
+ const char *name, Error **errp)
+{
+ ber_input_integer(v, (uint8_t *)obj, sizeof(*obj), errp);
+}
+
+static void ber_input_type_uint16_t(Visitor *v, uint16_t *obj,
+ const char *name, Error **errp)
+{
+ ber_input_integer(v, (uint8_t *)obj, sizeof(*obj), errp);
+}
+
+static void ber_input_type_uint32_t(Visitor *v, uint32_t *obj,
+ const char *name, Error **errp)
+{
+ ber_input_integer(v, (uint8_t *)obj, sizeof(*obj), errp);
+}
+
+static void ber_input_type_uint64_t(Visitor *v, uint64_t *obj,
+ const char *name, Error **errp)
+{
+ ber_input_integer(v, (uint8_t *)obj, sizeof(*obj), errp);
+}
+
+static void ber_input_type_int8_t(Visitor *v, int8_t *obj,
+ const char *name, Error **errp)
+{
+ ber_input_integer(v, (uint8_t *)obj, sizeof(*obj), errp);
+}
+
+static void ber_input_type_int16_t(Visitor *v, int16_t *obj,
+ const char *name, Error **errp)
+{
+ ber_input_integer(v, (uint8_t *)obj, sizeof(*obj), errp);
+}
+
+static void ber_input_type_int32_t(Visitor *v, int32_t *obj,
+ const char *name, Error **errp)
+{
+ ber_input_integer(v, (uint8_t *)obj, sizeof(*obj), errp);
+}
+
+static void ber_input_type_int64_t(Visitor *v, int64_t *obj,
+ const char *name, Error **errp)
+{
+ ber_input_integer(v, (uint8_t *)obj, sizeof(*obj), errp);
+}
+
+static void ber_input_type_bool(Visitor *v, bool *obj, const char *name,
+ Error **errp)
+{
+ BERInputVisitor *aiv = to_biv(v);
+ uint32_t ber_type_tag;
+ uint8_t ber_type_flags, byte;
+ bool is_indefinite;
+ uint64_t len;
+ char buf[128];
+
+ ber_type_tag = ber_read_type(aiv, &ber_type_flags, errp);
+ if (error_is_set(errp)) {
+ return;
+ }
+
+ if (ber_type_tag != BER_TYPE_BOOLEAN || ber_type_flags != 0) {
+ error_set(errp, QERR_INVALID_PARAMETER_TYPE,
+ ber_type_to_str(ber_type_tag),
+ ber_type_to_str(BER_TYPE_BOOLEAN));
+ return;
+ }
+ len = ber_read_length(aiv, &is_indefinite, errp);
+ if (error_is_set(errp)) {
+ return;
+ }
+#ifdef BER_DEBUG
+ fprintf(stderr, "pos: %" PRIu64 " bool len: %" PRIi64 "\n",
+ aiv->cur_pos, len);
+#endif
+
+ if (is_indefinite || len != 1) {
+ snprintf(buf, sizeof(buf),
+ "ASN.1 bool length indicator at offset %" PRIu64
+ " is indefinite or != 1",
+ aiv->cur_pos);
+ error_set(errp, QERR_INVALID_PARAMETER_VALUE,
+ buf, "1");
+ return;
+ }
+ if (qemu_read_bytes(aiv->qfile, &byte, 1) != 1) {
+ error_set(errp, QERR_QEMUFILE_ERROR,
+ "Error while reading boolean");
+ return;
+ }
+ aiv->cur_pos++;
+ *obj = byte;
+
+#ifdef BER_DEBUG
+ fprintf(stderr, "pos: %" PRIu64 " bool: %d\n", aiv->cur_pos, *obj);
+#endif
+}
+
+/* Function for recursive reading of fragmented primitives */
+static uint32_t ber_input_fragment(BERInputVisitor *aiv,
+ uint32_t exp_type_tag,
+ uint8_t exp_type_flags,
+ uint8_t **buffer, size_t *buffer_len,
+ bool may_realloc,
+ uint32_t offset, uint32_t nesting,
+ bool indefinite, uint64_t max_pos,
+ const char *name, Error **errp)
+{
+ uint32_t ber_type_tag;
+ uint8_t ber_type_flags;
+ uint32_t bytes_read = 0;
+ bool is_indefinite;
+ uint64_t len;
+ char buf[128];
+
+ assert((exp_type_flags & BER_TYPE_CONSTRUCTED) == BER_TYPE_PRIMITIVE);
+
+ ber_type_tag = ber_read_type(aiv, &ber_type_flags, errp);
+ if (error_is_set(errp)) {
+ return 0;
+ }
+
+ if (ber_type_tag != exp_type_tag) {
+ error_set(errp, QERR_INVALID_PARAMETER_TYPE,
+ ber_type_to_str(ber_type_tag & BER_TYPE_TAG_MASK),
+ ber_type_to_str(exp_type_tag));
+ return 0;
+ }
+
+ if ((ber_type_flags & BER_TYPE_CONSTRUCTED)) {
+#ifndef BER_ALLOW_FRAGMENTED_PRIMITIVES
+ error_set(errp, QERR_INVALID_STREAM,
+ "constructed encoding of primitive types is not supported");
+ goto err_exit;
+#else
+ if (nesting == 1) {
+ /* don't allow further nesting */
+ error_set(errp, QERR_INVALID_STREAM, "invalid nesting");
+ goto err_exit;
+ }
+ len = ber_read_length(aiv, &is_indefinite, errp);
+ if (error_is_set(errp)) {
+ goto err_exit;
+ }
+#ifdef BER_DEBUG
+ fprintf(stderr, "pos: %" PRIu64 " string len: %" PRIi64 "\n",
+ aiv->cur_pos, len);
+#endif
+
+ if (!is_indefinite) {
+ if ((*buffer) == NULL) {
+ /* allocate buffer once; due to the ASN.1 overhead it
+ * will be bigger than what we need */
+ *buffer = g_malloc0(len);
+ *buffer_len = len;
+ may_realloc = false;
+ }
+ }
+#ifdef BER_DEBUG
+ fprintf(stderr, "recursing now to read constructed type.\n");
+ fprintf(stderr, " is_indefinite: %d\n", is_indefinite);
+#endif
+ bytes_read += ber_input_fragment(aiv, exp_type_tag, exp_type_flags,
+ buffer, buffer_len, may_realloc,
+ offset, nesting + 1, is_indefinite,
+ aiv->cur_pos + len, name, errp);
+ return bytes_read;
+#endif
+ }
+
+ while (true) {
+ /* Would reading the length carry us beyond what we are allowed to
+ * read?
+ */
+ if (!indefinite &&
+ max_pos != 0 &&
+ aiv->cur_pos + 1 > max_pos) {
+ snprintf(buf, sizeof(buf),
+ "data stream would cause parsing beyond "
+ "allowed offset at %" PRIu64,
+ max_pos);
+ /* input stream is malformed */
+ error_set(errp, QERR_INVALID_STREAM, buf);
+ goto err_exit;
+ }
+
+ /* not-constructed case */
+ len = ber_read_length(aiv, &is_indefinite, errp);
+ if (error_is_set(errp)) {
+ goto err_exit;
+ }
+#ifdef BER_DEBUG
+ fprintf(stderr, "pos: %" PRIu64 " string len: %" PRIi64 "\n",
+ aiv->cur_pos, len);
+#endif
+ if (is_indefinite) {
+ snprintf(buf, sizeof(buf),
+ "Got indefinite type length in primitive type (%s) at"
+ "offset %" PRIu64,
+ ber_type_to_str(ber_type_tag), aiv->cur_pos);
+ error_set(errp, QERR_INVALID_PARAMETER, buf);
+ goto err_exit;
+ }
+ /* if max_pos is not set, set it here */
+ if (!indefinite && max_pos == 0) {
+ max_pos = aiv->cur_pos + len;
+ }
+
+ /* Would reading the data carry us beyond what we are allowed to
+ * read ?
+ */
+ if (!indefinite && aiv->cur_pos + len > max_pos) {
+ /* input stream is malformed */
+ snprintf(buf, sizeof(buf),
+ "data stream would cause parsing beyond "
+ "allowed offset at %" PRIu64,
+ max_pos);
+ error_set(errp, QERR_INVALID_STREAM, buf);
+ goto err_exit;
+ }
+
+ if (offset + len > *buffer_len) {
+ if (!may_realloc) {
+ snprintf(buf, sizeof(buf),
+ "given buffer is too small (%lu < %"PRIu64")",
+ (unsigned long)*buffer_len, offset + len);
+ error_set(errp, QERR_INVALID_STREAM, buf);
+ }
+ /* allocate one more byte for strings, set to 0 */
+ *buffer = g_realloc(*buffer, offset + len + 1);
+ *buffer_len = offset + len;
+ (*buffer)[offset+len] = 0;
+ }
+
+ if (qemu_read_bytes(aiv->qfile,
+ &((uint8_t *)*buffer)[offset], len) != len) {
+ error_set(errp, QERR_QEMUFILE_ERROR,
+ "Error while reading data");
+ goto err_exit;
+ }
+
+ offset += len;
+ bytes_read += len;
+
+ aiv->cur_pos += len;
+#ifdef BER_DEBUG
+ if (exp_type_tag == BER_TYPE_IA5_STRING) {
+ fprintf(stderr, "pos: %" PRIu64 " string: %.*s\n", aiv->cur_pos,
+ offset, *buffer);
+ }
+#endif
+
+ if (nesting == 0) {
+ break;
+ }
+
+ /* indefinite length case: loop until we encounter EOC */
+ if (indefinite) {
+ ber_type_tag = ber_read_type(aiv, &ber_type_flags, errp);
+ if (error_is_set(errp)) {
+ goto err_exit;
+ }
+
+ if (ber_type_tag == BER_TYPE_EOC) {
+ uint8_t byte;
+ if (qemu_read_bytes(aiv->qfile, &byte, 1) != 1) {
+ error_set(errp, QERR_QEMUFILE_ERROR,
+ "Error while reading BER_TYPE_EOC length");
+ goto err_exit;
+ }
+ aiv->cur_pos++;
+
+ if (byte != 0) {
+ snprintf(buf, sizeof(buf),
+ "ASN.1 EOC length field is invalid at offset "
+ "%" PRIu64,
+ aiv->cur_pos);
+ error_set(errp, QERR_INVALID_PARAMETER, buf);
+ goto err_exit;
+ }
+ return bytes_read;
+ }
+
+ if (ber_type_tag != exp_type_tag ||
+ ber_type_flags != exp_type_flags) {
+ snprintf(buf, sizeof(buf),
+ "ASN.1 type field or flags are wrong. Found "
+ "0x%x/%u, expected "
+ "0x%x/%u at offset %" PRIu64,
+ ber_type_tag, ber_type_flags,
+ exp_type_tag, exp_type_flags,
+ aiv->cur_pos);
+ error_set(errp, QERR_INVALID_PARAMETER, buf);
+ goto err_exit;
+ }
+ continue;
+ }
+
+ /* in definite length coding case; caller told us how far to read */
+ if (aiv->cur_pos == max_pos) {
+ return bytes_read;
+ }
+
+ ber_type_tag = ber_read_type(aiv, &ber_type_flags, errp);
+ if (error_is_set(errp)) {
+ goto err_exit;
+ }
+
+ if ((ber_type_flags & BER_TYPE_P_C_MASK) == BER_TYPE_CONSTRUCTED) {
+ error_set(errp, QERR_INVALID_PARAMETER_TYPE,
+ "constructed BER type",
+ ber_type_to_str(exp_type_tag));
+ goto err_exit;
+ }
+
+ if (ber_type_tag != exp_type_tag) {
+ error_set(errp, QERR_INVALID_PARAMETER_TYPE,
+ ber_type_to_str(ber_type_tag & BER_TYPE_TAG_MASK),
+ ber_type_to_str(exp_type_tag));
+ goto err_exit;
+ }
+ }
+ return bytes_read;
+
+err_exit:
+ if (may_realloc) {
+ g_free(*buffer);
+ *buffer = NULL;
+ }
+ return 0;
+}
+
+static void ber_input_type_str(Visitor *v, char **obj, const char *name,
+ Error **errp)
+{
+ BERInputVisitor *aiv = to_biv(v);
+ size_t buffer_len = 0;
+
+ ber_input_fragment(aiv, BER_TYPE_IA5_STRING, 0,
+ (uint8_t **)obj, &buffer_len, (*obj == NULL),
+ 0, 0, false, 0, name, errp);
+
+ if (!error_is_set(errp) && *obj == NULL) {
+ /* adjust NULL string to "" */
+ *obj = g_strdup("");
+ }
+}
+
+static void ber_input_sized_buffer(Visitor *v, uint8_t **obj, size_t len,
+ const char *name, Error **errp)
+{
+ BERInputVisitor *aiv = to_biv(v);
+
+ ber_input_fragment(aiv, BER_TYPE_OCTET_STRING, 0,
+ (uint8_t **)obj, &len, (*obj == NULL),
+ 0, 0, false, 0, name, errp);
+
+#ifdef BER_DEBUG
+ fprintf(stderr, "pos: %" PRIu64 " data at: %p data:\n",
+ aiv->cur_pos, *obj);
+ int i;
+ for (i = 0; i < len; i++) {
+ fprintf(stderr, "%02x ", (*obj)[i]);
+ if ((i & 0xf) == 0xf) {
+ fprintf(stderr, "\n");
+ }
+ }
+ fprintf(stderr, "\n");
+#endif
+}
+
+Visitor *ber_input_get_visitor(BERInputVisitor *v)
+{
+ return &v->visitor;
+}
+
+uint64_t ber_input_get_parser_position(BERInputVisitor *v)
+{
+ return v->cur_pos;
+}
+
+uint64_t ber_input_get_largest_needed_buffer(BERInputVisitor *v)
+{
+ return v->largest_needed_buffer;
+}
+
+void ber_input_visitor_cleanup(BERInputVisitor *v)
+{
+ g_free(v);
+}
+
+BERInputVisitor *ber_input_visitor_new(QEMUFile *qfile,
+ uint64_t max_allowed_buffer_size)
+{
+ BERInputVisitor *v;
+
+ v = g_malloc0(sizeof(*v));
+
+ v->visitor.start_struct = ber_input_start_struct;
+ v->visitor.end_struct = ber_input_end_struct;
+ v->visitor.start_array = ber_input_start_array;
+ v->visitor.next_array = ber_input_next_array;
+ v->visitor.end_array = ber_input_end_array;
+ v->visitor.start_list = ber_input_start_list;
+ v->visitor.next_list = ber_input_next_list;
+ v->visitor.end_list = ber_input_end_list;
+ v->visitor.type_int = ber_input_type_int;
+ v->visitor.type_uint8_t = ber_input_type_uint8_t;
+ v->visitor.type_uint16_t = ber_input_type_uint16_t;
+ v->visitor.type_uint32_t = ber_input_type_uint32_t;
+ v->visitor.type_uint64_t = ber_input_type_uint64_t;
+ v->visitor.type_int8_t = ber_input_type_int8_t;
+ v->visitor.type_int16_t = ber_input_type_int16_t;
+ v->visitor.type_int32_t = ber_input_type_int32_t;
+ v->visitor.type_int64_t = ber_input_type_int64_t;
+ v->visitor.type_bool = ber_input_type_bool;
+ v->visitor.type_str = ber_input_type_str;
+ v->visitor.type_sized_buffer = ber_input_sized_buffer;
+
+ v->qfile = qfile;
+ v->cur_pos = 0;
+ v->max_allowed_buffer_size = max_allowed_buffer_size;
+ v->largest_needed_buffer = 0;
+
+ return v;
+}
Index: b/ber/ber-input-visitor.h
===================================================================
--- /dev/null
+++ b/ber/ber-input-visitor.h
@@ -0,0 +1,30 @@
+/*
+ * BER Input Visitor header
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Anthony Liguori <address@hidden>
+ * Stefan Berger <address@hidden>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#ifndef BER_INPUT_VISITOR_H
+#define BER_INPUT_VISITOR_H
+
+#include "include/qapi/visitor.h"
+
+typedef struct BERInputVisitor BERInputVisitor;
+
+BERInputVisitor *ber_input_visitor_new(QEMUFile *,
+ uint64_t max_allowd_buffer_size);
+void ber_input_visitor_cleanup(BERInputVisitor *v);
+uint64_t ber_input_get_parser_position(BERInputVisitor *v);
+uint64_t ber_input_get_largest_needed_buffer(BERInputVisitor *v);
+
+Visitor *ber_input_get_visitor(BERInputVisitor *v);
+
+#endif
Index: b/ber/ber-output-visitor.c
===================================================================
--- /dev/null
+++ b/ber/ber-output-visitor.c
@@ -0,0 +1,563 @@
+/*
+ * BER Output Visitor
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Anthony Liguori <address@hidden>
+ * Stefan Berger <address@hidden>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include "qemu-common.h"
+#include "ber-output-visitor.h"
+#include "qemu/queue.h"
+#include "qemu-common.h"
+#include "include/qapi/qmp/qerror.h"
+#include "hw/hw.h"
+#include "ber.h"
+#include "qapi/visitor-impl.h"
+
+
+#define CER_FRAGMENT_CHUNK_SIZE 1000
+
+/*#define BER_DEBUG*/
+
+typedef struct QStackEntry {
+ QEMUFile *qfile;
+ bool is_list_head;
+ QTAILQ_ENTRY(QStackEntry) node;
+} QStackEntry;
+
+typedef QTAILQ_HEAD(QStack, QStackEntry) QStack;
+
+struct BEROutputVisitor {
+ Visitor visitor;
+ QStack stack;
+ QEMUFile *qfile;
+
+ BERTypePC mode;
+};
+
+static BEROutputVisitor *to_aov(Visitor *v)
+{
+ return container_of(v, BEROutputVisitor, visitor);
+}
+
+static void ber_output_push(BEROutputVisitor *qov, QEMUFile *qfile,
+ Error **errp)
+{
+ QStackEntry *e = g_malloc0(sizeof(*e));
+
+ e->qfile = qfile;
+ e->is_list_head = true;
+ QTAILQ_INSERT_HEAD(&qov->stack, e, node);
+}
+
+static QEMUFile *ber_output_pop(BEROutputVisitor *qov)
+{
+ QStackEntry *e = QTAILQ_FIRST(&qov->stack);
+ QEMUFile *qfile;
+
+ QTAILQ_REMOVE(&qov->stack, e, node);
+ qfile = e->qfile;
+ g_free(e);
+
+ return qfile;
+}
+
+static unsigned int ber_encode_type(uint8_t *buffer, uint32_t buflen,
+ enum ber_type_tag ber_type,
+ uint8_t ber_type_flags,
+ Error **errp)
+{
+ unsigned int idx = 0;
+
+ if (buflen < 1) {
+ error_set(errp, QERR_BUFFER_OVERRUN);
+ return 0;
+ }
+
+ if (ber_type > BER_TYPE_LONG_FORM) {
+ int byte = 4;
+ uint32_t mask = (0x7f << (7 * byte));
+ bool do_write = false;
+
+ buffer[0] = ber_type_flags | BER_TYPE_LONG_FORM;
+
+ while (byte >= 0) {
+ if (!do_write) {
+ if ((mask & ber_type)) {
+ do_write = true;
+ if (1 + byte + 1 > buflen) {
+ error_set(errp, QERR_BUFFER_OVERRUN);
+ return 0;
+ }
+ }
+ }
+ if (do_write) {
+ buffer[1 + idx] = (ber_type >> (7 * byte)) & 0x7f;
+ if (byte > 0) {
+ buffer[1 + idx] |= 0x80;
+ }
+ idx++;
+ }
+ byte--;
+ mask = (0x7f << (7 * byte));
+ }
+ } else {
+ buffer[0] = ber_type | ber_type_flags;
+ }
+ return 1 + idx;
+}
+
+static unsigned int ber_encode_len(uint8_t *buffer, uint32_t buflen,
+ uint64_t len, Error **errp)
+{
+ uint64_t mask = 0xFF00000000000000ULL;
+ int shift = 64 - 8;
+ int c = 0;
+
+ if (len <= 0x7f && buflen >= 1) {
+ buffer[0] = len;
+ return 1;
+ }
+
+ while (mask && (mask & len) == 0) {
+ mask >>= 8;
+ shift -= 8;
+ }
+
+ while (shift >= 0) {
+ if (1 + c + 1 > buflen) {
+ error_set(errp, QERR_BUFFER_OVERRUN);
+ return 0;
+ }
+ buffer[1+c] = (len >> shift);
+ c++;
+ shift -= 8;
+ }
+
+ buffer[0] = BER_LENGTH_LONG | c;
+
+ return 1 + c;
+}
+
+static void ber_output_start_constructed(Visitor *v, uint32_t ber_type,
+ Error **errp)
+{
+ BEROutputVisitor *aov = to_aov(v);
+ uint8_t buf[20];
+ unsigned int tag_bytes_written;
+
+ switch (aov->mode) {
+ case BER_TYPE_PRIMITIVE:
+ ber_output_push(aov, aov->qfile, errp);
+ if (error_is_set(errp)) {
+ return;
+ }
+ aov->qfile = qemu_bufopen("w", NULL);
+ break;
+ case BER_TYPE_CONSTRUCTED:
+ ber_output_push(aov, aov->qfile, errp); /* needed for list support */
+ if (error_is_set(errp)) {
+ return;
+ }
+ tag_bytes_written = ber_encode_type(buf, sizeof(buf),
+ ber_type, BER_TYPE_CONSTRUCTED,
+ errp);
+ if (error_is_set(errp)) {
+ return;
+ }
+ buf[tag_bytes_written] = BER_LENGTH_INDEFINITE;
+ if (qemu_write_bytes(aov->qfile, buf, 1 + tag_bytes_written) !=
+ 1 + tag_bytes_written) {
+ error_set(errp, QERR_QEMUFILE_ERROR,
+ "Error while writing constructed type");
+ return;
+ }
+ }
+}
+
+static void ber_output_constructed_ber_close(BEROutputVisitor *aov,
+ uint32_t ber_type,
+ Error **errp)
+{
+ uint8_t buf[20];
+ const QEMUSizedBuffer *qsb;
+ uint64_t len;
+ unsigned int num_bytes, tag_bytes_written;
+ QEMUFile *qfile = ber_output_pop(aov);
+
+ tag_bytes_written = ber_encode_type(buf, sizeof(buf),
+ ber_type, BER_TYPE_CONSTRUCTED,
+ errp);
+ if (error_is_set(errp)) {
+ return;
+ }
+
+ qsb = qemu_buf_get(aov->qfile);
+ len = qsb_get_length(qsb);
+#ifdef BER_DEBUG
+ fprintf(stderr, "constructed type (0x%02x, %p) has length %ld bytes\n",
+ ber_type, aov->qfile, len);
+#endif
+
+ num_bytes = ber_encode_len(&buf[tag_bytes_written],
+ sizeof(buf) - tag_bytes_written,
+ len, errp);
+ if (error_is_set(errp)) {
+ return;
+ }
+ if (qemu_write_bytes(qfile, buf, tag_bytes_written + num_bytes) !=
+ tag_bytes_written + num_bytes ||
+ qemu_write_bytes(qfile, qsb_get_buffer(qsb, 0),
+ qsb_get_length(qsb)) != qsb_get_length(qsb)) {
+ error_set(errp, QERR_QEMUFILE_ERROR,
+ "Error while writing buffer");
+ return;
+ }
+
+ qemu_fclose(aov->qfile);
+ aov->qfile = qfile;
+ qemu_fflush(qfile);
+}
+
+static void ber_output_end_constructed(Visitor *v, uint32_t ber_type,
+ Error **errp)
+{
+ BEROutputVisitor *aov = to_aov(v);
+ uint8_t buf[20];
+
+#ifdef BER_DEBUG
+ fprintf(stderr, "end set/struct:\n");
+#endif
+
+ switch (aov->mode) {
+ case BER_TYPE_PRIMITIVE:
+ ber_output_constructed_ber_close(aov, ber_type, errp);
+ break;
+
+ case BER_TYPE_CONSTRUCTED:
+ ber_output_pop(aov);
+ buf[0] = BER_TYPE_EOC;
+ buf[1] = 0;
+ if (qemu_write_bytes(aov->qfile, buf, 2) != 2) {
+ error_set(errp, QERR_QEMUFILE_ERROR,
+ "Error while writing buffer with BER_TYPE_EOC");
+ return;
+ }
+ break;
+ }
+}
+
+static void ber_output_start_struct(Visitor *v, void **obj, const char *kind,
+ const char *name, size_t unused,
+ Error **errp)
+{
+ ber_output_start_constructed(v, BER_TYPE_SEQUENCE, errp);
+}
+
+static void ber_output_end_struct(Visitor *v, Error **errp)
+{
+ ber_output_end_constructed(v, BER_TYPE_SEQUENCE, errp);
+}
+
+static void ber_output_start_array(Visitor *v, void **obj,
+ const char *name, size_t elem_count,
+ size_t elem_size, Error **errp)
+{
+ ber_output_start_constructed(v, BER_TYPE_SET, errp);
+}
+
+static void ber_output_next_array(Visitor *v, Error **errp)
+{
+ /* nothing to do here */
+}
+
+static void ber_output_end_array(Visitor *v, Error **errp)
+{
+ ber_output_end_constructed(v, BER_TYPE_SET, errp);
+}
+
+static void ber_output_start_list(Visitor *v, const char *name,
+ Error **errp)
+{
+ ber_output_start_constructed(v, BER_TYPE_CUSTOM_LIST, errp);
+}
+
+static GenericList *ber_output_next_list(Visitor *v, GenericList **listp,
+ Error **errp)
+{
+ GenericList *list = *listp;
+ BEROutputVisitor *bov = to_aov(v);
+ QStackEntry *e = QTAILQ_FIRST(&bov->stack);
+
+ assert(e);
+ if (e->is_list_head) {
+ e->is_list_head = false;
+ return list;
+ }
+
+ return list ? list->next : NULL;
+}
+
+static void ber_output_end_list(Visitor *v, Error **errp)
+{
+ ber_output_end_constructed(v, BER_TYPE_CUSTOM_LIST, errp);
+}
+
+static void ber_output_fragment(Visitor *v, uint32_t ber_type,
+ uint8_t *buffer,
+ size_t buflen, Error **errp)
+{
+ uint32_t offset = 0;
+ bool fragmented = false;
+ uint32_t chunk;
+ unsigned int num_bytes, type_bytes;
+ uint8_t buf[20];
+ uint32_t chunk_size;
+ BEROutputVisitor *aov = to_aov(v);
+
+ switch (aov->mode) {
+ case BER_TYPE_CONSTRUCTED:
+ /* X.690 9.2 */
+ fragmented = (buflen > CER_FRAGMENT_CHUNK_SIZE);
+ chunk_size = 1000;
+ break;
+ case BER_TYPE_PRIMITIVE:
+ chunk_size = 0xffffffff;
+ break;
+ }
+
+ if (fragmented) {
+ ber_output_start_constructed(&aov->visitor, ber_type, errp);
+ if (error_is_set(errp)) {
+ return;
+ }
+ }
+
+ do {
+ chunk = (buflen - offset > chunk_size) ? chunk_size : buflen - offset;
+
+ type_bytes = ber_encode_type(buf, sizeof(buf), ber_type, 0,
+ errp);
+ if (error_is_set(errp)) {
+ return;
+ }
+ num_bytes = ber_encode_len(&buf[type_bytes], sizeof(buf) - type_bytes,
+ chunk, errp);
+ if (error_is_set(errp)) {
+ return;
+ }
+ if (qemu_write_bytes(aov->qfile, buf, type_bytes + num_bytes) !=
+ type_bytes + num_bytes ||
+ qemu_write_bytes(aov->qfile, &buffer[offset], chunk) != chunk) {
+ error_set(errp, QERR_QEMUFILE_ERROR,
+ "Error while writing buffer");
+ return;
+ }
+ offset += chunk;
+ } while (offset < buflen);
+
+ if (fragmented) {
+ ber_output_end_constructed(&aov->visitor, ber_type, errp);
+ }
+}
+
+static void ber_output_int(Visitor *v, int64_t val, uint8_t maxnumbytes,
+ Error **errp)
+{
+ uint8_t buf[20];
+ int shift = (maxnumbytes - 1) * 8;
+ uint64_t mask = 0xFF80ULL << (shift - 8);
+ bool exp_zeros;
+ int c = 0;
+ BEROutputVisitor *aov = to_aov(v);
+
+#ifdef BER_DEBUG
+ fprintf(stderr, "Writing int 0x%lx (signed=%d, len=%d)\n",
+ val, is_signed, maxnumbytes);
+#endif
+
+ buf[0] = BER_TYPE_INTEGER;
+
+ if (maxnumbytes > 1) {
+ exp_zeros = ((mask & val) == 0) ? true : false;
+ while (mask != 0xFF) {
+ if (exp_zeros) {
+ if ((mask & val) != 0) {
+ break;
+ }
+ } else {
+ if ((mask & val) != mask) {
+ break;
+ }
+ }
+ shift -= 8;
+ mask >>= 8;
+ }
+ }
+
+ while (shift >= 0) {
+ buf[2+c] = (val >> shift);
+ c++;
+ shift -= 8;
+ }
+ buf[1] = c;
+
+ if (qemu_write_bytes(aov->qfile, buf, 1 + 1 + c) != 1 + 1 + c) {
+ error_set(errp, QERR_QEMUFILE_ERROR, "Error while writing integer");
+ return;
+ }
+}
+
+static void ber_output_type_int(Visitor *v, int64_t *obj, const char *name,
+ Error **errp)
+{
+ ber_output_int(v, *obj, sizeof(*obj), errp);
+}
+
+static void ber_output_type_uint8_t(Visitor *v, uint8_t *obj,
+ const char *name, Error **errp)
+{
+ ber_output_int(v, *obj, sizeof(*obj), errp);
+}
+
+static void ber_output_type_uint16_t(Visitor *v, uint16_t *obj,
+ const char *name, Error **errp)
+{
+ ber_output_int(v, *obj, sizeof(*obj), errp);
+}
+
+static void ber_output_type_uint32_t(Visitor *v, uint32_t *obj,
+ const char *name, Error **errp)
+{
+ ber_output_int(v, *obj, sizeof(*obj), errp);
+}
+
+static void ber_output_type_uint64_t(Visitor *v, uint64_t *obj,
+ const char *name, Error **errp)
+{
+ ber_output_int(v, *obj, sizeof(*obj), errp);
+}
+
+static void ber_output_type_int8_t(Visitor *v, int8_t *obj,
+ const char *name, Error **errp)
+{
+ ber_output_int(v, (int64_t)*obj, sizeof(*obj), errp);
+}
+
+static void ber_output_type_int16_t(Visitor *v, int16_t *obj,
+ const char *name, Error **errp)
+{
+ ber_output_int(v, (int64_t)*obj, sizeof(*obj), errp);
+}
+
+static void ber_output_type_int32_t(Visitor *v, int32_t *obj,
+ const char *name, Error **errp)
+{
+ ber_output_int(v, (int64_t)*obj, sizeof(*obj), errp);
+}
+
+static void ber_output_type_int64_t(Visitor *v, int64_t *obj,
+ const char *name, Error **errp)
+{
+ ber_output_int(v, (int64_t)*obj, sizeof(*obj), errp);
+}
+
+static void ber_output_type_bool(Visitor *v, bool *obj, const char *name,
+ Error **errp)
+{
+ BEROutputVisitor *aov = to_aov(v);
+ bool b = 0;
+
+ switch (aov->mode) {
+ case BER_TYPE_PRIMITIVE:
+ b = *obj;
+ break;
+ case BER_TYPE_CONSTRUCTED:
+ b = (*obj) ? 0xff : 0;
+ break;
+ }
+ ber_output_fragment(v, BER_TYPE_BOOLEAN, (uint8_t *)&b, sizeof(b), errp);
+}
+
+static void ber_output_type_str(Visitor *v, char **obj, const char *name,
+ Error **errp)
+{
+#ifdef BER_DEBUG
+ fprintf(stderr, "Writing string %s, len = 0x%02x\n", *obj,
+ (int)strlen(*obj));
+#endif
+ ber_output_fragment(v, BER_TYPE_IA5_STRING,
+ (uint8_t *)*obj,
+ *obj == NULL ? 0 : strlen(*obj), errp);
+}
+
+static void ber_output_sized_buffer(Visitor *v, uint8_t **obj,
+ size_t size, const char *name,
+ Error **errp)
+{
+ ber_output_fragment(v, BER_TYPE_OCTET_STRING,
+ *obj, size, errp);
+}
+
+void ber_output_visitor_cleanup(BEROutputVisitor *v)
+{
+ QStackEntry *e, *tmp;
+
+ QTAILQ_FOREACH_SAFE(e, &v->stack, node, tmp) {
+ QTAILQ_REMOVE(&v->stack, e, node);
+ if (e->qfile) {
+ qemu_fclose(e->qfile);
+ }
+ g_free(e);
+ }
+
+ g_free(v);
+}
+
+
+Visitor *ber_output_get_visitor(BEROutputVisitor *v)
+{
+ return &v->visitor;
+}
+
+BEROutputVisitor *ber_output_visitor_new(QEMUFile *qfile,
+ BERTypePC mode)
+{
+ BEROutputVisitor *v;
+
+ v = g_malloc0(sizeof(*v));
+
+ v->visitor.start_struct = ber_output_start_struct;
+ v->visitor.end_struct = ber_output_end_struct;
+ v->visitor.start_array = ber_output_start_array;
+ v->visitor.next_array = ber_output_next_array;
+ v->visitor.end_array = ber_output_end_array;
+ v->visitor.start_list = ber_output_start_list;
+ v->visitor.next_list = ber_output_next_list;
+ v->visitor.end_list = ber_output_end_list;
+ v->visitor.type_int = ber_output_type_int;
+ v->visitor.type_uint8_t = ber_output_type_uint8_t;
+ v->visitor.type_uint16_t = ber_output_type_uint16_t;
+ v->visitor.type_uint32_t = ber_output_type_uint32_t;
+ v->visitor.type_uint64_t = ber_output_type_uint64_t;
+ v->visitor.type_int8_t = ber_output_type_int8_t;
+ v->visitor.type_int16_t = ber_output_type_int16_t;
+ v->visitor.type_int32_t = ber_output_type_int32_t;
+ v->visitor.type_int64_t = ber_output_type_int64_t;
+ v->visitor.type_bool = ber_output_type_bool;
+ v->visitor.type_str = ber_output_type_str;
+ v->visitor.type_sized_buffer = ber_output_sized_buffer;
+
+ QTAILQ_INIT(&v->stack);
+ v->qfile = qfile;
+ v->mode = mode;
+
+ return v;
+}
Index: b/ber/ber-output-visitor.h
===================================================================
--- /dev/null
+++ b/ber/ber-output-visitor.h
@@ -0,0 +1,28 @@
+/*
+ * BER Output Visitor header
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Anthony Liguori <address@hidden>
+ * Stefan Berger <address@hidden>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#ifndef BER_OUTPUT_VISITOR_H
+#define BER_OUTPUT_VISITOR_H
+
+#include "include/qapi/visitor.h"
+#include "ber.h"
+
+typedef struct BEROutputVisitor BEROutputVisitor;
+
+BEROutputVisitor *ber_output_visitor_new(QEMUFile *, BERTypePC mode);
+void ber_output_visitor_cleanup(BEROutputVisitor *v);
+
+Visitor *ber_output_get_visitor(BEROutputVisitor *v);
+
+#endif
Index: b/ber/ber.h
===================================================================
--- /dev/null
+++ b/ber/ber.h
@@ -0,0 +1,85 @@
+#ifndef BER_BER_H
+#define BER_BER_H
+
+/* This is a subset of BER for QEMU use. */
+/* QEMU will use the DER encoding always with one extension from
+ * CER: SET and SEQUENCE types can have indefinite-length encoding
+ * if the encoding is not all immediately available.
+ *
+ * We assume that SET encodings can be available or not available,
+ * and that SEQUENCE encodings are available unless a SEQUENCE includes
+ * a non-available SET.
+ *
+ * The last is an extension to allow an arbitrarily large SET
+ * to be produced online without knowing the length in advance.
+ *
+ * All types used shall be universal, with explicit tagging, to simplify
+ * use by external tools.
+ */
+
+typedef enum ber_type_class {
+ BER_TYPE_CLASS_UNIVERSAL = 0x0 << 7,
+ BER_TYPE_CLASS_APPLICATION = 0x1 << 6,
+ BER_TYPE_CLASS_CONTENT_SPECIFIC = 0x2 << 6,
+ BER_TYPE_CLASS_PRIVATE = 0x3 << 6,
+ BER_TYPE_CLASS_MASK = 0x3 << 6 /* Mask to get class */
+} BERTypeClass;
+
+/* P/C bit */
+typedef enum ber_type_p_c {
+ BER_TYPE_PRIMITIVE = (0x0 << 5),
+ BER_TYPE_CONSTRUCTED = (0x1 << 5),
+ BER_TYPE_P_C_MASK = (0x1 << 5) /* Mask to get P/C bit */
+} BERTypePC;
+
+typedef enum ber_type_tag {
+ BER_TYPE_EOC /* P 0 0*/,
+ BER_TYPE_BOOLEAN /* P 1 1*/,
+ BER_TYPE_INTEGER /* P 2 2*/,
+ BER_TYPE_BIT_STRING /* P/C 3 3*/,
+ BER_TYPE_OCTET_STRING /* P/C 4 4*/,
+ BER_TYPE_NULL /* P 5 5*/,
+ BER_TYPE_OBJECT_ID /* P 6 6*/,
+ BER_TYPE_OBJECT_DESC /* P 7 7*/,
+ BER_TYPE_EXTERNAL /* C 8 8*/,
+ BER_TYPE_REAL /* P 9 9*/,
+ BER_TYPE_ENUMERATED /* P 10 A*/,
+ BER_TYPE_EMBEDDED /* C 11 B*/,
+ BER_TYPE_UTF8_STRING /* P/C 12 C*/,
+ BER_TYPE_RELATIVE_OID /* P 13 D*/,
+ BER_TYPE_UNUSED_0xE /* */,
+ BER_TYPE_UNUSED_0xF /* */,
+ BER_TYPE_SEQUENCE /* C 16 10*/,
+ BER_TYPE_SET /* C 17 11*/,
+ BER_TYPE_NUMERIC_STRING /* P/C 18 12*/,
+ BER_TYPE_PRINTABLE_STRING /* P/C 19 13*/,
+ BER_TYPE_T61STRING /* P/C 20 14*/,
+ BER_TYPE_VIDEOTEX_STRING /* P/C 21 15*/,
+ BER_TYPE_IA5_STRING /* P/C 22 16*/,
+ BER_TYPE_UTCTIME /* P/C 23 17*/,
+ BER_TYPE_GENERALIZED_TIME /* P/C 24 18*/,
+ BER_TYPE_GRAPHIC_STRING /* P/C 25 19*/,
+ BER_TYPE_VISIBLE_STRING /* P/C 26 1A*/,
+ BER_TYPE_GENERAL_STRING /* P/C 27 1B*/,
+ BER_TYPE_UNIVERSAL_STRING /* P/C 28 1C*/,
+ BER_TYPE_CHARACTER_STRING /* P/C 29 1D*/,
+ BER_TYPE_BMP_STRING /* P/C 30 1E*/,
+ BER_TYPE_LONG_FORM /* - 31 1F*/,
+ BER_TYPE_TAG_MASK = 0x1f /* Mask to get tag */,
+ BER_TYPE_CUSTOM_LIST = 0x20,
+} BERTypeTag;
+
+typedef enum ber_length {
+ /* Special length values */
+ BER_LENGTH_INDEFINITE = (0x1 << 7),
+ BER_LENGTH_RESERVED = 0xFF,
+ /* Anything else is either short or long */
+ BER_LENGTH_SHORT = (0x0 << 7),
+ BER_LENGTH_LONG = (0x1 << 7),
+ BER_LENGTH_SHORT_LONG_MASK = (0x1 << 7),
+ BER_LENGTH_MASK = 0x7F,
+} BERLength;
+
+const char *ber_type_to_str(uint8_t ber_type);
+
+#endif /* BER_BER_H */
Index: b/qapi/qapi-visit-core.c
===================================================================
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -67,6 +67,28 @@ void visit_end_list(Visitor *v, Error **
v->end_list(v, errp);
}
+void visit_start_array(Visitor *v, void **obj, const char *name,
+ size_t elem_count, size_t elem_size, Error **errp)
+{
+ if (!error_is_set(errp)) {
+ v->start_array(v, obj, name, elem_count, elem_size, errp);
+ }
+}
+
+void visit_next_array(Visitor *v, Error **errp)
+{
+ if (!error_is_set(errp)) {
+ v->next_array(v, errp);
+ }
+}
+
+void visit_end_array(Visitor *v, Error **errp)
+{
+ if (!error_is_set(errp)) {
+ v->end_array(v, errp);
+ }
+}
+
void visit_start_optional(Visitor *v, bool *present, const char *name,
Error **errp)
{
@@ -313,3 +335,11 @@ void input_type_enum(Visitor *v, int *ob
g_free(enum_str);
*obj = value;
}
+
+void visit_type_sized_buffer(Visitor *v, uint8_t **obj, size_t len,
+ const char *name, Error **errp)
+{
+ if (!error_is_set(errp)) {
+ v->type_sized_buffer(v, obj, len, name, errp);
+ }
+}
Index: b/include/qapi/visitor.h
===================================================================
--- a/include/qapi/visitor.h
+++ b/include/qapi/visitor.h
@@ -22,6 +22,10 @@ typedef struct GenericList
struct GenericList *next;
} GenericList;
+typedef struct GenericItem {
+ void *value;
+} GenericItem;
+
typedef struct Visitor Visitor;
void visit_start_handle(Visitor *v, void **obj, const char *kind,
@@ -33,6 +37,10 @@ void visit_end_struct(Visitor *v, Error
void visit_start_list(Visitor *v, const char *name, Error **errp);
GenericList *visit_next_list(Visitor *v, GenericList **list, Error **errp);
void visit_end_list(Visitor *v, Error **errp);
+void visit_start_array(Visitor *v, void **obj, const char *name,
+ size_t elem_count, size_t elem_size, Error **errp);
+void visit_next_array(Visitor *v, Error **errp);
+void visit_end_array(Visitor *v, Error **errp);
void visit_start_optional(Visitor *v, bool *present, const char *name,
Error **errp);
void visit_end_optional(Visitor *v, Error **errp);
@@ -51,5 +59,7 @@ void visit_type_size(Visitor *v, uint64_
void visit_type_bool(Visitor *v, bool *obj, const char *name, Error **errp);
void visit_type_str(Visitor *v, char **obj, const char *name, Error **errp);
void visit_type_number(Visitor *v, double *obj, const char *name, Error
**errp);
+void visit_type_sized_buffer(Visitor *v, uint8_t **obj, size_t len,
+ const char *name, Error **errp);
#endif
Index: b/include/qapi/visitor-impl.h
===================================================================
--- a/include/qapi/visitor-impl.h
+++ b/include/qapi/visitor-impl.h
@@ -26,14 +26,37 @@ struct Visitor
GenericList *(*next_list)(Visitor *v, GenericList **list, Error **errp);
void (*end_list)(Visitor *v, Error **errp);
+ void (*start_array)(Visitor *v, void **obj, const char *name,
+ size_t elem_count, size_t elem_size, Error **errp);
+ void (*next_array)(Visitor *v, Error **errp);
+ void (*end_array)(Visitor *v, Error **errp);
+
void (*type_enum)(Visitor *v, int *obj, const char *strings[],
const char *kind, const char *name, Error **errp);
void (*type_int)(Visitor *v, int64_t *obj, const char *name, Error **errp);
+ void (*type_uint8_t)(Visitor *v, uint8_t *obj, const char *name,
+ Error **errp);
+ void (*type_uint16_t)(Visitor *v, uint16_t *obj, const char *name,
+ Error **errp);
+ void (*type_uint32_t)(Visitor *v, uint32_t *obj, const char *name,
+ Error **errp);
+ void (*type_uint64_t)(Visitor *v, uint64_t *obj, const char *name,
+ Error **errp);
+ void (*type_int8_t)(Visitor *v, int8_t *obj, const char *name,
+ Error **errp);
+ void (*type_int16_t)(Visitor *v, int16_t *obj, const char *name,
+ Error **errp);
+ void (*type_int32_t)(Visitor *v, int32_t *obj, const char *name,
+ Error **errp);
+ void (*type_int64_t)(Visitor *v, int64_t *obj, const char *name,
+ Error **errp);
void (*type_bool)(Visitor *v, bool *obj, const char *name, Error **errp);
void (*type_str)(Visitor *v, char **obj, const char *name, Error **errp);
void (*type_number)(Visitor *v, double *obj, const char *name,
Error **errp);
+ void (*type_sized_buffer)(Visitor *v, uint8_t **obj, size_t size,
+ const char *name, Error **errp);
/* May be NULL */
void (*start_optional)(Visitor *v, bool *present, const char *name,
Index: b/include/qapi/qmp/qerror.h
===================================================================
--- a/include/qapi/qmp/qerror.h
+++ b/include/qapi/qmp/qerror.h
@@ -249,4 +249,10 @@ void assert_no_error(Error *err);
#define QERR_SOCKET_CREATE_FAILED \
ERROR_CLASS_GENERIC_ERROR, "Failed to create socket"
+#define QERR_INVALID_STREAM \
+ ERROR_CLASS_GENERIC_ERROR, "Data stream is invalid, error was '%s'"
+
+#define QERR_QEMUFILE_ERROR \
+ ERROR_CLASS_GENERIC_ERROR, "QEMUFile has an error, error was '%s'"
+
#endif /* QERROR_H */
Index: b/ber/Makefile.objs
===================================================================
--- /dev/null
+++ b/ber/Makefile.objs
@@ -0,0 +1,2 @@
+common-obj-y += ber-common.o ber-input-visitor.o ber-output-visitor.o
+