1 | /* |
2 | * String parsing visitor |
3 | * |
4 | * Copyright Red Hat, Inc. 2012-2016 |
5 | * |
6 | * Author: Paolo Bonzini <pbonzini@redhat.com> |
7 | * David Hildenbrand <david@redhat.com> |
8 | * |
9 | * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. |
10 | * See the COPYING.LIB file in the top-level directory. |
11 | */ |
12 | |
13 | #include "qemu/osdep.h" |
14 | #include "qapi/error.h" |
15 | #include "qapi/string-input-visitor.h" |
16 | #include "qapi/visitor-impl.h" |
17 | #include "qapi/qmp/qerror.h" |
18 | #include "qapi/qmp/qnull.h" |
19 | #include "qemu/option.h" |
20 | #include "qemu/cutils.h" |
21 | |
22 | typedef enum ListMode { |
23 | /* no list parsing active / no list expected */ |
24 | LM_NONE, |
25 | /* we have an unparsed string remaining */ |
26 | LM_UNPARSED, |
27 | /* we have an unfinished int64 range */ |
28 | LM_INT64_RANGE, |
29 | /* we have an unfinished uint64 range */ |
30 | LM_UINT64_RANGE, |
31 | /* we have parsed the string completely and no range is remaining */ |
32 | LM_END, |
33 | } ListMode; |
34 | |
35 | /* protect against DOS attacks, limit the amount of elements per range */ |
36 | #define RANGE_MAX_ELEMENTS 65536 |
37 | |
38 | typedef union RangeElement { |
39 | int64_t i64; |
40 | uint64_t u64; |
41 | } RangeElement; |
42 | |
43 | struct StringInputVisitor |
44 | { |
45 | Visitor visitor; |
46 | |
47 | /* List parsing state */ |
48 | ListMode lm; |
49 | RangeElement rangeNext; |
50 | RangeElement rangeEnd; |
51 | const char *unparsed_string; |
52 | void *list; |
53 | |
54 | /* The original string to parse */ |
55 | const char *string; |
56 | }; |
57 | |
58 | static StringInputVisitor *to_siv(Visitor *v) |
59 | { |
60 | return container_of(v, StringInputVisitor, visitor); |
61 | } |
62 | |
63 | static void start_list(Visitor *v, const char *name, GenericList **list, |
64 | size_t size, Error **errp) |
65 | { |
66 | StringInputVisitor *siv = to_siv(v); |
67 | |
68 | assert(siv->lm == LM_NONE); |
69 | siv->list = list; |
70 | siv->unparsed_string = siv->string; |
71 | |
72 | if (!siv->string[0]) { |
73 | if (list) { |
74 | *list = NULL; |
75 | } |
76 | siv->lm = LM_END; |
77 | } else { |
78 | if (list) { |
79 | *list = g_malloc0(size); |
80 | } |
81 | siv->lm = LM_UNPARSED; |
82 | } |
83 | } |
84 | |
85 | static GenericList *next_list(Visitor *v, GenericList *tail, size_t size) |
86 | { |
87 | StringInputVisitor *siv = to_siv(v); |
88 | |
89 | switch (siv->lm) { |
90 | case LM_END: |
91 | return NULL; |
92 | case LM_INT64_RANGE: |
93 | case LM_UINT64_RANGE: |
94 | case LM_UNPARSED: |
95 | /* we have an unparsed string or something left in a range */ |
96 | break; |
97 | default: |
98 | abort(); |
99 | } |
100 | |
101 | tail->next = g_malloc0(size); |
102 | return tail->next; |
103 | } |
104 | |
105 | static void check_list(Visitor *v, Error **errp) |
106 | { |
107 | const StringInputVisitor *siv = to_siv(v); |
108 | |
109 | switch (siv->lm) { |
110 | case LM_INT64_RANGE: |
111 | case LM_UINT64_RANGE: |
112 | case LM_UNPARSED: |
113 | error_setg(errp, "Fewer list elements expected" ); |
114 | return; |
115 | case LM_END: |
116 | return; |
117 | default: |
118 | abort(); |
119 | } |
120 | } |
121 | |
122 | static void end_list(Visitor *v, void **obj) |
123 | { |
124 | StringInputVisitor *siv = to_siv(v); |
125 | |
126 | assert(siv->lm != LM_NONE); |
127 | assert(siv->list == obj); |
128 | siv->list = NULL; |
129 | siv->unparsed_string = NULL; |
130 | siv->lm = LM_NONE; |
131 | } |
132 | |
133 | static int try_parse_int64_list_entry(StringInputVisitor *siv, int64_t *obj) |
134 | { |
135 | const char *endptr; |
136 | int64_t start, end; |
137 | |
138 | /* parse a simple int64 or range */ |
139 | if (qemu_strtoi64(siv->unparsed_string, &endptr, 0, &start)) { |
140 | return -EINVAL; |
141 | } |
142 | end = start; |
143 | |
144 | switch (endptr[0]) { |
145 | case '\0': |
146 | siv->unparsed_string = endptr; |
147 | break; |
148 | case ',': |
149 | siv->unparsed_string = endptr + 1; |
150 | break; |
151 | case '-': |
152 | /* parse the end of the range */ |
153 | if (qemu_strtoi64(endptr + 1, &endptr, 0, &end)) { |
154 | return -EINVAL; |
155 | } |
156 | if (start > end || end - start >= RANGE_MAX_ELEMENTS) { |
157 | return -EINVAL; |
158 | } |
159 | switch (endptr[0]) { |
160 | case '\0': |
161 | siv->unparsed_string = endptr; |
162 | break; |
163 | case ',': |
164 | siv->unparsed_string = endptr + 1; |
165 | break; |
166 | default: |
167 | return -EINVAL; |
168 | } |
169 | break; |
170 | default: |
171 | return -EINVAL; |
172 | } |
173 | |
174 | /* we have a proper range (with maybe only one element) */ |
175 | siv->lm = LM_INT64_RANGE; |
176 | siv->rangeNext.i64 = start; |
177 | siv->rangeEnd.i64 = end; |
178 | return 0; |
179 | } |
180 | |
181 | static void parse_type_int64(Visitor *v, const char *name, int64_t *obj, |
182 | Error **errp) |
183 | { |
184 | StringInputVisitor *siv = to_siv(v); |
185 | int64_t val; |
186 | |
187 | switch (siv->lm) { |
188 | case LM_NONE: |
189 | /* just parse a simple int64, bail out if not completely consumed */ |
190 | if (qemu_strtoi64(siv->string, NULL, 0, &val)) { |
191 | error_setg(errp, QERR_INVALID_PARAMETER_VALUE, |
192 | name ? name : "null" , "int64" ); |
193 | return; |
194 | } |
195 | *obj = val; |
196 | return; |
197 | case LM_UNPARSED: |
198 | if (try_parse_int64_list_entry(siv, obj)) { |
199 | error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null" , |
200 | "list of int64 values or ranges" ); |
201 | return; |
202 | } |
203 | assert(siv->lm == LM_INT64_RANGE); |
204 | /* fall through */ |
205 | case LM_INT64_RANGE: |
206 | /* return the next element in the range */ |
207 | assert(siv->rangeNext.i64 <= siv->rangeEnd.i64); |
208 | *obj = siv->rangeNext.i64++; |
209 | |
210 | if (siv->rangeNext.i64 > siv->rangeEnd.i64 || *obj == INT64_MAX) { |
211 | /* end of range, check if there is more to parse */ |
212 | siv->lm = siv->unparsed_string[0] ? LM_UNPARSED : LM_END; |
213 | } |
214 | return; |
215 | case LM_END: |
216 | error_setg(errp, "Fewer list elements expected" ); |
217 | return; |
218 | default: |
219 | abort(); |
220 | } |
221 | } |
222 | |
223 | static int try_parse_uint64_list_entry(StringInputVisitor *siv, uint64_t *obj) |
224 | { |
225 | const char *endptr; |
226 | uint64_t start, end; |
227 | |
228 | /* parse a simple uint64 or range */ |
229 | if (qemu_strtou64(siv->unparsed_string, &endptr, 0, &start)) { |
230 | return -EINVAL; |
231 | } |
232 | end = start; |
233 | |
234 | switch (endptr[0]) { |
235 | case '\0': |
236 | siv->unparsed_string = endptr; |
237 | break; |
238 | case ',': |
239 | siv->unparsed_string = endptr + 1; |
240 | break; |
241 | case '-': |
242 | /* parse the end of the range */ |
243 | if (qemu_strtou64(endptr + 1, &endptr, 0, &end)) { |
244 | return -EINVAL; |
245 | } |
246 | if (start > end || end - start >= RANGE_MAX_ELEMENTS) { |
247 | return -EINVAL; |
248 | } |
249 | switch (endptr[0]) { |
250 | case '\0': |
251 | siv->unparsed_string = endptr; |
252 | break; |
253 | case ',': |
254 | siv->unparsed_string = endptr + 1; |
255 | break; |
256 | default: |
257 | return -EINVAL; |
258 | } |
259 | break; |
260 | default: |
261 | return -EINVAL; |
262 | } |
263 | |
264 | /* we have a proper range (with maybe only one element) */ |
265 | siv->lm = LM_UINT64_RANGE; |
266 | siv->rangeNext.u64 = start; |
267 | siv->rangeEnd.u64 = end; |
268 | return 0; |
269 | } |
270 | |
271 | static void parse_type_uint64(Visitor *v, const char *name, uint64_t *obj, |
272 | Error **errp) |
273 | { |
274 | StringInputVisitor *siv = to_siv(v); |
275 | uint64_t val; |
276 | |
277 | switch (siv->lm) { |
278 | case LM_NONE: |
279 | /* just parse a simple uint64, bail out if not completely consumed */ |
280 | if (qemu_strtou64(siv->string, NULL, 0, &val)) { |
281 | error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null" , |
282 | "uint64" ); |
283 | return; |
284 | } |
285 | *obj = val; |
286 | return; |
287 | case LM_UNPARSED: |
288 | if (try_parse_uint64_list_entry(siv, obj)) { |
289 | error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null" , |
290 | "list of uint64 values or ranges" ); |
291 | return; |
292 | } |
293 | assert(siv->lm == LM_UINT64_RANGE); |
294 | /* fall through */ |
295 | case LM_UINT64_RANGE: |
296 | /* return the next element in the range */ |
297 | assert(siv->rangeNext.u64 <= siv->rangeEnd.u64); |
298 | *obj = siv->rangeNext.u64++; |
299 | |
300 | if (siv->rangeNext.u64 > siv->rangeEnd.u64 || *obj == UINT64_MAX) { |
301 | /* end of range, check if there is more to parse */ |
302 | siv->lm = siv->unparsed_string[0] ? LM_UNPARSED : LM_END; |
303 | } |
304 | return; |
305 | case LM_END: |
306 | error_setg(errp, "Fewer list elements expected" ); |
307 | return; |
308 | default: |
309 | abort(); |
310 | } |
311 | } |
312 | |
313 | static void parse_type_size(Visitor *v, const char *name, uint64_t *obj, |
314 | Error **errp) |
315 | { |
316 | StringInputVisitor *siv = to_siv(v); |
317 | Error *err = NULL; |
318 | uint64_t val; |
319 | |
320 | assert(siv->lm == LM_NONE); |
321 | parse_option_size(name, siv->string, &val, &err); |
322 | if (err) { |
323 | error_propagate(errp, err); |
324 | return; |
325 | } |
326 | |
327 | *obj = val; |
328 | } |
329 | |
330 | static void parse_type_bool(Visitor *v, const char *name, bool *obj, |
331 | Error **errp) |
332 | { |
333 | StringInputVisitor *siv = to_siv(v); |
334 | |
335 | assert(siv->lm == LM_NONE); |
336 | if (!strcasecmp(siv->string, "on" ) || |
337 | !strcasecmp(siv->string, "yes" ) || |
338 | !strcasecmp(siv->string, "true" )) { |
339 | *obj = true; |
340 | return; |
341 | } |
342 | if (!strcasecmp(siv->string, "off" ) || |
343 | !strcasecmp(siv->string, "no" ) || |
344 | !strcasecmp(siv->string, "false" )) { |
345 | *obj = false; |
346 | return; |
347 | } |
348 | |
349 | error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null" , |
350 | "boolean" ); |
351 | } |
352 | |
353 | static void parse_type_str(Visitor *v, const char *name, char **obj, |
354 | Error **errp) |
355 | { |
356 | StringInputVisitor *siv = to_siv(v); |
357 | |
358 | assert(siv->lm == LM_NONE); |
359 | *obj = g_strdup(siv->string); |
360 | } |
361 | |
362 | static void parse_type_number(Visitor *v, const char *name, double *obj, |
363 | Error **errp) |
364 | { |
365 | StringInputVisitor *siv = to_siv(v); |
366 | double val; |
367 | |
368 | assert(siv->lm == LM_NONE); |
369 | if (qemu_strtod_finite(siv->string, NULL, &val)) { |
370 | error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null" , |
371 | "number" ); |
372 | return; |
373 | } |
374 | |
375 | *obj = val; |
376 | } |
377 | |
378 | static void parse_type_null(Visitor *v, const char *name, QNull **obj, |
379 | Error **errp) |
380 | { |
381 | StringInputVisitor *siv = to_siv(v); |
382 | |
383 | assert(siv->lm == LM_NONE); |
384 | *obj = NULL; |
385 | |
386 | if (siv->string[0]) { |
387 | error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null" , |
388 | "null" ); |
389 | return; |
390 | } |
391 | |
392 | *obj = qnull(); |
393 | } |
394 | |
395 | static void string_input_free(Visitor *v) |
396 | { |
397 | StringInputVisitor *siv = to_siv(v); |
398 | |
399 | g_free(siv); |
400 | } |
401 | |
402 | Visitor *string_input_visitor_new(const char *str) |
403 | { |
404 | StringInputVisitor *v; |
405 | |
406 | assert(str); |
407 | v = g_malloc0(sizeof(*v)); |
408 | |
409 | v->visitor.type = VISITOR_INPUT; |
410 | v->visitor.type_int64 = parse_type_int64; |
411 | v->visitor.type_uint64 = parse_type_uint64; |
412 | v->visitor.type_size = parse_type_size; |
413 | v->visitor.type_bool = parse_type_bool; |
414 | v->visitor.type_str = parse_type_str; |
415 | v->visitor.type_number = parse_type_number; |
416 | v->visitor.type_null = parse_type_null; |
417 | v->visitor.start_list = start_list; |
418 | v->visitor.next_list = next_list; |
419 | v->visitor.check_list = check_list; |
420 | v->visitor.end_list = end_list; |
421 | v->visitor.free = string_input_free; |
422 | |
423 | v->string = str; |
424 | v->lm = LM_NONE; |
425 | return &v->visitor; |
426 | } |
427 | |