]> git.ipfire.org Git - thirdparty/lldpd.git/blob - src/marshal.c
log: convert LLOG_* to log_* and add more debug logs
[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 /* A serialized object */
33 struct marshal_serialized {
34 void *orig; /* Original reference. Also enforce alignment. */
35 size_t size;
36 unsigned char object[0];
37 };
38
39 struct marshal_info marshal_info_string = {
40 .name = "null string",
41 .size = 0,
42 .pointers = {MARSHAL_SUBINFO_NULL},
43 };
44 struct marshal_info marshal_info_fstring = {
45 .name = "fixed string",
46 .size = 0,
47 .pointers = {MARSHAL_SUBINFO_NULL},
48 };
49 struct marshal_info marshal_info_ignore = {
50 .name = "ignored",
51 .size = 0,
52 .pointers = {MARSHAL_SUBINFO_NULL},
53 };
54
55 /* List of already seen pointers */
56 struct ref {
57 TAILQ_ENTRY(ref) next;
58 void *pointer;
59 };
60 TAILQ_HEAD(ref_l, ref);
61
62 /* Serialize the given object. */
63 size_t
64 marshal_serialize_(struct marshal_info *mi, void *unserialized, void **input,
65 int skip, void *_refs, int osize)
66 {
67 struct ref_l *refs = _refs;
68 struct ref *cref;
69 int size;
70 size_t len;
71 struct marshal_subinfo *current;
72 struct marshal_serialized *new = NULL, *serialized = NULL;
73
74 log_debug("marshal", "start serialization of %s", mi->name);
75
76 /* Check if we have already serialized this one. */
77 if (!refs) {
78 refs = calloc(1, sizeof(struct ref_l));
79 if (!refs) {
80 log_warnx("marshal", "unable to allocate memory for list of references");
81 return -1;
82 }
83 TAILQ_INIT(refs);
84 }
85 TAILQ_FOREACH(cref, refs, next) {
86 if (unserialized == cref->pointer)
87 return 0;
88 }
89
90 /* Handle special cases. */
91 size = mi->size;
92 if (!strcmp(mi->name, "null string"))
93 size = strlen((char *)unserialized) + 1;
94 else if (!strcmp(mi->name, "fixed string"))
95 size = osize;
96
97 /* Allocate serialized structure */
98 len = sizeof(struct marshal_serialized) + (skip?0:size);
99 serialized = calloc(1, len);
100 if (!serialized) {
101 log_warnx("marshal", "unable to allocate memory to serialize structure %s",
102 mi->name);
103 len = -1;
104 goto marshal_error;
105 }
106 serialized->orig = unserialized;
107
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");
111 free(serialized);
112 len = -1;
113 goto marshal_error;
114 }
115 cref->pointer = unserialized;
116 TAILQ_INSERT_TAIL(refs, cref, next);
117
118 /* First, serialize the main structure */
119 if (!skip)
120 memcpy(serialized->object, unserialized, size);
121
122 /* Then, serialize inner structures */
123 for (current = mi->pointers; current->mi; current++) {
124 size_t sublen;
125 void *source;
126 void *target;
127 if (current->kind == ignore) continue;
128 if (current->kind == pointer) {
129 memcpy(&source,
130 (unsigned char *)unserialized + current->offset,
131 sizeof(void *));
132 if (source == NULL) continue;
133 } else
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,
138 source, &target,
139 current->kind == substruct, refs, osize);
140 if (sublen == -1) {
141 log_warnx("marshal", "unable to serialize substructure %s for %s",
142 current->mi->name, mi->name);
143 free(serialized);
144 return -1;
145 }
146 if (sublen == 0) continue; /* This was already serialized */
147 /* Append the result */
148 new = realloc(serialized, len + sublen);
149 if (!new) {
150 log_warnx("marshal", "unable to allocate more memory to serialize structure %s",
151 mi->name);
152 free(serialized);
153 free(target);
154 len = -1;
155 goto marshal_error;
156 }
157 memcpy((unsigned char *)new + len, target, sublen);
158 free(target);
159 len += sublen;
160 serialized = (struct marshal_serialized *)new;
161 }
162
163 serialized->size = len;
164 *input = serialized;
165 marshal_error:
166 if (refs && !_refs) {
167 struct ref *cref, *cref_next;
168 for (cref = TAILQ_FIRST(refs);
169 cref != NULL;
170 cref = cref_next) {
171 cref_next = TAILQ_NEXT(cref, next);
172 TAILQ_REMOVE(refs, cref, next);
173 free(cref);
174 }
175 free(refs);
176 }
177 return len;
178 }
179
180 /* This structure is used to track memory allocation when serializing */
181 struct gc {
182 TAILQ_ENTRY(gc) next;
183 void *pointer;
184 void *orig; /* Original reference (not valid anymore !) */
185 };
186 TAILQ_HEAD(gc_l, gc);
187
188 static void*
189 marshal_alloc(struct gc_l *pointers, size_t len, void *orig)
190 {
191 struct gc *gpointer = NULL;
192
193 void *result = calloc(1, len);
194 if (!result) return NULL;
195 if ((gpointer = (struct gc *)calloc(1,
196 sizeof(struct gc))) == NULL) {
197 free(result);
198 return NULL;
199 }
200 gpointer->pointer = result;
201 gpointer->orig = orig;
202 TAILQ_INSERT_TAIL(pointers, gpointer, next);
203 return result;
204 }
205 static void
206 marshal_free(struct gc_l *pointers, int gconly)
207 {
208 struct gc *pointer, *pointer_next;
209 for (pointer = TAILQ_FIRST(pointers);
210 pointer != NULL;
211 pointer = pointer_next) {
212 pointer_next = TAILQ_NEXT(pointer, next);
213 TAILQ_REMOVE(pointers, pointer, next);
214 if (!gconly)
215 free(pointer->pointer);
216 free(pointer);
217 }
218 }
219
220
221 /* Unserialize the given object. */
222 size_t
223 marshal_unserialize_(struct marshal_info *mi, void *buffer, size_t len, void **output,
224 void *_pointers, int skip, int osize)
225 {
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;
230 void *new;
231 struct marshal_subinfo *current;
232 struct gc *apointer;
233
234 log_debug("marshal", "start unserialization of %s", mi->name);
235
236 if (len < sizeof(struct marshal_serialized) || len < total_len) {
237 log_warnx("marshal", "data to deserialize is too small (%zu) for structure %s",
238 len, mi->name);
239 return 0;
240 }
241
242 /* Initialize garbage collection */
243 if (!pointers) {
244 pointers = calloc(1, sizeof(struct gc_l));
245 if (!pointers) {
246 log_warnx("marshal", "unable to allocate memory for garbage collection");
247 return 0;
248 }
249 TAILQ_INIT(pointers);
250 }
251
252 /* Special cases */
253 size = mi->size;
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. */
260 }
261 if (size > len - sizeof(struct marshal_serialized)) {
262 log_warnx("marshal", "data to deserialize contains a string too long");
263 total_len = 0;
264 goto unmarshal_error;
265 }
266 total_len += size;
267 }
268
269 /* First, the main structure */
270 if (!skip) {
271 if ((*output = marshal_alloc(pointers, size + extra, serialized->orig)) == NULL) {
272 log_warnx("marshal", "unable to allocate memory to unserialize structure %s",
273 mi->name);
274 total_len = 0;
275 goto unmarshal_error;
276 }
277 memcpy(*output, serialized->object, size);
278 }
279
280 /* Then, each substructure */
281 for (current = mi->pointers; current->mi; current++) {
282 size_t sublen;
283 new = (unsigned char *)*output + current->offset;
284 if (current->kind == ignore) {
285 memset((unsigned char *)*output + current->offset,
286 0, sizeof(void *));
287 continue;
288 }
289 if (current->kind == pointer) {
290 if (*(void **)new == NULL) continue;
291
292 /* Did we already see this reference? */
293 already = 0;
294 TAILQ_FOREACH(apointer, pointers, next)
295 if (apointer->orig == *(void **)new) {
296 memcpy((unsigned char *)*output + current->offset,
297 &apointer->pointer, sizeof(void *));
298 already = 1;
299 break;
300 }
301 if (already) continue;
302 }
303 /* Deserialize */
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);
309 if (sublen == 0) {
310 log_warnx("marshal", "unable to serialize substructure %s for %s",
311 current->mi->name, mi->name);
312 total_len = 0;
313 goto unmarshal_error;
314 }
315 /* Link the result */
316 if (current->kind == pointer)
317 memcpy((unsigned char *)*output + current->offset,
318 &new, sizeof(void *));
319 total_len += sublen;
320 }
321
322 unmarshal_error:
323 if (pointers && !_pointers) {
324 marshal_free(pointers, (total_len > 0));
325 free(pointers);
326 }
327 return total_len;
328 }