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.
19 #define MARSHAL_EXPORT
24 #include <sys/types.h>
25 #include <sys/queue.h>
28 #include "compat/compat.h"
31 #include "lldpd-structs.h"
33 /* A serialized object */
34 struct marshal_serialized
{
35 void *orig
; /* Original reference. Also enforce alignment. */
37 unsigned char object
[0];
40 struct marshal_info marshal_info_string
= {
41 .name
= "null string",
43 .pointers
= {MARSHAL_SUBINFO_NULL
},
45 struct marshal_info marshal_info_fstring
= {
46 .name
= "fixed string",
48 .pointers
= {MARSHAL_SUBINFO_NULL
},
50 struct marshal_info marshal_info_ignore
= {
53 .pointers
= {MARSHAL_SUBINFO_NULL
},
56 /* List of already seen pointers */
58 TAILQ_ENTRY(ref
) next
;
60 int dummy
; /* To renumerate pointers */
62 TAILQ_HEAD(ref_l
, ref
);
64 /* Serialize the given object. */
66 marshal_serialize_(struct marshal_info
*mi
, void *unserialized
, void **input
,
67 int skip
, void *_refs
, int osize
)
69 struct ref_l
*refs
= _refs
;
73 struct marshal_subinfo
*current
;
74 struct marshal_serialized
*new = NULL
, *serialized
= NULL
;
77 log_debug("marshal", "start serialization of %s", mi
->name
);
79 /* Check if we have already serialized this one. */
81 refs
= calloc(1, sizeof(struct ref_l
));
83 log_warnx("marshal", "unable to allocate memory for list of references");
88 TAILQ_FOREACH(cref
, refs
, next
) {
89 if (unserialized
== cref
->pointer
)
91 /* dummy should be higher than any existing dummy */
92 if (cref
->dummy
>= dummy
) dummy
= cref
->dummy
+ 1;
95 /* Handle special cases. */
97 if (!strcmp(mi
->name
, "null string"))
98 size
= strlen((char *)unserialized
) + 1;
99 else if (!strcmp(mi
->name
, "fixed string"))
102 /* Allocate serialized structure */
103 len
= sizeof(struct marshal_serialized
) + (skip
?0:size
);
104 serialized
= calloc(1, len
);
106 log_warnx("marshal", "unable to allocate memory to serialize structure %s",
111 /* We don't use the original pointer but a dummy one. */
112 serialized
->orig
= (unsigned char*)NULL
+ dummy
;
114 /* Append the new reference */
115 if (!(cref
= calloc(1, sizeof(struct ref
)))) {
116 log_warnx("marshal", "unable to allocate memory for list of references");
121 cref
->pointer
= unserialized
;
123 TAILQ_INSERT_TAIL(refs
, cref
, next
);
125 /* First, serialize the main structure */
127 memcpy(serialized
->object
, unserialized
, size
);
129 /* Then, serialize inner structures */
130 for (current
= mi
->pointers
; current
->mi
; current
++) {
134 if (current
->kind
== ignore
) continue;
135 if (current
->kind
== pointer
) {
137 (unsigned char *)unserialized
+ current
->offset
,
139 if (source
== NULL
) continue;
141 source
= (void *)((unsigned char *)unserialized
+ current
->offset
);
142 if (current
->offset2
)
143 memcpy(&osize
, (unsigned char*)unserialized
+ current
->offset2
, sizeof(int));
144 sublen
= marshal_serialize_(current
->mi
,
146 current
->kind
== substruct
, refs
, osize
);
148 log_warnx("marshal", "unable to serialize substructure %s for %s",
149 current
->mi
->name
, mi
->name
);
153 /* We want to put the renumerated pointer instead of the real one. */
154 if (current
->kind
== pointer
&& !skip
) {
155 TAILQ_FOREACH(cref
, refs
, next
) {
156 if (source
== cref
->pointer
) {
157 void *fakepointer
= (unsigned char*)NULL
+ cref
->dummy
;
158 memcpy((unsigned char *)serialized
->object
+ current
->offset
,
159 &fakepointer
, sizeof(void *));
164 if (sublen
== 0) continue; /* This was already serialized */
165 /* Append the result */
166 new = realloc(serialized
, len
+ sublen
);
168 log_warnx("marshal", "unable to allocate more memory to serialize structure %s",
175 memcpy((unsigned char *)new + len
, target
, sublen
);
178 serialized
= (struct marshal_serialized
*)new;
181 serialized
->size
= len
;
184 if (refs
&& !_refs
) {
185 struct ref
*cref
, *cref_next
;
186 for (cref
= TAILQ_FIRST(refs
);
189 cref_next
= TAILQ_NEXT(cref
, next
);
190 TAILQ_REMOVE(refs
, cref
, next
);
198 /* This structure is used to track memory allocation when serializing */
200 TAILQ_ENTRY(gc
) next
;
202 void *orig
; /* Original reference (not valid anymore !) */
204 TAILQ_HEAD(gc_l
, gc
);
207 marshal_alloc(struct gc_l
*pointers
, size_t len
, void *orig
)
209 struct gc
*gpointer
= NULL
;
211 void *result
= calloc(1, len
);
212 if (!result
) return NULL
;
213 if ((gpointer
= (struct gc
*)calloc(1,
214 sizeof(struct gc
))) == NULL
) {
218 gpointer
->pointer
= result
;
219 gpointer
->orig
= orig
;
220 TAILQ_INSERT_TAIL(pointers
, gpointer
, next
);
224 marshal_free(struct gc_l
*pointers
, int gconly
)
226 struct gc
*pointer
, *pointer_next
;
227 for (pointer
= TAILQ_FIRST(pointers
);
229 pointer
= pointer_next
) {
230 pointer_next
= TAILQ_NEXT(pointer
, next
);
231 TAILQ_REMOVE(pointers
, pointer
, next
);
233 free(pointer
->pointer
);
239 /* Unserialize the given object. */
241 marshal_unserialize_(struct marshal_info
*mi
, void *buffer
, size_t len
, void **output
,
242 void *_pointers
, int skip
, int osize
)
244 int total_len
= sizeof(struct marshal_serialized
) + (skip
?0:mi
->size
);
245 struct marshal_serialized
*serialized
= buffer
;
246 struct gc_l
*pointers
= _pointers
;
247 int size
, already
, extra
= 0;
249 struct marshal_subinfo
*current
;
252 log_debug("marshal", "start unserialization of %s", mi
->name
);
254 if (len
< sizeof(struct marshal_serialized
) || len
< total_len
) {
255 log_warnx("marshal", "data to deserialize is too small (%zu) for structure %s",
260 /* Initialize garbage collection */
262 pointers
= calloc(1, sizeof(struct gc_l
));
264 log_warnx("marshal", "unable to allocate memory for garbage collection");
267 TAILQ_INIT(pointers
);
272 if (!strcmp(mi
->name
, "null string") || !strcmp(mi
->name
, "fixed string")) {
273 switch (mi
->name
[0]) {
274 case 'n': size
= strnlen((char *)serialized
->object
,
275 len
- sizeof(struct marshal_serialized
)) + 1; break;
276 case 'f': size
= osize
; extra
=1; break; /* The extra byte is to ensure that
277 the string is null terminated. */
279 if (size
> len
- sizeof(struct marshal_serialized
)) {
280 log_warnx("marshal", "data to deserialize contains a string too long");
282 goto unmarshal_error
;
287 /* First, the main structure */
289 if ((*output
= marshal_alloc(pointers
, size
+ extra
, serialized
->orig
)) == NULL
) {
290 log_warnx("marshal", "unable to allocate memory to unserialize structure %s",
293 goto unmarshal_error
;
295 memcpy(*output
, serialized
->object
, size
);
298 /* Then, each substructure */
299 for (current
= mi
->pointers
; current
->mi
; current
++) {
301 new = (unsigned char *)*output
+ current
->offset
;
302 if (current
->kind
== ignore
) {
303 memset((unsigned char *)*output
+ current
->offset
,
307 if (current
->kind
== pointer
) {
308 if (*(void **)new == NULL
) continue;
310 /* Did we already see this reference? */
312 TAILQ_FOREACH(apointer
, pointers
, next
)
313 if (apointer
->orig
== *(void **)new) {
314 memcpy((unsigned char *)*output
+ current
->offset
,
315 &apointer
->pointer
, sizeof(void *));
319 if (already
) continue;
322 if (current
->offset2
)
323 memcpy(&osize
, (unsigned char *)*output
+ current
->offset2
, sizeof(int));
324 sublen
= marshal_unserialize_(current
->mi
,
325 (unsigned char *)buffer
+ total_len
, len
- total_len
, &new, pointers
,
326 current
->kind
== substruct
, osize
);
328 log_warnx("marshal", "unable to serialize substructure %s for %s",
329 current
->mi
->name
, mi
->name
);
331 goto unmarshal_error
;
333 /* Link the result */
334 if (current
->kind
== pointer
)
335 memcpy((unsigned char *)*output
+ current
->offset
,
336 &new, sizeof(void *));
341 if (pointers
&& !_pointers
) {
342 marshal_free(pointers
, (total_len
> 0));