]> git.ipfire.org Git - thirdparty/lldpd.git/blame - src/marshal.c
WIP
[thirdparty/lldpd.git] / src / marshal.c
CommitLineData
4b292b55 1/* -*- mode: c; c-file-style: "openbsd" -*- */
db323555
VB
2/*
3 * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx>
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
6bf5e749 18#define MARSHAL_EXPORT
4b292b55
VB
19#include "marshal.h"
20
21#include <stdio.h>
22#include <stdlib.h>
23#include <sys/types.h>
24#include <sys/queue.h>
25#include <string.h>
26
27#include "compat/compat.h"
28#include "log.h"
29
30#include "lldpd-structs.h"
db323555 31
d8234294 32/* Stolen from CCAN */
dd1d33cf
VB
33#if HAVE_ALIGNOF
34# define ALIGNOF(t) (__alignof__(t))
35#else
36# define ALIGNOF(t) ((sizeof(t) > 1)?((char *)(&((struct { char c; t _h; } *)0)->_h) - (char *)0):1)
37#endif
d8234294 38
db323555
VB
39/* A serialized object */
40struct marshal_serialized {
41 void *orig; /* Original reference. Also enforce alignment. */
42 size_t size;
43 unsigned char object[0];
44};
45
6bf5e749 46struct marshal_info marshal_info_string = {
da781141
VB
47 .name = "null string",
48 .size = 0,
01d467de 49 .pointers = {MARSHAL_SUBINFO_NULL},
da781141 50};
6bf5e749 51struct marshal_info marshal_info_fstring = {
ca4ed9da
VB
52 .name = "fixed string",
53 .size = 0,
01d467de 54 .pointers = {MARSHAL_SUBINFO_NULL},
ca4ed9da 55};
6bf5e749 56struct marshal_info marshal_info_ignore = {
305e061c
VB
57 .name = "ignored",
58 .size = 0,
01d467de 59 .pointers = {MARSHAL_SUBINFO_NULL},
305e061c 60};
da781141 61
db323555
VB
62/* List of already seen pointers */
63struct ref {
64 TAILQ_ENTRY(ref) next;
65 void *pointer;
47862f89 66 int dummy; /* To renumerate pointers */
db323555
VB
67};
68TAILQ_HEAD(ref_l, ref);
69
70/* Serialize the given object. */
281a5cd4 71ssize_t
6bf5e749 72marshal_serialize_(struct marshal_info *mi, void *unserialized, void **input,
ca4ed9da 73 int skip, void *_refs, int osize)
4b292b55 74{
db323555 75 struct ref_l *refs = _refs;
5fd6695c
VB
76 struct ref *cref;
77 int size;
78 size_t len;
79 struct marshal_subinfo *current;
80 struct marshal_serialized *new = NULL, *serialized = NULL;
47862f89 81 int dummy = 1;
5fd6695c 82
4b571a60 83 log_debug("marshal", "start serialization of %s (%zu)", mi->name, mi->size);
6f8925be 84
5fd6695c 85 /* Check if we have already serialized this one. */
db323555
VB
86 if (!refs) {
87 refs = calloc(1, sizeof(struct ref_l));
88 if (!refs) {
6f8925be 89 log_warnx("marshal", "unable to allocate memory for list of references");
db323555
VB
90 return -1;
91 }
92 TAILQ_INIT(refs);
93 }
db323555
VB
94 TAILQ_FOREACH(cref, refs, next) {
95 if (unserialized == cref->pointer)
96 return 0;
47862f89
VB
97 /* dummy should be higher than any existing dummy */
98 if (cref->dummy >= dummy) dummy = cref->dummy + 1;
db323555
VB
99 }
100
da781141 101 /* Handle special cases. */
5fd6695c 102 size = mi->size;
da781141 103 if (!strcmp(mi->name, "null string"))
12313820 104 /* We know we can't be called with NULL */
da781141 105 size = strlen((char *)unserialized) + 1;
ca4ed9da
VB
106 else if (!strcmp(mi->name, "fixed string"))
107 size = osize;
da781141
VB
108
109 /* Allocate serialized structure */
5fd6695c
VB
110 len = sizeof(struct marshal_serialized) + (skip?0:size);
111 serialized = calloc(1, len);
db323555 112 if (!serialized) {
6f8925be 113 log_warnx("marshal", "unable to allocate memory to serialize structure %s",
db323555
VB
114 mi->name);
115 len = -1;
116 goto marshal_error;
117 }
47862f89
VB
118 /* We don't use the original pointer but a dummy one. */
119 serialized->orig = (unsigned char*)NULL + dummy;
db323555
VB
120
121 /* Append the new reference */
122 if (!(cref = calloc(1, sizeof(struct ref)))) {
6f8925be 123 log_warnx("marshal", "unable to allocate memory for list of references");
db323555
VB
124 free(serialized);
125 len = -1;
126 goto marshal_error;
127 }
128 cref->pointer = unserialized;
47862f89 129 cref->dummy = dummy;
db323555
VB
130 TAILQ_INSERT_TAIL(refs, cref, next);
131
132 /* First, serialize the main structure */
133 if (!skip)
da781141 134 memcpy(serialized->object, unserialized, size);
db323555
VB
135
136 /* Then, serialize inner structures */
db323555
VB
137 for (current = mi->pointers; current->mi; current++) {
138 size_t sublen;
d8234294 139 size_t padlen;
db323555 140 void *source;
aa015c26 141 void *target = NULL;
4b571a60 142 if (current->kind == ignore) {
17d9ea80 143 log_debug("marshal", "ignore from %zu (%zu)", current->offset, current->offset2);
4b571a60
VB
144 memset((unsigned char *)serialized->object + current->offset, 0, current->offset2);
145 continue;
146 }
db323555 147 if (current->kind == pointer) {
6578dc6a
VB
148 memcpy(&source,
149 (unsigned char *)unserialized + current->offset,
150 sizeof(void *));
db323555
VB
151 if (source == NULL) continue;
152 } else
153 source = (void *)((unsigned char *)unserialized + current->offset);
6bf5e749
VB
154 if (current->offset2)
155 memcpy(&osize, (unsigned char*)unserialized + current->offset2, sizeof(int));
3abff5ce 156 target = NULL;
6bf5e749 157 sublen = marshal_serialize_(current->mi,
db323555 158 source, &target,
ca4ed9da 159 current->kind == substruct, refs, osize);
3abff5ce 160 if (sublen == -1) {
6f8925be 161 log_warnx("marshal", "unable to serialize substructure %s for %s",
db323555
VB
162 current->mi->name, mi->name);
163 free(serialized);
164 return -1;
165 }
47862f89
VB
166 /* We want to put the renumerated pointer instead of the real one. */
167 if (current->kind == pointer && !skip) {
168 TAILQ_FOREACH(cref, refs, next) {
169 if (source == cref->pointer) {
170 void *fakepointer = (unsigned char*)NULL + cref->dummy;
171 memcpy((unsigned char *)serialized->object + current->offset,
172 &fakepointer, sizeof(void *));
173 break;
174 }
175 }
176 }
db323555 177 if (sublen == 0) continue; /* This was already serialized */
d8234294
VB
178 /* Append the result, force alignment to be able to unserialize it */
179 padlen = ALIGNOF(struct marshal_serialized);
180 padlen = (padlen - (len % padlen)) % padlen;
17d9ea80 181 log_debug("marshal", "realloc %lu", len + padlen + sublen);
d8234294 182 new = realloc(serialized, len + padlen + sublen);
db323555 183 if (!new) {
6f8925be 184 log_warnx("marshal", "unable to allocate more memory to serialize structure %s",
db323555
VB
185 mi->name);
186 free(serialized);
187 free(target);
188 len = -1;
189 goto marshal_error;
190 }
d8234294
VB
191 memset((unsigned char *)new + len, 0, padlen);
192 memcpy((unsigned char *)new + len + padlen, target, sublen);
db323555 193 free(target);
d8234294 194 len += sublen + padlen;
db323555
VB
195 serialized = (struct marshal_serialized *)new;
196 }
197
198 serialized->size = len;
199 *input = serialized;
200marshal_error:
201 if (refs && !_refs) {
202 struct ref *cref, *cref_next;
203 for (cref = TAILQ_FIRST(refs);
204 cref != NULL;
205 cref = cref_next) {
206 cref_next = TAILQ_NEXT(cref, next);
207 TAILQ_REMOVE(refs, cref, next);
208 free(cref);
209 }
210 free(refs);
211 }
212 return len;
213}
214
215/* This structure is used to track memory allocation when serializing */
216struct gc {
217 TAILQ_ENTRY(gc) next;
218 void *pointer;
219 void *orig; /* Original reference (not valid anymore !) */
220};
221TAILQ_HEAD(gc_l, gc);
222
223static void*
224marshal_alloc(struct gc_l *pointers, size_t len, void *orig)
225{
5fd6695c
VB
226 struct gc *gpointer = NULL;
227
4b292b55 228 void *result = calloc(1, len);
db323555 229 if (!result) return NULL;
db323555
VB
230 if ((gpointer = (struct gc *)calloc(1,
231 sizeof(struct gc))) == NULL) {
232 free(result);
233 return NULL;
234 }
235 gpointer->pointer = result;
236 gpointer->orig = orig;
237 TAILQ_INSERT_TAIL(pointers, gpointer, next);
238 return result;
239}
240static void
241marshal_free(struct gc_l *pointers, int gconly)
242{
243 struct gc *pointer, *pointer_next;
244 for (pointer = TAILQ_FIRST(pointers);
245 pointer != NULL;
246 pointer = pointer_next) {
247 pointer_next = TAILQ_NEXT(pointer, next);
248 TAILQ_REMOVE(pointers, pointer, next);
249 if (!gconly)
250 free(pointer->pointer);
251 free(pointer);
252 }
253}
254
255
256/* Unserialize the given object. */
257size_t
6bf5e749 258marshal_unserialize_(struct marshal_info *mi, void *buffer, size_t len, void **output,
ca4ed9da 259 void *_pointers, int skip, int osize)
db323555
VB
260{
261 int total_len = sizeof(struct marshal_serialized) + (skip?0:mi->size);
262 struct marshal_serialized *serialized = buffer;
5fd6695c 263 struct gc_l *pointers = _pointers;
4b292b55 264 int size, already, extra = 0;
5fd6695c
VB
265 void *new;
266 struct marshal_subinfo *current;
267 struct gc *apointer;
268
6f8925be
VB
269 log_debug("marshal", "start unserialization of %s", mi->name);
270
db323555 271 if (len < sizeof(struct marshal_serialized) || len < total_len) {
6f8925be 272 log_warnx("marshal", "data to deserialize is too small (%zu) for structure %s",
4b292b55 273 len, mi->name);
db323555
VB
274 return 0;
275 }
276
277 /* Initialize garbage collection */
db323555
VB
278 if (!pointers) {
279 pointers = calloc(1, sizeof(struct gc_l));
280 if (!pointers) {
6f8925be 281 log_warnx("marshal", "unable to allocate memory for garbage collection");
db323555
VB
282 return 0;
283 }
284 TAILQ_INIT(pointers);
285 }
286
da781141 287 /* Special cases */
5fd6695c 288 size = mi->size;
ca4ed9da
VB
289 if (!strcmp(mi->name, "null string") || !strcmp(mi->name, "fixed string")) {
290 switch (mi->name[0]) {
291 case 'n': size = strnlen((char *)serialized->object,
292 len - sizeof(struct marshal_serialized)) + 1; break;
4b292b55
VB
293 case 'f': size = osize; extra=1; break; /* The extra byte is to ensure that
294 the string is null terminated. */
ca4ed9da
VB
295 }
296 if (size > len - sizeof(struct marshal_serialized)) {
6f8925be 297 log_warnx("marshal", "data to deserialize contains a string too long");
da781141
VB
298 total_len = 0;
299 goto unmarshal_error;
300 }
301 total_len += size;
302 }
303
db323555
VB
304 /* First, the main structure */
305 if (!skip) {
4b292b55 306 if ((*output = marshal_alloc(pointers, size + extra, serialized->orig)) == NULL) {
6f8925be 307 log_warnx("marshal", "unable to allocate memory to unserialize structure %s",
db323555
VB
308 mi->name);
309 total_len = 0;
310 goto unmarshal_error;
311 }
da781141 312 memcpy(*output, serialized->object, size);
db323555
VB
313 }
314
315 /* Then, each substructure */
db323555
VB
316 for (current = mi->pointers; current->mi; current++) {
317 size_t sublen;
d8234294 318 size_t padlen;
5fd6695c 319 new = (unsigned char *)*output + current->offset;
305e061c 320 if (current->kind == ignore) {
6578dc6a 321 memset((unsigned char *)*output + current->offset,
4b571a60 322 0, current->offset2);
305e061c
VB
323 continue;
324 }
db323555
VB
325 if (current->kind == pointer) {
326 if (*(void **)new == NULL) continue;
327
328 /* Did we already see this reference? */
5fd6695c
VB
329 already = 0;
330 TAILQ_FOREACH(apointer, pointers, next)
331 if (apointer->orig == *(void **)new) {
6578dc6a 332 memcpy((unsigned char *)*output + current->offset,
5fd6695c 333 &apointer->pointer, sizeof(void *));
db323555
VB
334 already = 1;
335 break;
336 }
337 if (already) continue;
338 }
339 /* Deserialize */
6bf5e749
VB
340 if (current->offset2)
341 memcpy(&osize, (unsigned char *)*output + current->offset2, sizeof(int));
d8234294
VB
342 padlen = ALIGNOF(struct marshal_serialized);
343 padlen = (padlen - (total_len % padlen)) % padlen;
344 if (len < total_len + padlen || ((sublen = marshal_unserialize_(current->mi,
345 (unsigned char *)buffer + total_len + padlen,
346 len - total_len - padlen, &new, pointers,
347 current->kind == substruct, osize)) == 0)) {
6f8925be 348 log_warnx("marshal", "unable to serialize substructure %s for %s",
db323555
VB
349 current->mi->name, mi->name);
350 total_len = 0;
351 goto unmarshal_error;
352 }
353 /* Link the result */
354 if (current->kind == pointer)
6578dc6a
VB
355 memcpy((unsigned char *)*output + current->offset,
356 &new, sizeof(void *));
d8234294 357 total_len += sublen + padlen;
db323555
VB
358 }
359
360unmarshal_error:
361 if (pointers && !_pointers) {
362 marshal_free(pointers, (total_len > 0));
363 free(pointers);
364 }
365 return total_len;
366}