qemu-devel
[Top][All Lists]
Advanced

[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
+




reply via email to

[Prev in Thread] Current Thread [Next in Thread]