]> git.ipfire.org Git - thirdparty/lldpd.git/blob - src/marshal.c
120bfdcad2b494fc0da164cfe9ee034af103944c
[thirdparty/lldpd.git] / src / marshal.c
1 /* -*- mode: c; c-file-style: "openbsd" -*- */
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
18 #define MARSHAL_EXPORT
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"
31
32 /* Stolen from CCAN */
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
38
39 /* A serialized object */
40 struct marshal_serialized {
41 void *orig; /* Original reference. Also enforce alignment. */
42 size_t size;
43 unsigned char object[0];
44 };
45
46 struct marshal_info marshal_info_string = {
47 .name = "null string",
48 .size = 0,
49 .pointers = {MARSHAL_SUBINFO_NULL},
50 };
51 struct marshal_info marshal_info_fstring = {
52 .name = "fixed string",
53 .size = 0,
54 .pointers = {MARSHAL_SUBINFO_NULL},
55 };
56 struct marshal_info marshal_info_ignore = {
57 .name = "ignored",
58 .size = 0,
59 .pointers = {MARSHAL_SUBINFO_NULL},
60 };
61
62 /* List of already seen pointers */
63 struct ref {
64 TAILQ_ENTRY(ref) next;
65 void *pointer;
66 int dummy; /* To renumerate pointers */
67 };
68 TAILQ_HEAD(ref_l, ref);
69
70 /* Serialize the given object. */
71 ssize_t
72 marshal_serialize_(struct marshal_info *mi, void *unserialized, void **input,
73 int skip, void *_refs, int osize)
74 {
75 struct ref_l *refs = _refs;
76 struct ref *cref;
77 int size;
78 size_t len;
79 struct marshal_subinfo *current;
80 struct marshal_serialized *new = NULL, *serialized = NULL;
81 int dummy = 1;
82
83 log_debug("marshal", "start serialization of %s (%zu)", mi->name, mi->size);
84
85 /* Check if we have already serialized this one. */
86 if (!refs) {
87 refs = calloc(1, sizeof(struct ref_l));
88 if (!refs) {
89 log_warnx("marshal", "unable to allocate memory for list of references");
90 return -1;
91 }
92 TAILQ_INIT(refs);
93 }
94 TAILQ_FOREACH(cref, refs, next) {
95 if (unserialized == cref->pointer)
96 return 0;
97 /* dummy should be higher than any existing dummy */
98 if (cref->dummy >= dummy) dummy = cref->dummy + 1;
99 }
100
101 /* Handle special cases. */
102 size = mi->size;
103 if (!strcmp(mi->name, "null string"))
104 /* We know we can't be called with NULL */
105 size = strlen((char *)unserialized) + 1;
106 else if (!strcmp(mi->name, "fixed string"))
107 size = osize;
108
109 /* Allocate serialized structure */
110 len = sizeof(struct marshal_serialized) + (skip?0:size);
111 serialized = calloc(1, len);
112 if (!serialized) {
113 log_warnx("marshal", "unable to allocate memory to serialize structure %s",
114 mi->name);
115 len = -1;
116 goto marshal_error;
117 }
118 /* We don't use the original pointer but a dummy one. */
119 serialized->orig = (unsigned char*)NULL + dummy;
120
121 /* Append the new reference */
122 if (!(cref = calloc(1, sizeof(struct ref)))) {
123 log_warnx("marshal", "unable to allocate memory for list of references");
124 free(serialized);
125 len = -1;
126 goto marshal_error;
127 }
128 cref->pointer = unserialized;
129 cref->dummy = dummy;
130 TAILQ_INSERT_TAIL(refs, cref, next);
131
132 /* First, serialize the main structure */
133 if (!skip)
134 memcpy(serialized->object, unserialized, size);
135
136 /* Then, serialize inner structures */
137 for (current = mi->pointers; current->mi; current++) {
138 size_t sublen;
139 size_t padlen;
140 void *source;
141 void *target = NULL;
142 if (current->kind == ignore) {
143 memset((unsigned char *)serialized->object + current->offset, 0, current->offset2);
144 continue;
145 }
146 if (current->kind == pointer) {
147 memcpy(&source,
148 (unsigned char *)unserialized + current->offset,
149 sizeof(void *));
150 if (source == NULL) continue;
151 } else
152 source = (void *)((unsigned char *)unserialized + current->offset);
153 if (current->offset2)
154 memcpy(&osize, (unsigned char*)unserialized + current->offset2, sizeof(int));
155 target = NULL;
156 sublen = marshal_serialize_(current->mi,
157 source, &target,
158 current->kind == substruct, refs, osize);
159 if (sublen == -1) {
160 log_warnx("marshal", "unable to serialize substructure %s for %s",
161 current->mi->name, mi->name);
162 free(serialized);
163 return -1;
164 }
165 /* We want to put the renumerated pointer instead of the real one. */
166 if (current->kind == pointer && !skip) {
167 TAILQ_FOREACH(cref, refs, next) {
168 if (source == cref->pointer) {
169 void *fakepointer = (unsigned char*)NULL + cref->dummy;
170 memcpy((unsigned char *)serialized->object + current->offset,
171 &fakepointer, sizeof(void *));
172 break;
173 }
174 }
175 }
176 if (sublen == 0) continue; /* This was already serialized */
177 /* Append the result, force alignment to be able to unserialize it */
178 padlen = ALIGNOF(struct marshal_serialized);
179 padlen = (padlen - (len % padlen)) % padlen;
180 new = realloc(serialized, len + padlen + sublen);
181 if (!new) {
182 log_warnx("marshal", "unable to allocate more memory to serialize structure %s",
183 mi->name);
184 free(serialized);
185 free(target);
186 len = -1;
187 goto marshal_error;
188 }
189 memset((unsigned char *)new + len, 0, padlen);
190 memcpy((unsigned char *)new + len + padlen, target, sublen);
191 free(target);
192 len += sublen + padlen;
193 serialized = (struct marshal_serialized *)new;
194 }
195
196 serialized->size = len;
197 *input = serialized;
198 marshal_error:
199 if (refs && !_refs) {
200 struct ref *cref, *cref_next;
201 for (cref = TAILQ_FIRST(refs);
202 cref != NULL;
203 cref = cref_next) {
204 cref_next = TAILQ_NEXT(cref, next);
205 TAILQ_REMOVE(refs, cref, next);
206 free(cref);
207 }
208 free(refs);
209 }
210 return len;
211 }
212
213 /* This structure is used to track memory allocation when serializing */
214 struct gc {
215 TAILQ_ENTRY(gc) next;
216 void *pointer;
217 void *orig; /* Original reference (not valid anymore !) */
218 };
219 TAILQ_HEAD(gc_l, gc);
220
221 static void*
222 marshal_alloc(struct gc_l *pointers, size_t len, void *orig)
223 {
224 struct gc *gpointer = NULL;
225
226 void *result = calloc(1, len);
227 if (!result) return NULL;
228 if ((gpointer = (struct gc *)calloc(1,
229 sizeof(struct gc))) == NULL) {
230 free(result);
231 return NULL;
232 }
233 gpointer->pointer = result;
234 gpointer->orig = orig;
235 TAILQ_INSERT_TAIL(pointers, gpointer, next);
236 return result;
237 }
238 static void
239 marshal_free(struct gc_l *pointers, int gconly)
240 {
241 struct gc *pointer, *pointer_next;
242 for (pointer = TAILQ_FIRST(pointers);
243 pointer != NULL;
244 pointer = pointer_next) {
245 pointer_next = TAILQ_NEXT(pointer, next);
246 TAILQ_REMOVE(pointers, pointer, next);
247 if (!gconly)
248 free(pointer->pointer);
249 free(pointer);
250 }
251 }
252
253
254 /* Unserialize the given object. */
255 size_t
256 marshal_unserialize_(struct marshal_info *mi, void *buffer, size_t len, void **output,
257 void *_pointers, int skip, int osize)
258 {
259 int total_len = sizeof(struct marshal_serialized) + (skip?0:mi->size);
260 struct marshal_serialized *serialized = buffer;
261 struct gc_l *pointers = _pointers;
262 int size, already, extra = 0;
263 void *new;
264 struct marshal_subinfo *current;
265 struct gc *apointer;
266
267 log_debug("marshal", "start unserialization of %s", mi->name);
268
269 if (len < sizeof(struct marshal_serialized) || len < total_len) {
270 log_warnx("marshal", "data to deserialize is too small (%zu) for structure %s",
271 len, mi->name);
272 return 0;
273 }
274
275 /* Initialize garbage collection */
276 if (!pointers) {
277 pointers = calloc(1, sizeof(struct gc_l));
278 if (!pointers) {
279 log_warnx("marshal", "unable to allocate memory for garbage collection");
280 return 0;
281 }
282 TAILQ_INIT(pointers);
283 }
284
285 /* Special cases */
286 size = mi->size;
287 if (!strcmp(mi->name, "null string") || !strcmp(mi->name, "fixed string")) {
288 switch (mi->name[0]) {
289 case 'n': size = strnlen((char *)serialized->object,
290 len - sizeof(struct marshal_serialized)) + 1; break;
291 case 'f': size = osize; extra=1; break; /* The extra byte is to ensure that
292 the string is null terminated. */
293 }
294 if (size > len - sizeof(struct marshal_serialized)) {
295 log_warnx("marshal", "data to deserialize contains a string too long");
296 total_len = 0;
297 goto unmarshal_error;
298 }
299 total_len += size;
300 }
301
302 /* First, the main structure */
303 if (!skip) {
304 if ((*output = marshal_alloc(pointers, size + extra, serialized->orig)) == NULL) {
305 log_warnx("marshal", "unable to allocate memory to unserialize structure %s",
306 mi->name);
307 total_len = 0;
308 goto unmarshal_error;
309 }
310 memcpy(*output, serialized->object, size);
311 }
312
313 /* Then, each substructure */
314 for (current = mi->pointers; current->mi; current++) {
315 size_t sublen;
316 size_t padlen;
317 new = (unsigned char *)*output + current->offset;
318 if (current->kind == ignore) {
319 memset((unsigned char *)*output + current->offset,
320 0, current->offset2);
321 continue;
322 }
323 if (current->kind == pointer) {
324 if (*(void **)new == NULL) continue;
325
326 /* Did we already see this reference? */
327 already = 0;
328 TAILQ_FOREACH(apointer, pointers, next)
329 if (apointer->orig == *(void **)new) {
330 memcpy((unsigned char *)*output + current->offset,
331 &apointer->pointer, sizeof(void *));
332 already = 1;
333 break;
334 }
335 if (already) continue;
336 }
337 /* Deserialize */
338 if (current->offset2)
339 memcpy(&osize, (unsigned char *)*output + current->offset2, sizeof(int));
340 padlen = ALIGNOF(struct marshal_serialized);
341 padlen = (padlen - (total_len % padlen)) % padlen;
342 if (len < total_len + padlen || ((sublen = marshal_unserialize_(current->mi,
343 (unsigned char *)buffer + total_len + padlen,
344 len - total_len - padlen, &new, pointers,
345 current->kind == substruct, osize)) == 0)) {
346 log_warnx("marshal", "unable to serialize substructure %s for %s",
347 current->mi->name, mi->name);
348 total_len = 0;
349 goto unmarshal_error;
350 }
351 /* Link the result */
352 if (current->kind == pointer)
353 memcpy((unsigned char *)*output + current->offset,
354 &new, sizeof(void *));
355 total_len += sublen + padlen;
356 }
357
358 unmarshal_error:
359 if (pointers && !_pointers) {
360 marshal_free(pointers, (total_len > 0));
361 free(pointers);
362 }
363 return total_len;
364 }