summaryrefslogtreecommitdiffstats
path: root/src/marshal.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/marshal.c')
-rw-r--r--src/marshal.c380
1 files changed, 380 insertions, 0 deletions
diff --git a/src/marshal.c b/src/marshal.c
new file mode 100644
index 0000000..c6a25f7
--- /dev/null
+++ b/src/marshal.c
@@ -0,0 +1,380 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define MARSHAL_EXPORT
+#include "marshal.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <string.h>
+
+#include "compat/compat.h"
+#include "log.h"
+
+#include "lldpd-structs.h"
+
+/* Stolen from CCAN */
+#if HAVE_ALIGNOF
+# define ALIGNOF(t) (__alignof__(t))
+#else
+# define ALIGNOF(t) \
+ ((sizeof(t) > 1) ? \
+ ((char *)(&((struct { \
+ char c; \
+ t _h; \
+ } *)0) \
+ ->_h) - \
+ (char *)0) : \
+ 1)
+#endif
+
+/* A serialized object */
+struct marshal_serialized {
+ void *orig; /* Original reference. Also enforce alignment. */
+ size_t size;
+ unsigned char object[0];
+};
+
+struct marshal_info marshal_info_string = {
+ .name = "null string",
+ .size = 0,
+ .pointers = { MARSHAL_SUBINFO_NULL },
+};
+struct marshal_info marshal_info_fstring = {
+ .name = "fixed string",
+ .size = 0,
+ .pointers = { MARSHAL_SUBINFO_NULL },
+};
+struct marshal_info marshal_info_ignore = {
+ .name = "ignored",
+ .size = 0,
+ .pointers = { MARSHAL_SUBINFO_NULL },
+};
+
+/* List of already seen pointers */
+struct ref {
+ TAILQ_ENTRY(ref) next;
+ void *pointer;
+ uintptr_t dummy; /* To renumerate pointers */
+};
+TAILQ_HEAD(ref_l, ref);
+
+/* Serialize the given object. */
+ssize_t
+marshal_serialize_(struct marshal_info *mi, void *unserialized, void **input, int skip,
+ void *_refs, int osize)
+{
+ struct ref_l *refs = _refs;
+ struct ref *cref;
+ int size;
+ size_t len;
+ struct marshal_subinfo *current;
+ struct marshal_serialized *new = NULL, *serialized = NULL;
+ uintptr_t dummy = 1;
+
+ log_debug("marshal", "start serialization of %s", mi->name);
+
+ /* Check if we have already serialized this one. */
+ if (!refs) {
+ refs = calloc(1, sizeof(struct ref_l));
+ if (!refs) {
+ log_warnx("marshal",
+ "unable to allocate memory for list of references");
+ return -1;
+ }
+ TAILQ_INIT(refs);
+ }
+ TAILQ_FOREACH (cref, refs, next) {
+ if (unserialized == cref->pointer) return 0;
+ /* dummy should be higher than any existing dummy */
+ if (cref->dummy >= dummy) dummy = cref->dummy + 1;
+ }
+
+ /* Handle special cases. */
+ size = mi->size;
+ if (!strcmp(mi->name, "null string")) /* We know we can't be called with NULL */
+ size = strlen((char *)unserialized) + 1;
+ else if (!strcmp(mi->name, "fixed string"))
+ size = osize;
+
+ /* Allocate serialized structure */
+ len = sizeof(struct marshal_serialized) + (skip ? 0 : size);
+ serialized = calloc(1, len);
+ if (!serialized) {
+ log_warnx("marshal",
+ "unable to allocate memory to serialize structure %s", mi->name);
+ len = -1;
+ goto marshal_error;
+ }
+ /* We don't use the original pointer but a dummy one. */
+ serialized->orig = (unsigned char *)dummy;
+
+ /* Append the new reference */
+ if (!(cref = calloc(1, sizeof(struct ref)))) {
+ log_warnx("marshal",
+ "unable to allocate memory for list of references");
+ free(serialized);
+ len = -1;
+ goto marshal_error;
+ }
+ cref->pointer = unserialized;
+ cref->dummy = dummy;
+ TAILQ_INSERT_TAIL(refs, cref, next);
+
+ /* First, serialize the main structure */
+ if (!skip) memcpy(serialized->object, unserialized, size);
+
+ /* Then, serialize inner structures */
+ for (current = mi->pointers; current->mi; current++) {
+ size_t sublen;
+ size_t padlen;
+ void *source;
+ void *target = NULL;
+ if (current->kind == ignore) continue;
+ if (current->kind == pointer) {
+ memcpy(&source, (unsigned char *)unserialized + current->offset,
+ sizeof(void *));
+ if (source == NULL) continue;
+ } else
+ source =
+ (void *)((unsigned char *)unserialized + current->offset);
+ if (current->offset2)
+ memcpy(&osize, (unsigned char *)unserialized + current->offset2,
+ sizeof(int));
+ target = NULL;
+ sublen = marshal_serialize_(current->mi, source, &target,
+ current->kind == substruct, refs, osize);
+ if (sublen == -1) {
+ log_warnx("marshal",
+ "unable to serialize substructure %s for %s",
+ current->mi->name, mi->name);
+ free(serialized);
+ return -1;
+ }
+ /* We want to put the renumerated pointer instead of the real one. */
+ if (current->kind == pointer && !skip) {
+ TAILQ_FOREACH (cref, refs, next) {
+ if (source == cref->pointer) {
+ void *fakepointer =
+ (unsigned char *)cref->dummy;
+ memcpy((unsigned char *)serialized->object +
+ current->offset,
+ &fakepointer, sizeof(void *));
+ break;
+ }
+ }
+ }
+ if (sublen == 0) continue; /* This was already serialized */
+ /* Append the result, force alignment to be able to unserialize it */
+ padlen = ALIGNOF(struct marshal_serialized);
+ padlen = (padlen - (len % padlen)) % padlen;
+ new = realloc(serialized, len + padlen + sublen);
+ if (!new) {
+ log_warnx("marshal",
+ "unable to allocate more memory to serialize structure %s",
+ mi->name);
+ free(serialized);
+ free(target);
+ len = -1;
+ goto marshal_error;
+ }
+ memset((unsigned char *)new + len, 0, padlen);
+ memcpy((unsigned char *)new + len + padlen, target, sublen);
+ free(target);
+ len += sublen + padlen;
+ serialized = (struct marshal_serialized *)new;
+ }
+
+ serialized->size = len;
+ *input = serialized;
+marshal_error:
+ if (refs && !_refs) {
+ struct ref *cref, *cref_next;
+ for (cref = TAILQ_FIRST(refs); cref != NULL; cref = cref_next) {
+ cref_next = TAILQ_NEXT(cref, next);
+ TAILQ_REMOVE(refs, cref, next);
+ free(cref);
+ }
+ free(refs);
+ }
+ return len;
+}
+
+/* This structure is used to track memory allocation when serializing */
+struct gc {
+ TAILQ_ENTRY(gc) next;
+ void *pointer;
+ void *orig; /* Original reference (not valid anymore !) */
+};
+TAILQ_HEAD(gc_l, gc);
+
+static void *
+marshal_alloc(struct gc_l *pointers, size_t len, void *orig)
+{
+ struct gc *gpointer = NULL;
+
+ void *result = calloc(1, len);
+ if (!result) return NULL;
+ if ((gpointer = (struct gc *)calloc(1, sizeof(struct gc))) == NULL) {
+ free(result);
+ return NULL;
+ }
+ gpointer->pointer = result;
+ gpointer->orig = orig;
+ TAILQ_INSERT_TAIL(pointers, gpointer, next);
+ return result;
+}
+static void
+marshal_free(struct gc_l *pointers, int gconly)
+{
+ struct gc *pointer, *pointer_next;
+ for (pointer = TAILQ_FIRST(pointers); pointer != NULL; pointer = pointer_next) {
+ pointer_next = TAILQ_NEXT(pointer, next);
+ TAILQ_REMOVE(pointers, pointer, next);
+ if (!gconly) free(pointer->pointer);
+ free(pointer);
+ }
+}
+
+/* Unserialize the given object. */
+size_t
+marshal_unserialize_(struct marshal_info *mi, void *buffer, size_t len, void **output,
+ void *_pointers, int skip, int osize)
+{
+ int total_len = sizeof(struct marshal_serialized) + (skip ? 0 : mi->size);
+ struct marshal_serialized *serialized = buffer;
+ struct gc_l *pointers = _pointers;
+ int size, already, extra = 0;
+ void *new;
+ struct marshal_subinfo *current;
+ struct gc *apointer;
+
+ log_debug("marshal", "start unserialization of %s", mi->name);
+
+ if (len < sizeof(struct marshal_serialized) || len < total_len) {
+ log_warnx("marshal",
+ "data to deserialize is too small (%zu) for structure %s", len,
+ mi->name);
+ return 0;
+ }
+
+ /* Initialize garbage collection */
+ if (!pointers) {
+ pointers = calloc(1, sizeof(struct gc_l));
+ if (!pointers) {
+ log_warnx("marshal",
+ "unable to allocate memory for garbage collection");
+ return 0;
+ }
+ TAILQ_INIT(pointers);
+ }
+
+ /* Special cases */
+ size = mi->size;
+ if (!strcmp(mi->name, "null string") || !strcmp(mi->name, "fixed string")) {
+ switch (mi->name[0]) {
+ case 'n':
+ size = strnlen((char *)serialized->object,
+ len - sizeof(struct marshal_serialized)) +
+ 1;
+ break;
+ case 'f':
+ size = osize;
+ extra = 1;
+ break; /* The extra byte is to ensure that
+ the string is null terminated. */
+ }
+ if (size > len - sizeof(struct marshal_serialized)) {
+ log_warnx("marshal",
+ "data to deserialize contains a string too long");
+ total_len = 0;
+ goto unmarshal_error;
+ }
+ total_len += size;
+ }
+
+ /* First, the main structure */
+ if (!skip) {
+ if ((*output = marshal_alloc(pointers, size + extra,
+ serialized->orig)) == NULL) {
+ log_warnx("marshal",
+ "unable to allocate memory to unserialize structure %s",
+ mi->name);
+ total_len = 0;
+ goto unmarshal_error;
+ }
+ memcpy(*output, serialized->object, size);
+ }
+
+ /* Then, each substructure */
+ for (current = mi->pointers; current->mi; current++) {
+ size_t sublen;
+ size_t padlen;
+ new = (unsigned char *)*output + current->offset;
+ if (current->kind == ignore) {
+ memset((unsigned char *)*output + current->offset, 0,
+ sizeof(void *));
+ continue;
+ }
+ if (current->kind == pointer) {
+ if (*(void **)new == NULL) continue;
+
+ /* Did we already see this reference? */
+ already = 0;
+ TAILQ_FOREACH (apointer, pointers, next)
+ if (apointer->orig == *(void **)new) {
+ memcpy((unsigned char *)*output +
+ current->offset,
+ &apointer->pointer, sizeof(void *));
+ already = 1;
+ break;
+ }
+ if (already) continue;
+ }
+ /* Deserialize */
+ if (current->offset2)
+ memcpy(&osize, (unsigned char *)*output + current->offset2,
+ sizeof(int));
+ padlen = ALIGNOF(struct marshal_serialized);
+ padlen = (padlen - (total_len % padlen)) % padlen;
+ if (len < total_len + padlen ||
+ ((sublen = marshal_unserialize_(current->mi,
+ (unsigned char *)buffer + total_len + padlen,
+ len - total_len - padlen, &new, pointers,
+ current->kind == substruct, osize)) == 0)) {
+ log_warnx("marshal",
+ "unable to serialize substructure %s for %s",
+ current->mi->name, mi->name);
+ total_len = 0;
+ goto unmarshal_error;
+ }
+ /* Link the result */
+ if (current->kind == pointer)
+ memcpy((unsigned char *)*output + current->offset, &new,
+ sizeof(void *));
+ total_len += sublen + padlen;
+ }
+
+unmarshal_error:
+ if (pointers && !_pointers) {
+ marshal_free(pointers, (total_len > 0));
+ free(pointers);
+ }
+ return total_len;
+}