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 /* A serialized object */
33 struct marshal_serialized
{
34 void *orig
; /* Original reference. Also enforce alignment. */
36 unsigned char object
[0];
39 struct marshal_info marshal_info_string
= {
40 .name
= "null string",
42 .pointers
= {MARSHAL_SUBINFO_NULL
},
44 struct marshal_info marshal_info_fstring
= {
45 .name
= "fixed string",
47 .pointers
= {MARSHAL_SUBINFO_NULL
},
49 struct marshal_info marshal_info_ignore
= {
52 .pointers
= {MARSHAL_SUBINFO_NULL
},
55 /* List of already seen pointers */
57 TAILQ_ENTRY(ref
) next
;
60 TAILQ_HEAD(ref_l
, ref
);
62 /* Serialize the given object. */
64 marshal_serialize_(struct marshal_info
*mi
, void *unserialized
, void **input
,
65 int skip
, void *_refs
, int osize
)
67 struct ref_l
*refs
= _refs
;
71 struct marshal_subinfo
*current
;
72 struct marshal_serialized
*new = NULL
, *serialized
= NULL
;
74 log_debug("marshal", "start serialization of %s", mi
->name
);
76 /* Check if we have already serialized this one. */
78 refs
= calloc(1, sizeof(struct ref_l
));
80 log_warnx("marshal", "unable to allocate memory for list of references");
85 TAILQ_FOREACH(cref
, refs
, next
) {
86 if (unserialized
== cref
->pointer
)
90 /* Handle special cases. */
92 if (!strcmp(mi
->name
, "null string"))
93 size
= strlen((char *)unserialized
) + 1;
94 else if (!strcmp(mi
->name
, "fixed string"))
97 /* Allocate serialized structure */
98 len
= sizeof(struct marshal_serialized
) + (skip
?0:size
);
99 serialized
= calloc(1, len
);
101 log_warnx("marshal", "unable to allocate memory to serialize structure %s",
106 serialized
->orig
= unserialized
;
108 /* Append the new reference */
109 if (!(cref
= calloc(1, sizeof(struct ref
)))) {
110 log_warnx("marshal", "unable to allocate memory for list of references");
115 cref
->pointer
= unserialized
;
116 TAILQ_INSERT_TAIL(refs
, cref
, next
);
118 /* First, serialize the main structure */
120 memcpy(serialized
->object
, unserialized
, size
);
122 /* Then, serialize inner structures */
123 for (current
= mi
->pointers
; current
->mi
; current
++) {
127 if (current
->kind
== ignore
) continue;
128 if (current
->kind
== pointer
) {
130 (unsigned char *)unserialized
+ current
->offset
,
132 if (source
== NULL
) continue;
134 source
= (void *)((unsigned char *)unserialized
+ current
->offset
);
135 if (current
->offset2
)
136 memcpy(&osize
, (unsigned char*)unserialized
+ current
->offset2
, sizeof(int));
137 sublen
= marshal_serialize_(current
->mi
,
139 current
->kind
== substruct
, refs
, osize
);
141 log_warnx("marshal", "unable to serialize substructure %s for %s",
142 current
->mi
->name
, mi
->name
);
146 if (sublen
== 0) continue; /* This was already serialized */
147 /* Append the result */
148 new = realloc(serialized
, len
+ sublen
);
150 log_warnx("marshal", "unable to allocate more memory to serialize structure %s",
157 memcpy((unsigned char *)new + len
, target
, sublen
);
160 serialized
= (struct marshal_serialized
*)new;
163 serialized
->size
= len
;
166 if (refs
&& !_refs
) {
167 struct ref
*cref
, *cref_next
;
168 for (cref
= TAILQ_FIRST(refs
);
171 cref_next
= TAILQ_NEXT(cref
, next
);
172 TAILQ_REMOVE(refs
, cref
, next
);
180 /* This structure is used to track memory allocation when serializing */
182 TAILQ_ENTRY(gc
) next
;
184 void *orig
; /* Original reference (not valid anymore !) */
186 TAILQ_HEAD(gc_l
, gc
);
189 marshal_alloc(struct gc_l
*pointers
, size_t len
, void *orig
)
191 struct gc
*gpointer
= NULL
;
193 void *result
= calloc(1, len
);
194 if (!result
) return NULL
;
195 if ((gpointer
= (struct gc
*)calloc(1,
196 sizeof(struct gc
))) == NULL
) {
200 gpointer
->pointer
= result
;
201 gpointer
->orig
= orig
;
202 TAILQ_INSERT_TAIL(pointers
, gpointer
, next
);
206 marshal_free(struct gc_l
*pointers
, int gconly
)
208 struct gc
*pointer
, *pointer_next
;
209 for (pointer
= TAILQ_FIRST(pointers
);
211 pointer
= pointer_next
) {
212 pointer_next
= TAILQ_NEXT(pointer
, next
);
213 TAILQ_REMOVE(pointers
, pointer
, next
);
215 free(pointer
->pointer
);
221 /* Unserialize the given object. */
223 marshal_unserialize_(struct marshal_info
*mi
, void *buffer
, size_t len
, void **output
,
224 void *_pointers
, int skip
, int osize
)
226 int total_len
= sizeof(struct marshal_serialized
) + (skip
?0:mi
->size
);
227 struct marshal_serialized
*serialized
= buffer
;
228 struct gc_l
*pointers
= _pointers
;
229 int size
, already
, extra
= 0;
231 struct marshal_subinfo
*current
;
234 log_debug("marshal", "start unserialization of %s", mi
->name
);
236 if (len
< sizeof(struct marshal_serialized
) || len
< total_len
) {
237 log_warnx("marshal", "data to deserialize is too small (%zu) for structure %s",
242 /* Initialize garbage collection */
244 pointers
= calloc(1, sizeof(struct gc_l
));
246 log_warnx("marshal", "unable to allocate memory for garbage collection");
249 TAILQ_INIT(pointers
);
254 if (!strcmp(mi
->name
, "null string") || !strcmp(mi
->name
, "fixed string")) {
255 switch (mi
->name
[0]) {
256 case 'n': size
= strnlen((char *)serialized
->object
,
257 len
- sizeof(struct marshal_serialized
)) + 1; break;
258 case 'f': size
= osize
; extra
=1; break; /* The extra byte is to ensure that
259 the string is null terminated. */
261 if (size
> len
- sizeof(struct marshal_serialized
)) {
262 log_warnx("marshal", "data to deserialize contains a string too long");
264 goto unmarshal_error
;
269 /* First, the main structure */
271 if ((*output
= marshal_alloc(pointers
, size
+ extra
, serialized
->orig
)) == NULL
) {
272 log_warnx("marshal", "unable to allocate memory to unserialize structure %s",
275 goto unmarshal_error
;
277 memcpy(*output
, serialized
->object
, size
);
280 /* Then, each substructure */
281 for (current
= mi
->pointers
; current
->mi
; current
++) {
283 new = (unsigned char *)*output
+ current
->offset
;
284 if (current
->kind
== ignore
) {
285 memset((unsigned char *)*output
+ current
->offset
,
289 if (current
->kind
== pointer
) {
290 if (*(void **)new == NULL
) continue;
292 /* Did we already see this reference? */
294 TAILQ_FOREACH(apointer
, pointers
, next
)
295 if (apointer
->orig
== *(void **)new) {
296 memcpy((unsigned char *)*output
+ current
->offset
,
297 &apointer
->pointer
, sizeof(void *));
301 if (already
) continue;
304 if (current
->offset2
)
305 memcpy(&osize
, (unsigned char *)*output
+ current
->offset2
, sizeof(int));
306 sublen
= marshal_unserialize_(current
->mi
,
307 (unsigned char *)buffer
+ total_len
, len
- total_len
, &new, pointers
,
308 current
->kind
== substruct
, osize
);
310 log_warnx("marshal", "unable to serialize substructure %s for %s",
311 current
->mi
->name
, mi
->name
);
313 goto unmarshal_error
;
315 /* Link the result */
316 if (current
->kind
== pointer
)
317 memcpy((unsigned char *)*output
+ current
->offset
,
318 &new, sizeof(void *));
323 if (pointers
&& !_pointers
) {
324 marshal_free(pointers
, (total_len
> 0));