dcerpc-nt: add UNION_ALIGN_TO... helpers
[wireshark-sm.git] / wsutil / json_dumper.c
blob2761d37c08707f78899979f1df508a51ecc7cef4
1 /* json_dumper.c
2 * Routines for serializing data as JSON.
4 * Copyright 2018, Peter Wu <peter@lekensteyn.nl>
5 * Copyright (C) 2016 Jakub Zawadzki
7 * Wireshark - Network traffic analyzer
8 * By Gerald Combs <gerald@wireshark.org>
9 * Copyright 1998 Gerald Combs
11 * SPDX-License-Identifier: GPL-2.0-or-later
14 #include "config.h"
15 #define WS_LOG_DOMAIN LOG_DOMAIN_WSUTIL
17 #include <glib.h>
19 #include "json_dumper.h"
20 #include <math.h>
22 #include <wsutil/array.h>
23 #include <wsutil/wslog.h>
26 * json_dumper.state[current_depth] describes a nested element:
27 * - type: none/object/array/non-base64 value/base64 value
28 * - has_name: Whether the object member name was set.
30 * (A base64 value isn't really a nested element, but that's a
31 * convenient way of handling them, with a begin call that opens
32 * the string with a double-quote, one or more calls to convert
33 * raw bytes to base64 and add them to the value, and an end call
34 * that finishes the base64 encoding, adds any remaining raw bytes
35 * in base64 encoding, and closes the string with a double-quote.)
37 enum json_dumper_element_type {
38 JSON_DUMPER_TYPE_NONE = 0,
39 JSON_DUMPER_TYPE_VALUE = 1,
40 JSON_DUMPER_TYPE_OBJECT = 2,
41 JSON_DUMPER_TYPE_ARRAY = 3,
42 JSON_DUMPER_TYPE_BASE64 = 4,
44 #define JSON_DUMPER_TYPE(state) ((enum json_dumper_element_type)((state) & 7))
45 #define JSON_DUMPER_HAS_NAME (1 << 3)
47 static const char *json_dumper_element_type_names[] = {
48 [JSON_DUMPER_TYPE_NONE] = "none",
49 [JSON_DUMPER_TYPE_VALUE] = "value",
50 [JSON_DUMPER_TYPE_OBJECT] = "object",
51 [JSON_DUMPER_TYPE_ARRAY] = "array",
52 [JSON_DUMPER_TYPE_BASE64] = "base64"
54 #define NUM_JSON_DUMPER_ELEMENT_TYPE_NAMES array_length(json_dumper_element_type_names)
56 #define JSON_DUMPER_FLAGS_ERROR (1 << 16) /* Output flag: an error occurred. */
58 enum json_dumper_change {
59 JSON_DUMPER_BEGIN,
60 JSON_DUMPER_END,
61 JSON_DUMPER_SET_NAME,
62 JSON_DUMPER_SET_VALUE,
63 JSON_DUMPER_WRITE_BASE64,
64 JSON_DUMPER_FINISH,
67 /* JSON Dumper putc */
68 static void
69 jd_putc(const json_dumper *dumper, char c)
71 if (dumper->output_file) {
72 fputc(c, dumper->output_file);
75 if (dumper->output_string) {
76 g_string_append_c(dumper->output_string, c);
80 /* JSON Dumper puts */
81 static void
82 jd_puts(const json_dumper *dumper, const char *s)
84 if (dumper->output_file) {
85 fputs(s, dumper->output_file);
88 if (dumper->output_string) {
89 g_string_append(dumper->output_string, s);
93 static void
94 jd_puts_len(const json_dumper *dumper, const char *s, size_t len)
96 if (dumper->output_file) {
97 fwrite(s, 1, len, dumper->output_file);
100 if (dumper->output_string) {
101 g_string_append_len(dumper->output_string, s, len);
105 static void
106 jd_vprintf(const json_dumper *dumper, const char *format, va_list args)
108 if (dumper->output_file) {
109 vfprintf(dumper->output_file, format, args);
112 if (dumper->output_string) {
113 g_string_append_vprintf(dumper->output_string, format, args);
117 static void
118 json_puts_string(const json_dumper *dumper, const char *str, bool dot_to_underscore)
120 if (!str) {
121 jd_puts(dumper, "null");
122 return;
125 static const char json_cntrl[0x20][6] = {
126 "u0000", "u0001", "u0002", "u0003", "u0004", "u0005", "u0006", "u0007", "b", "t", "n", "u000b", "f", "r", "u000e", "u000f",
127 "u0010", "u0011", "u0012", "u0013", "u0014", "u0015", "u0016", "u0017", "u0018", "u0019", "u001a", "u001b", "u001c", "u001d", "u001e", "u001f"
130 jd_putc(dumper, '"');
131 for (int i = 0; str[i]; i++) {
132 if ((unsigned)str[i] < 0x20) {
133 jd_putc(dumper, '\\');
134 jd_puts(dumper, json_cntrl[(unsigned)str[i]]);
135 } else if (i > 0 && str[i - 1] == '<' && str[i] == '/') {
136 // Convert </script> to <\/script> to avoid breaking web pages.
137 jd_puts(dumper, "\\/");
138 } else {
139 if (str[i] == '\\' || str[i] == '"') {
140 jd_putc(dumper, '\\');
142 if (dot_to_underscore && str[i] == '.')
143 jd_putc(dumper, '_');
144 else
145 jd_putc(dumper, str[i]);
148 jd_putc(dumper, '"');
151 static inline uint8_t
152 json_dumper_get_prev_state(json_dumper *dumper)
154 unsigned depth = dumper->current_depth;
155 return depth != 0 ? dumper->state[depth - 1] : 0;
158 static inline uint8_t
159 json_dumper_get_curr_state(json_dumper *dumper)
161 unsigned depth = dumper->current_depth;
162 return dumper->state[depth];
166 * Called when a programming error is encountered where the JSON manipulation
167 * state got corrupted. This could happen when pairing the wrong begin/end
168 * calls, when writing multiple values for the same object, etc.
170 static void
171 json_dumper_bad(json_dumper *dumper, const char *what)
173 dumper->flags |= JSON_DUMPER_FLAGS_ERROR;
174 if ((dumper->flags & JSON_DUMPER_FLAGS_NO_DEBUG)) {
175 /* Console output can be slow, disable log calls to speed up fuzzing. */
177 * XXX - should this call abort()? If that flag isn't set,
178 * ws_error() wou;d call it; is there any point in continuing
179 * to do anything if we get here when fuzzing?
181 return;
184 if (dumper->output_file) {
185 fflush(dumper->output_file);
187 char unknown_curr_type_name[10+1];
188 char unknown_prev_type_name[10+1];
189 const char *curr_type_name, *prev_type_name;
190 uint8_t curr_state = json_dumper_get_curr_state(dumper);
191 uint8_t curr_type = JSON_DUMPER_TYPE(curr_state);
192 if (curr_type < NUM_JSON_DUMPER_ELEMENT_TYPE_NAMES) {
193 curr_type_name = json_dumper_element_type_names[curr_type];
194 } else {
195 snprintf(unknown_curr_type_name, sizeof unknown_curr_type_name, "%u", curr_type);
196 curr_type_name = unknown_curr_type_name;
198 if (dumper->current_depth != 0) {
199 uint8_t prev_state = json_dumper_get_prev_state(dumper);
200 uint8_t prev_type = JSON_DUMPER_TYPE(prev_state);
201 if (prev_type < NUM_JSON_DUMPER_ELEMENT_TYPE_NAMES) {
202 prev_type_name = json_dumper_element_type_names[prev_type];
203 } else {
204 snprintf(unknown_prev_type_name, sizeof unknown_prev_type_name, "%u", prev_type);
205 prev_type_name = unknown_prev_type_name;
207 } else {
208 prev_type_name = "(none)";
210 ws_error("json_dumper error: %s: current stack depth %u, current type %s, previous_type %s",
211 what, dumper->current_depth, curr_type_name, prev_type_name);
212 /* NOTREACHED */
215 static inline bool
216 json_dumper_stack_would_overflow(json_dumper *dumper)
218 if (dumper->current_depth + 1 >= JSON_DUMPER_MAX_DEPTH) {
219 json_dumper_bad(dumper, "JSON dumper stack overflow");
220 return true;
222 return false;
225 static inline bool
226 json_dumper_stack_would_underflow(json_dumper *dumper)
228 if (dumper->current_depth == 0) {
229 json_dumper_bad(dumper, "JSON dumper stack underflow");
230 return true;
232 return false;
236 * Checks that the dumper has not already had an error. Fail, and
237 * return false, to tell our caller not to do any more work, if it
238 * has.
240 static bool
241 json_dumper_check_previous_error(json_dumper *dumper)
243 if ((dumper->flags & JSON_DUMPER_FLAGS_ERROR)) {
244 json_dumper_bad(dumper, "previous corruption detected");
245 return false;
247 return true;
250 static void
251 print_newline_indent(const json_dumper *dumper, unsigned depth)
253 if ((dumper->flags & JSON_DUMPER_FLAGS_PRETTY_PRINT)) {
254 jd_putc(dumper, '\n');
255 for (unsigned i = 0; i < depth; i++) {
256 jd_puts(dumper, " ");
262 * Prints commas, newlines and indentation (if necessary). Used for array
263 * values, object names and normal values (strings, etc.).
265 static void
266 prepare_token(json_dumper *dumper)
268 if (dumper->current_depth == 0) {
269 // not part of an array or object.
270 return;
272 uint8_t prev_state = dumper->state[dumper->current_depth - 1];
274 // While processing the object value, reset the key state as it is consumed.
275 dumper->state[dumper->current_depth - 1] &= ~JSON_DUMPER_HAS_NAME;
277 switch (JSON_DUMPER_TYPE(prev_state)) {
278 case JSON_DUMPER_TYPE_OBJECT:
279 if ((prev_state & JSON_DUMPER_HAS_NAME)) {
280 // Object key already set, value follows. No indentation needed.
281 return;
283 break;
284 case JSON_DUMPER_TYPE_ARRAY:
285 break;
286 default:
287 // Initial values do not need indentation.
288 return;
291 uint8_t curr_state = json_dumper_get_curr_state(dumper);
292 if (curr_state != JSON_DUMPER_TYPE_NONE) {
293 jd_putc(dumper, ',');
295 print_newline_indent(dumper, dumper->current_depth);
299 * Common code to open an object/array/base64 value, printing
300 * an opening character.
302 * It also makes various correctness checks.
304 static bool
305 json_dumper_begin_nested_element(json_dumper *dumper, enum json_dumper_element_type type)
307 if (!json_dumper_check_previous_error(dumper)) {
308 return false;
311 /* Make sure we won't overflow the dumper stack */
312 if (json_dumper_stack_would_overflow(dumper)) {
313 return false;
316 prepare_token(dumper);
317 switch (type) {
318 case JSON_DUMPER_TYPE_OBJECT:
319 jd_putc(dumper, '{');
320 break;
321 case JSON_DUMPER_TYPE_ARRAY:
322 jd_putc(dumper, '[');
323 break;
324 case JSON_DUMPER_TYPE_BASE64:
325 dumper->base64_state = 0;
326 dumper->base64_save = 0;
328 jd_putc(dumper, '"');
329 break;
330 default:
331 json_dumper_bad(dumper, "beginning unknown nested element type");
332 return false;
335 dumper->state[dumper->current_depth] = type;
337 * Guaranteed not to overflow, as json_dumper_stack_would_overflow()
338 * returned false.
340 ++dumper->current_depth;
341 dumper->state[dumper->current_depth] = JSON_DUMPER_TYPE_NONE;
342 return true;
346 * Common code to close an object/array/base64 value, printing a
347 * closing character (and if necessary, it is preceded by newline
348 * and indentation).
350 * It also makes various correctness checks.
352 static bool
353 json_dumper_end_nested_element(json_dumper *dumper, enum json_dumper_element_type type)
355 if (!json_dumper_check_previous_error(dumper)) {
356 return false;
359 uint8_t prev_state = json_dumper_get_prev_state(dumper);
361 switch (type) {
362 case JSON_DUMPER_TYPE_OBJECT:
363 if (JSON_DUMPER_TYPE(prev_state) != JSON_DUMPER_TYPE_OBJECT) {
364 json_dumper_bad(dumper, "ending non-object nested item type as object");
365 return false;
367 break;
368 case JSON_DUMPER_TYPE_ARRAY:
369 if (JSON_DUMPER_TYPE(prev_state) != JSON_DUMPER_TYPE_ARRAY) {
370 json_dumper_bad(dumper, "ending non-array nested item type as array");
371 return false;
373 break;
374 case JSON_DUMPER_TYPE_BASE64:
375 if (JSON_DUMPER_TYPE(prev_state) != JSON_DUMPER_TYPE_BASE64) {
376 json_dumper_bad(dumper, "ending non-base64 nested item type as base64");
377 return false;
379 break;
380 default:
381 json_dumper_bad(dumper, "ending unknown nested element type");
382 return false;
385 if (prev_state & JSON_DUMPER_HAS_NAME) {
386 json_dumper_bad(dumper, "finishing object with last item having name but no value");
387 return false;
390 /* Make sure we won't underflow the dumper stack */
391 if (json_dumper_stack_would_underflow(dumper)) {
392 return false;
395 // if the object/array was non-empty, add a newline and indentation.
396 if (dumper->state[dumper->current_depth]) {
397 print_newline_indent(dumper, dumper->current_depth - 1);
400 switch (type) {
401 case JSON_DUMPER_TYPE_OBJECT:
402 jd_putc(dumper, '}');
403 break;
404 case JSON_DUMPER_TYPE_ARRAY:
405 jd_putc(dumper, ']');
406 break;
407 case JSON_DUMPER_TYPE_BASE64:
409 char buf[4];
410 size_t wrote;
412 wrote = g_base64_encode_close(false, buf, &dumper->base64_state, &dumper->base64_save);
413 jd_puts_len(dumper, buf, wrote);
415 jd_putc(dumper, '"');
416 break;
418 default:
419 json_dumper_bad(dumper, "ending unknown nested element type");
420 return false;
424 * Guaranteed not to underflow, as json_dumper_stack_would_underflow()
425 * returned false.
427 --dumper->current_depth;
428 return true;
431 void
432 json_dumper_begin_object(json_dumper *dumper)
434 json_dumper_begin_nested_element(dumper, JSON_DUMPER_TYPE_OBJECT);
437 void
438 json_dumper_set_member_name(json_dumper *dumper, const char *name)
440 if (!json_dumper_check_previous_error(dumper)) {
441 return;
444 uint8_t prev_state = json_dumper_get_prev_state(dumper);
446 /* Only object members, not array members, have names. */
447 if (JSON_DUMPER_TYPE(prev_state) != JSON_DUMPER_TYPE_OBJECT) {
448 json_dumper_bad(dumper, "setting name on non-object nested item type");
449 return;
451 /* An object member name can only be set once before its value is set. */
452 if (prev_state & JSON_DUMPER_HAS_NAME) {
453 json_dumper_bad(dumper, "setting name twice on an object member");
454 return;
457 prepare_token(dumper);
458 json_puts_string(dumper, name, dumper->flags & JSON_DUMPER_DOT_TO_UNDERSCORE);
459 jd_putc(dumper, ':');
460 if ((dumper->flags & JSON_DUMPER_FLAGS_PRETTY_PRINT)) {
461 jd_putc(dumper, ' ');
464 dumper->state[dumper->current_depth - 1] |= JSON_DUMPER_HAS_NAME;
467 void
468 json_dumper_end_object(json_dumper *dumper)
470 json_dumper_end_nested_element(dumper, JSON_DUMPER_TYPE_OBJECT);
473 void
474 json_dumper_begin_array(json_dumper *dumper)
476 json_dumper_begin_nested_element(dumper, JSON_DUMPER_TYPE_ARRAY);
479 void
480 json_dumper_end_array(json_dumper *dumper)
482 json_dumper_end_nested_element(dumper, JSON_DUMPER_TYPE_ARRAY);
485 static bool
486 json_dumper_setting_value_ok(json_dumper *dumper)
488 uint8_t prev_state = json_dumper_get_prev_state(dumper);
490 switch (JSON_DUMPER_TYPE(prev_state)) {
491 case JSON_DUMPER_TYPE_OBJECT:
493 * This value is part of an object. As such, it must
494 * have a name.
496 if (!(prev_state & JSON_DUMPER_HAS_NAME)) {
497 json_dumper_bad(dumper, "setting value of object member without a name");
498 return false;
500 break;
501 case JSON_DUMPER_TYPE_ARRAY:
503 * This value is part of an array. As such, it's not
504 * required to have a name (and shouldn't have a name;
505 * that's already been checked in json_dumper_set_member_name()).
507 break;
508 case JSON_DUMPER_TYPE_BASE64:
510 * We're in the middle of constructing a base64-encoded
511 * value. Only json_dumper_write_base64() can be used
512 * for that; we can't add individual values to it.
514 json_dumper_bad(dumper, "attempt to set value of base64 item to something not base64-encoded");
515 return false;
516 case JSON_DUMPER_TYPE_NONE:
517 case JSON_DUMPER_TYPE_VALUE:
519 uint8_t curr_state = json_dumper_get_curr_state(dumper);
520 switch (JSON_DUMPER_TYPE(curr_state)) {
521 case JSON_DUMPER_TYPE_NONE:
523 * We haven't put a value yet, so we can put one now.
525 break;
526 case JSON_DUMPER_TYPE_VALUE:
528 * This value isn't part of an object or array,
529 * and we've already put one value.
531 json_dumper_bad(dumper, "value not in object or array immediately follows another value");
532 return false;
533 case JSON_DUMPER_TYPE_OBJECT:
534 case JSON_DUMPER_TYPE_ARRAY:
535 case JSON_DUMPER_TYPE_BASE64:
537 * This should never be the case, no matter what
538 * our callers do:
540 * JSON_DUMPER_TYPE_OBJECT can be the previous
541 * type, meaning we're in the process of adding
542 * elements to an object, but it should never be
543 * the current type;
545 * JSON_DUMPER_TYPE_ARRAY can be the previous
546 * type, meaning we're in the process of adding
547 * elements to an array, but it should never be
548 * the current type;
550 * JSON_DUMPER_TYPE_BASE64 should only be the
551 * current type if we're in the middle of
552 * building a base64 value, in which case the
553 * previous type should also be JSON_DUMPER_TYPE_BASE64,
554 * but that's not the previous type.
556 json_dumper_bad(dumper, "internal error setting value - should not happen");
557 return false;
558 default:
559 json_dumper_bad(dumper, "internal error setting value, bad current state - should not happen");
560 return false;
562 break;
564 default:
565 json_dumper_bad(dumper, "internal error setting value, bad previous state - should not happen");
566 return false;
568 return true;
571 void
572 json_dumper_value_string(json_dumper *dumper, const char *value)
574 if (!json_dumper_check_previous_error(dumper)) {
575 return;
577 if (!json_dumper_setting_value_ok(dumper)) {
578 return;
581 prepare_token(dumper);
582 json_puts_string(dumper, value, false);
584 dumper->state[dumper->current_depth] = JSON_DUMPER_TYPE_VALUE;
587 void
588 json_dumper_value_double(json_dumper *dumper, double value)
590 if (!json_dumper_check_previous_error(dumper)) {
591 return;
594 if (!json_dumper_setting_value_ok(dumper)) {
595 return;
598 prepare_token(dumper);
599 char buffer[G_ASCII_DTOSTR_BUF_SIZE] = { 0 };
600 if (isfinite(value) && g_ascii_dtostr(buffer, G_ASCII_DTOSTR_BUF_SIZE, value) && buffer[0]) {
601 jd_puts(dumper, buffer);
602 } else {
603 jd_puts(dumper, "null");
606 dumper->state[dumper->current_depth] = JSON_DUMPER_TYPE_VALUE;
609 void
610 json_dumper_value_va_list(json_dumper *dumper, const char *format, va_list ap)
612 if (!json_dumper_check_previous_error(dumper)) {
613 return;
616 if (!json_dumper_setting_value_ok(dumper)) {
617 return;
620 prepare_token(dumper);
621 jd_vprintf(dumper, format, ap);
623 dumper->state[dumper->current_depth] = JSON_DUMPER_TYPE_VALUE;
626 void
627 json_dumper_value_anyf(json_dumper *dumper, const char *format, ...)
629 va_list ap;
631 va_start(ap, format);
632 json_dumper_value_va_list(dumper, format, ap);
633 va_end(ap);
636 bool
637 json_dumper_finish(json_dumper *dumper)
639 if (!json_dumper_check_previous_error(dumper)) {
640 return false;
643 if (dumper->current_depth != 0) {
644 json_dumper_bad(dumper, "JSON dumper stack not empty at finish");
645 return false;
648 jd_putc(dumper, '\n');
649 dumper->state[0] = JSON_DUMPER_TYPE_NONE;
650 return true;
653 void
654 json_dumper_begin_base64(json_dumper *dumper)
656 json_dumper_begin_nested_element(dumper, JSON_DUMPER_TYPE_BASE64);
659 void
660 json_dumper_write_base64(json_dumper* dumper, const unsigned char *data, size_t len)
662 if (!json_dumper_check_previous_error(dumper)) {
663 return;
666 uint8_t prev_state = json_dumper_get_prev_state(dumper);
668 if (JSON_DUMPER_TYPE(prev_state) != JSON_DUMPER_TYPE_BASE64) {
669 json_dumper_bad(dumper, "writing base64 data to a non-base64 value");
670 return;
673 #define CHUNK_SIZE 1024
674 char buf[(CHUNK_SIZE / 3 + 1) * 4 + 4];
676 while (len > 0) {
677 size_t chunk_size = len < CHUNK_SIZE ? len : CHUNK_SIZE;
678 size_t output_size = g_base64_encode_step(data, chunk_size, false, buf, &dumper->base64_state, &dumper->base64_save);
679 jd_puts_len(dumper, buf, output_size);
680 data += chunk_size;
681 len -= chunk_size;
684 dumper->state[dumper->current_depth] = JSON_DUMPER_TYPE_BASE64;
687 void
688 json_dumper_end_base64(json_dumper *dumper)
690 json_dumper_end_nested_element(dumper, JSON_DUMPER_TYPE_BASE64);