]> git.ipfire.org Git - thirdparty/lldpd.git/blob - src/marshal.c
marshal: handle fixed-size strings
[thirdparty/lldpd.git] / src / marshal.c
1 /*
2 * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx>
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include "lldpd.h"
18
19 /* A serialized object */
20 struct marshal_serialized {
21 void *orig; /* Original reference. Also enforce alignment. */
22 size_t size;
23 unsigned char object[0];
24 };
25
26 struct marshal_info marshal_info__string = {
27 .name = "null string",
28 .size = 0,
29 .pointers = {{ .mi = NULL }},
30 };
31 struct marshal_info marshal_info__fstring = {
32 .name = "fixed string",
33 .size = 0,
34 .pointers = {{ .mi = NULL }},
35 };
36
37 /* List of already seen pointers */
38 struct ref {
39 TAILQ_ENTRY(ref) next;
40 void *pointer;
41 };
42 TAILQ_HEAD(ref_l, ref);
43
44 /* Serialize the given object. */
45 size_t
46 _marshal_serialize(struct marshal_info *mi, void *unserialized, void **input,
47 int skip, void *_refs, int osize)
48 {
49 /* Check if we have already serialized this one. */
50 struct ref_l *refs = _refs;
51 if (!refs) {
52 refs = calloc(1, sizeof(struct ref_l));
53 if (!refs) {
54 LLOG_WARNX("unable to allocate memory for list of references");
55 return -1;
56 }
57 TAILQ_INIT(refs);
58 }
59 struct ref *cref;
60 TAILQ_FOREACH(cref, refs, next) {
61 if (unserialized == cref->pointer)
62 return 0;
63 }
64
65 /* Handle special cases. */
66 int size = mi->size;
67 if (!strcmp(mi->name, "null string"))
68 size = strlen((char *)unserialized) + 1;
69 else if (!strcmp(mi->name, "fixed string"))
70 size = osize;
71
72 /* Allocate serialized structure */
73 size_t len = sizeof(struct marshal_serialized) + (skip?0:size);
74 struct marshal_serialized *serialized = calloc(1, len);
75 if (!serialized) {
76 LLOG_WARNX("unable to allocate memory to serialize structure %s",
77 mi->name);
78 len = -1;
79 goto marshal_error;
80 }
81 serialized->orig = unserialized;
82
83 /* Append the new reference */
84 if (!(cref = calloc(1, sizeof(struct ref)))) {
85 LLOG_WARNX("unable to allocate memory for list of references");
86 free(serialized);
87 len = -1;
88 goto marshal_error;
89 }
90 cref->pointer = unserialized;
91 TAILQ_INSERT_TAIL(refs, cref, next);
92
93 /* First, serialize the main structure */
94 if (!skip)
95 memcpy(serialized->object, unserialized, size);
96
97 /* Then, serialize inner structures */
98 struct marshal_subinfo *current;
99 for (current = mi->pointers; current->mi; current++) {
100 size_t sublen;
101 void *source;
102 void *target;
103 if (current->kind == pointer) {
104 source = *(void **)((unsigned char *)unserialized + current->offset);
105 if (source == NULL) continue;
106 } else
107 source = (void *)((unsigned char *)unserialized + current->offset);
108 memcpy(&osize, (unsigned char*)unserialized + current->offset2, sizeof(int));
109 sublen = _marshal_serialize(current->mi,
110 source, &target,
111 current->kind == substruct, refs, osize);
112 if (sublen == -1) {
113 LLOG_WARNX("unable to serialize substructure %s for %s",
114 current->mi->name, mi->name);
115 free(serialized);
116 return -1;
117 }
118 if (sublen == 0) continue; /* This was already serialized */
119 /* Append the result */
120 unsigned char *new = realloc(serialized, len + sublen);
121 if (!new) {
122 LLOG_WARNX("unable to allocate more memory to serialize structure %s",
123 mi->name);
124 free(serialized);
125 free(target);
126 len = -1;
127 goto marshal_error;
128 }
129 memcpy(new + len, target, sublen);
130 free(target);
131 len += sublen;
132 serialized = (struct marshal_serialized *)new;
133 }
134
135 serialized->size = len;
136 *input = serialized;
137 marshal_error:
138 if (refs && !_refs) {
139 struct ref *cref, *cref_next;
140 for (cref = TAILQ_FIRST(refs);
141 cref != NULL;
142 cref = cref_next) {
143 cref_next = TAILQ_NEXT(cref, next);
144 TAILQ_REMOVE(refs, cref, next);
145 free(cref);
146 }
147 free(refs);
148 }
149 return len;
150 }
151
152 /* This structure is used to track memory allocation when serializing */
153 struct gc {
154 TAILQ_ENTRY(gc) next;
155 void *pointer;
156 void *orig; /* Original reference (not valid anymore !) */
157 };
158 TAILQ_HEAD(gc_l, gc);
159
160 static void*
161 marshal_alloc(struct gc_l *pointers, size_t len, void *orig)
162 {
163 void *result = malloc(len);
164 if (!result) return NULL;
165 struct gc *gpointer = NULL;
166 if ((gpointer = (struct gc *)calloc(1,
167 sizeof(struct gc))) == NULL) {
168 free(result);
169 return NULL;
170 }
171 gpointer->pointer = result;
172 gpointer->orig = orig;
173 TAILQ_INSERT_TAIL(pointers, gpointer, next);
174 return result;
175 }
176 static void
177 marshal_free(struct gc_l *pointers, int gconly)
178 {
179 struct gc *pointer, *pointer_next;
180 for (pointer = TAILQ_FIRST(pointers);
181 pointer != NULL;
182 pointer = pointer_next) {
183 pointer_next = TAILQ_NEXT(pointer, next);
184 TAILQ_REMOVE(pointers, pointer, next);
185 if (!gconly)
186 free(pointer->pointer);
187 free(pointer);
188 }
189 }
190
191
192 /* Unserialize the given object. */
193 size_t
194 _marshal_unserialize(struct marshal_info *mi, void *buffer, size_t len, void **output,
195 void *_pointers, int skip, int osize)
196 {
197 int total_len = sizeof(struct marshal_serialized) + (skip?0:mi->size);
198 struct marshal_serialized *serialized = buffer;
199 if (len < sizeof(struct marshal_serialized) || len < total_len) {
200 LLOG_WARNX("data to deserialize is too small for structure %s",
201 mi->name);
202 return 0;
203 }
204
205 /* Initialize garbage collection */
206 struct gc_l *pointers = _pointers;
207 if (!pointers) {
208 pointers = calloc(1, sizeof(struct gc_l));
209 if (!pointers) {
210 LLOG_WARNX("unable to allocate memory for garbage collection");
211 return 0;
212 }
213 TAILQ_INIT(pointers);
214 }
215
216 /* Special cases */
217 int size = mi->size;
218 if (!strcmp(mi->name, "null string") || !strcmp(mi->name, "fixed string")) {
219 switch (mi->name[0]) {
220 case 'n': size = strnlen((char *)serialized->object,
221 len - sizeof(struct marshal_serialized)) + 1; break;
222 case 'f': size = osize; break;
223 }
224 if (size > len - sizeof(struct marshal_serialized)) {
225 LLOG_WARNX("data to deserialize contains a string too long");
226 total_len = 0;
227 goto unmarshal_error;
228 }
229 total_len += size;
230 }
231
232 /* First, the main structure */
233 if (!skip) {
234 if ((*output = marshal_alloc(pointers, size, serialized->orig)) == NULL) {
235 LLOG_WARNX("unable to allocate memory to unserialize structure %s",
236 mi->name);
237 total_len = 0;
238 goto unmarshal_error;
239 }
240 memcpy(*output, serialized->object, size);
241 }
242
243 /* Then, each substructure */
244 struct marshal_subinfo *current;
245 for (current = mi->pointers; current->mi; current++) {
246 size_t sublen;
247 void *new = (unsigned char *)*output + current->offset;
248 if (current->kind == pointer) {
249 if (*(void **)new == NULL) continue;
250
251 /* Did we already see this reference? */
252 struct gc *pointer;
253 int already = 0;
254 TAILQ_FOREACH(pointer, pointers, next)
255 if (pointer->orig == *(void **)new) {
256 *(void **)((unsigned char *)*output +
257 current->offset) = pointer->pointer;
258 already = 1;
259 break;
260 }
261 if (already) continue;
262 }
263 /* Deserialize */
264 memcpy(&osize, (unsigned char *)*output + current->offset2, sizeof(int));
265 sublen = _marshal_unserialize(current->mi,
266 (unsigned char *)buffer + total_len, len - total_len, &new, pointers,
267 current->kind == substruct, osize);
268 if (sublen == 0) {
269 LLOG_WARNX("unable to serialize substructure %s for %s",
270 current->mi->name, mi->name);
271 total_len = 0;
272 goto unmarshal_error;
273 }
274 /* Link the result */
275 if (current->kind == pointer)
276 *(void **)((unsigned char *)*output + current->offset) = new;
277 total_len += sublen;
278 }
279
280 unmarshal_error:
281 if (pointers && !_pointers) {
282 marshal_free(pointers, (total_len > 0));
283 free(pointers);
284 }
285 return total_len;
286 }