1 /* -*- mode: c; c-file-style: "openbsd" -*- */
3 * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx>
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.
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.
18 #define MARSHAL_EXPORT
23 #include <sys/types.h>
24 #include <sys/queue.h>
27 #include "compat/compat.h"
30 #include "lldpd-structs.h"
32 /* Stolen from CCAN */
34 # define ALIGNOF(t) (__alignof__(t))
36 # define ALIGNOF(t) ((sizeof(t) > 1)?((char *)(&((struct { char c; t _h; } *)0)->_h) - (char *)0):1)
39 /* A serialized object */
40 struct marshal_serialized
{
41 void *orig
; /* Original reference. Also enforce alignment. */
43 unsigned char object
[0];
46 struct marshal_info marshal_info_string
= {
47 .name
= "null string",
49 .pointers
= {MARSHAL_SUBINFO_NULL
},
51 struct marshal_info marshal_info_fstring
= {
52 .name
= "fixed string",
54 .pointers
= {MARSHAL_SUBINFO_NULL
},
56 struct marshal_info marshal_info_ignore
= {
59 .pointers
= {MARSHAL_SUBINFO_NULL
},
62 /* List of already seen pointers */
64 TAILQ_ENTRY(ref
) next
;
66 int dummy
; /* To renumerate pointers */
68 TAILQ_HEAD(ref_l
, ref
);
70 /* Serialize the given object. */
72 marshal_serialize_(struct marshal_info
*mi
, void *unserialized
, void **input
,
73 int skip
, void *_refs
, int osize
)
75 struct ref_l
*refs
= _refs
;
79 struct marshal_subinfo
*current
;
80 struct marshal_serialized
*new = NULL
, *serialized
= NULL
;
83 log_debug("marshal", "start serialization of %s (%zu)", mi
->name
, mi
->size
);
85 /* Check if we have already serialized this one. */
87 refs
= calloc(1, sizeof(struct ref_l
));
89 log_warnx("marshal", "unable to allocate memory for list of references");
94 TAILQ_FOREACH(cref
, refs
, next
) {
95 if (unserialized
== cref
->pointer
)
97 /* dummy should be higher than any existing dummy */
98 if (cref
->dummy
>= dummy
) dummy
= cref
->dummy
+ 1;
101 /* Handle special cases. */
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"))
109 /* Allocate serialized structure */
110 len
= sizeof(struct marshal_serialized
) + (skip
?0:size
);
111 serialized
= calloc(1, len
);
113 log_warnx("marshal", "unable to allocate memory to serialize structure %s",
118 /* We don't use the original pointer but a dummy one. */
119 serialized
->orig
= (unsigned char*)NULL
+ dummy
;
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");
128 cref
->pointer
= unserialized
;
130 TAILQ_INSERT_TAIL(refs
, cref
, next
);
132 /* First, serialize the main structure */
134 memcpy(serialized
->object
, unserialized
, size
);
136 /* Then, serialize inner structures */
137 for (current
= mi
->pointers
; current
->mi
; current
++) {
142 if (current
->kind
== ignore
) {
143 memset((unsigned char *)serialized
->object
+ current
->offset
, 0, current
->offset2
);
146 if (current
->kind
== pointer
) {
148 (unsigned char *)unserialized
+ current
->offset
,
150 if (source
== NULL
) continue;
152 source
= (void *)((unsigned char *)unserialized
+ current
->offset
);
153 if (current
->offset2
)
154 memcpy(&osize
, (unsigned char*)unserialized
+ current
->offset2
, sizeof(int));
156 sublen
= marshal_serialize_(current
->mi
,
158 current
->kind
== substruct
, refs
, osize
);
160 log_warnx("marshal", "unable to serialize substructure %s for %s",
161 current
->mi
->name
, mi
->name
);
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 *));
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
);
182 log_warnx("marshal", "unable to allocate more memory to serialize structure %s",
189 memset((unsigned char *)new + len
, 0, padlen
);
190 memcpy((unsigned char *)new + len
+ padlen
, target
, sublen
);
192 len
+= sublen
+ padlen
;
193 serialized
= (struct marshal_serialized
*)new;
196 serialized
->size
= len
;
199 if (refs
&& !_refs
) {
200 struct ref
*cref
, *cref_next
;
201 for (cref
= TAILQ_FIRST(refs
);
204 cref_next
= TAILQ_NEXT(cref
, next
);
205 TAILQ_REMOVE(refs
, cref
, next
);
213 /* This structure is used to track memory allocation when serializing */
215 TAILQ_ENTRY(gc
) next
;
217 void *orig
; /* Original reference (not valid anymore !) */
219 TAILQ_HEAD(gc_l
, gc
);
222 marshal_alloc(struct gc_l
*pointers
, size_t len
, void *orig
)
224 struct gc
*gpointer
= NULL
;
226 void *result
= calloc(1, len
);
227 if (!result
) return NULL
;
228 if ((gpointer
= (struct gc
*)calloc(1,
229 sizeof(struct gc
))) == NULL
) {
233 gpointer
->pointer
= result
;
234 gpointer
->orig
= orig
;
235 TAILQ_INSERT_TAIL(pointers
, gpointer
, next
);
239 marshal_free(struct gc_l
*pointers
, int gconly
)
241 struct gc
*pointer
, *pointer_next
;
242 for (pointer
= TAILQ_FIRST(pointers
);
244 pointer
= pointer_next
) {
245 pointer_next
= TAILQ_NEXT(pointer
, next
);
246 TAILQ_REMOVE(pointers
, pointer
, next
);
248 free(pointer
->pointer
);
254 /* Unserialize the given object. */
256 marshal_unserialize_(struct marshal_info
*mi
, void *buffer
, size_t len
, void **output
,
257 void *_pointers
, int skip
, int osize
)
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;
264 struct marshal_subinfo
*current
;
267 log_debug("marshal", "start unserialization of %s", mi
->name
);
269 if (len
< sizeof(struct marshal_serialized
) || len
< total_len
) {
270 log_warnx("marshal", "data to deserialize is too small (%zu) for structure %s",
275 /* Initialize garbage collection */
277 pointers
= calloc(1, sizeof(struct gc_l
));
279 log_warnx("marshal", "unable to allocate memory for garbage collection");
282 TAILQ_INIT(pointers
);
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. */
294 if (size
> len
- sizeof(struct marshal_serialized
)) {
295 log_warnx("marshal", "data to deserialize contains a string too long");
297 goto unmarshal_error
;
302 /* First, the main structure */
304 if ((*output
= marshal_alloc(pointers
, size
+ extra
, serialized
->orig
)) == NULL
) {
305 log_warnx("marshal", "unable to allocate memory to unserialize structure %s",
308 goto unmarshal_error
;
310 memcpy(*output
, serialized
->object
, size
);
313 /* Then, each substructure */
314 for (current
= mi
->pointers
; current
->mi
; current
++) {
317 new = (unsigned char *)*output
+ current
->offset
;
318 if (current
->kind
== ignore
) {
319 memset((unsigned char *)*output
+ current
->offset
,
320 0, current
->offset2
);
323 if (current
->kind
== pointer
) {
324 if (*(void **)new == NULL
) continue;
326 /* Did we already see this reference? */
328 TAILQ_FOREACH(apointer
, pointers
, next
)
329 if (apointer
->orig
== *(void **)new) {
330 memcpy((unsigned char *)*output
+ current
->offset
,
331 &apointer
->pointer
, sizeof(void *));
335 if (already
) continue;
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
);
349 goto unmarshal_error
;
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
;
359 if (pointers
&& !_pointers
) {
360 marshal_free(pointers
, (total_len
> 0));