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))
38 ((char *)(&((struct { \
47 /* A serialized object */
48 struct marshal_serialized
{
49 void *orig
; /* Original reference. Also enforce alignment. */
51 unsigned char object
[0];
54 struct marshal_info marshal_info_string
= {
55 .name
= "null string",
57 .pointers
= { MARSHAL_SUBINFO_NULL
},
59 struct marshal_info marshal_info_fstring
= {
60 .name
= "fixed string",
62 .pointers
= { MARSHAL_SUBINFO_NULL
},
64 struct marshal_info marshal_info_ignore
= {
67 .pointers
= { MARSHAL_SUBINFO_NULL
},
70 /* List of already seen pointers */
72 TAILQ_ENTRY(ref
) next
;
74 uintptr_t dummy
; /* To renumerate pointers */
76 TAILQ_HEAD(ref_l
, ref
);
78 /* Serialize the given object. */
80 marshal_serialize_(struct marshal_info
*mi
, void *unserialized
, void **input
, int skip
,
81 void *_refs
, int osize
)
83 struct ref_l
*refs
= _refs
;
87 struct marshal_subinfo
*current
;
88 struct marshal_serialized
*new = NULL
, *serialized
= NULL
;
91 log_debug("marshal", "start serialization of %s", mi
->name
);
93 /* Check if we have already serialized this one. */
95 refs
= calloc(1, sizeof(struct ref_l
));
98 "unable to allocate memory for list of references");
103 TAILQ_FOREACH (cref
, refs
, next
) {
104 if (unserialized
== cref
->pointer
) return 0;
105 /* dummy should be higher than any existing dummy */
106 if (cref
->dummy
>= dummy
) dummy
= cref
->dummy
+ 1;
109 /* Handle special cases. */
111 if (!strcmp(mi
->name
, "null string")) /* We know we can't be called with NULL */
112 size
= strlen((char *)unserialized
) + 1;
113 else if (!strcmp(mi
->name
, "fixed string"))
116 /* Allocate serialized structure */
117 len
= sizeof(struct marshal_serialized
) + (skip
? 0 : size
);
118 serialized
= calloc(1, len
);
121 "unable to allocate memory to serialize structure %s", mi
->name
);
125 /* We don't use the original pointer but a dummy one. */
126 serialized
->orig
= (unsigned char *)dummy
;
128 /* Append the new reference */
129 if (!(cref
= calloc(1, sizeof(struct ref
)))) {
131 "unable to allocate memory for list of references");
136 cref
->pointer
= unserialized
;
138 TAILQ_INSERT_TAIL(refs
, cref
, next
);
140 /* First, serialize the main structure */
141 if (!skip
) memcpy(serialized
->object
, unserialized
, size
);
143 /* Then, serialize inner structures */
144 for (current
= mi
->pointers
; current
->mi
; current
++) {
149 if (current
->kind
== ignore
) continue;
150 if (current
->kind
== pointer
) {
151 memcpy(&source
, (unsigned char *)unserialized
+ current
->offset
,
153 if (source
== NULL
) continue;
156 (void *)((unsigned char *)unserialized
+ current
->offset
);
157 if (current
->offset2
)
158 memcpy(&osize
, (unsigned char *)unserialized
+ current
->offset2
,
161 sublen
= marshal_serialize_(current
->mi
, source
, &target
,
162 current
->kind
== substruct
, refs
, osize
);
165 "unable to serialize substructure %s for %s",
166 current
->mi
->name
, mi
->name
);
170 /* We want to put the renumerated pointer instead of the real one. */
171 if (current
->kind
== pointer
&& !skip
) {
172 TAILQ_FOREACH (cref
, refs
, next
) {
173 if (source
== cref
->pointer
) {
175 (unsigned char *)cref
->dummy
;
176 memcpy((unsigned char *)serialized
->object
+
178 &fakepointer
, sizeof(void *));
183 if (sublen
== 0) continue; /* This was already serialized */
184 /* Append the result, force alignment to be able to unserialize it */
185 padlen
= ALIGNOF(struct marshal_serialized
);
186 padlen
= (padlen
- (len
% padlen
)) % padlen
;
187 new = realloc(serialized
, len
+ padlen
+ sublen
);
190 "unable to allocate more memory to serialize structure %s",
197 memset((unsigned char *)new + len
, 0, padlen
);
198 memcpy((unsigned char *)new + len
+ padlen
, target
, sublen
);
200 len
+= sublen
+ padlen
;
201 serialized
= (struct marshal_serialized
*)new;
204 serialized
->size
= len
;
207 if (refs
&& !_refs
) {
208 struct ref
*cref
, *cref_next
;
209 for (cref
= TAILQ_FIRST(refs
); cref
!= NULL
; cref
= cref_next
) {
210 cref_next
= TAILQ_NEXT(cref
, next
);
211 TAILQ_REMOVE(refs
, cref
, next
);
219 /* This structure is used to track memory allocation when serializing */
221 TAILQ_ENTRY(gc
) next
;
223 void *orig
; /* Original reference (not valid anymore !) */
225 TAILQ_HEAD(gc_l
, gc
);
228 marshal_alloc(struct gc_l
*pointers
, size_t len
, void *orig
)
230 struct gc
*gpointer
= NULL
;
232 void *result
= calloc(1, len
);
233 if (!result
) return NULL
;
234 if ((gpointer
= (struct gc
*)calloc(1, sizeof(struct gc
))) == NULL
) {
238 gpointer
->pointer
= result
;
239 gpointer
->orig
= orig
;
240 TAILQ_INSERT_TAIL(pointers
, gpointer
, next
);
244 marshal_free(struct gc_l
*pointers
, int gconly
)
246 struct gc
*pointer
, *pointer_next
;
247 for (pointer
= TAILQ_FIRST(pointers
); pointer
!= NULL
; pointer
= pointer_next
) {
248 pointer_next
= TAILQ_NEXT(pointer
, next
);
249 TAILQ_REMOVE(pointers
, pointer
, next
);
250 if (!gconly
) free(pointer
->pointer
);
255 /* Unserialize the given object. */
257 marshal_unserialize_(struct marshal_info
*mi
, void *buffer
, size_t len
, void **output
,
258 void *_pointers
, int skip
, int osize
)
260 int total_len
= sizeof(struct marshal_serialized
) + (skip
? 0 : mi
->size
);
261 struct marshal_serialized
*serialized
= buffer
;
262 struct gc_l
*pointers
= _pointers
;
263 int size
, already
, extra
= 0;
265 struct marshal_subinfo
*current
;
268 log_debug("marshal", "start unserialization of %s", mi
->name
);
270 if (len
< sizeof(struct marshal_serialized
) || len
< total_len
) {
272 "data to deserialize is too small (%zu) for structure %s", len
,
277 /* Initialize garbage collection */
279 pointers
= calloc(1, sizeof(struct gc_l
));
282 "unable to allocate memory for garbage collection");
285 TAILQ_INIT(pointers
);
290 if (!strcmp(mi
->name
, "null string") || !strcmp(mi
->name
, "fixed string")) {
291 switch (mi
->name
[0]) {
293 size
= strnlen((char *)serialized
->object
,
294 len
- sizeof(struct marshal_serialized
)) +
300 break; /* The extra byte is to ensure that
301 the string is null terminated. */
303 if (size
> len
- sizeof(struct marshal_serialized
)) {
305 "data to deserialize contains a string too long");
307 goto unmarshal_error
;
312 /* First, the main structure */
314 if ((*output
= marshal_alloc(pointers
, size
+ extra
,
315 serialized
->orig
)) == NULL
) {
317 "unable to allocate memory to unserialize structure %s",
320 goto unmarshal_error
;
322 memcpy(*output
, serialized
->object
, size
);
325 /* Then, each substructure */
326 for (current
= mi
->pointers
; current
->mi
; current
++) {
329 new = (unsigned char *)*output
+ current
->offset
;
330 if (current
->kind
== ignore
) {
331 memset((unsigned char *)*output
+ current
->offset
, 0,
335 if (current
->kind
== pointer
) {
336 if (*(void **)new == NULL
) continue;
338 /* Did we already see this reference? */
340 TAILQ_FOREACH (apointer
, pointers
, next
)
341 if (apointer
->orig
== *(void **)new) {
342 memcpy((unsigned char *)*output
+
344 &apointer
->pointer
, sizeof(void *));
348 if (already
) continue;
351 if (current
->offset2
)
352 memcpy(&osize
, (unsigned char *)*output
+ current
->offset2
,
354 padlen
= ALIGNOF(struct marshal_serialized
);
355 padlen
= (padlen
- (total_len
% padlen
)) % padlen
;
356 if (len
< total_len
+ padlen
||
357 ((sublen
= marshal_unserialize_(current
->mi
,
358 (unsigned char *)buffer
+ total_len
+ padlen
,
359 len
- total_len
- padlen
, &new, pointers
,
360 current
->kind
== substruct
, osize
)) == 0)) {
362 "unable to serialize substructure %s for %s",
363 current
->mi
->name
, mi
->name
);
365 goto unmarshal_error
;
367 /* Link the result */
368 if (current
->kind
== pointer
)
369 memcpy((unsigned char *)*output
+ current
->offset
, &new,
371 total_len
+= sublen
+ padlen
;
375 if (pointers
&& !_pointers
) {
376 marshal_free(pointers
, (total_len
> 0));