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
15 #define WS_LOG_DOMAIN LOG_DOMAIN_WSUTIL
19 #include "json_dumper.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
{
62 JSON_DUMPER_SET_VALUE
,
63 JSON_DUMPER_WRITE_BASE64
,
67 /* JSON Dumper putc */
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 */
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
);
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
);
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
);
118 json_puts_string(const json_dumper
*dumper
, const char *str
, bool dot_to_underscore
)
121 jd_puts(dumper
, "null");
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
, "\\/");
139 if (str
[i
] == '\\' || str
[i
] == '"') {
140 jd_putc(dumper
, '\\');
142 if (dot_to_underscore
&& str
[i
] == '.')
143 jd_putc(dumper
, '_');
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.
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?
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
];
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
];
204 snprintf(unknown_prev_type_name
, sizeof unknown_prev_type_name
, "%u", prev_type
);
205 prev_type_name
= unknown_prev_type_name
;
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
);
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");
226 json_dumper_stack_would_underflow(json_dumper
*dumper
)
228 if (dumper
->current_depth
== 0) {
229 json_dumper_bad(dumper
, "JSON dumper stack underflow");
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
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");
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.).
266 prepare_token(json_dumper
*dumper
)
268 if (dumper
->current_depth
== 0) {
269 // not part of an array or object.
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.
284 case JSON_DUMPER_TYPE_ARRAY
:
287 // Initial values do not need indentation.
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.
305 json_dumper_begin_nested_element(json_dumper
*dumper
, enum json_dumper_element_type type
)
307 if (!json_dumper_check_previous_error(dumper
)) {
311 /* Make sure we won't overflow the dumper stack */
312 if (json_dumper_stack_would_overflow(dumper
)) {
316 prepare_token(dumper
);
318 case JSON_DUMPER_TYPE_OBJECT
:
319 jd_putc(dumper
, '{');
321 case JSON_DUMPER_TYPE_ARRAY
:
322 jd_putc(dumper
, '[');
324 case JSON_DUMPER_TYPE_BASE64
:
325 dumper
->base64_state
= 0;
326 dumper
->base64_save
= 0;
328 jd_putc(dumper
, '"');
331 json_dumper_bad(dumper
, "beginning unknown nested element type");
335 dumper
->state
[dumper
->current_depth
] = type
;
337 * Guaranteed not to overflow, as json_dumper_stack_would_overflow()
340 ++dumper
->current_depth
;
341 dumper
->state
[dumper
->current_depth
] = JSON_DUMPER_TYPE_NONE
;
346 * Common code to close an object/array/base64 value, printing a
347 * closing character (and if necessary, it is preceded by newline
350 * It also makes various correctness checks.
353 json_dumper_end_nested_element(json_dumper
*dumper
, enum json_dumper_element_type type
)
355 if (!json_dumper_check_previous_error(dumper
)) {
359 uint8_t prev_state
= json_dumper_get_prev_state(dumper
);
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");
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");
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");
381 json_dumper_bad(dumper
, "ending unknown nested element type");
385 if (prev_state
& JSON_DUMPER_HAS_NAME
) {
386 json_dumper_bad(dumper
, "finishing object with last item having name but no value");
390 /* Make sure we won't underflow the dumper stack */
391 if (json_dumper_stack_would_underflow(dumper
)) {
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);
401 case JSON_DUMPER_TYPE_OBJECT
:
402 jd_putc(dumper
, '}');
404 case JSON_DUMPER_TYPE_ARRAY
:
405 jd_putc(dumper
, ']');
407 case JSON_DUMPER_TYPE_BASE64
:
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
, '"');
419 json_dumper_bad(dumper
, "ending unknown nested element type");
424 * Guaranteed not to underflow, as json_dumper_stack_would_underflow()
427 --dumper
->current_depth
;
432 json_dumper_begin_object(json_dumper
*dumper
)
434 json_dumper_begin_nested_element(dumper
, JSON_DUMPER_TYPE_OBJECT
);
438 json_dumper_set_member_name(json_dumper
*dumper
, const char *name
)
440 if (!json_dumper_check_previous_error(dumper
)) {
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");
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");
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
;
468 json_dumper_end_object(json_dumper
*dumper
)
470 json_dumper_end_nested_element(dumper
, JSON_DUMPER_TYPE_OBJECT
);
474 json_dumper_begin_array(json_dumper
*dumper
)
476 json_dumper_begin_nested_element(dumper
, JSON_DUMPER_TYPE_ARRAY
);
480 json_dumper_end_array(json_dumper
*dumper
)
482 json_dumper_end_nested_element(dumper
, JSON_DUMPER_TYPE_ARRAY
);
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
496 if (!(prev_state
& JSON_DUMPER_HAS_NAME
)) {
497 json_dumper_bad(dumper
, "setting value of object member without a name");
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()).
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");
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.
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");
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
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
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
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");
559 json_dumper_bad(dumper
, "internal error setting value, bad current state - should not happen");
565 json_dumper_bad(dumper
, "internal error setting value, bad previous state - should not happen");
572 json_dumper_value_string(json_dumper
*dumper
, const char *value
)
574 if (!json_dumper_check_previous_error(dumper
)) {
577 if (!json_dumper_setting_value_ok(dumper
)) {
581 prepare_token(dumper
);
582 json_puts_string(dumper
, value
, false);
584 dumper
->state
[dumper
->current_depth
] = JSON_DUMPER_TYPE_VALUE
;
588 json_dumper_value_double(json_dumper
*dumper
, double value
)
590 if (!json_dumper_check_previous_error(dumper
)) {
594 if (!json_dumper_setting_value_ok(dumper
)) {
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
);
603 jd_puts(dumper
, "null");
606 dumper
->state
[dumper
->current_depth
] = JSON_DUMPER_TYPE_VALUE
;
610 json_dumper_value_va_list(json_dumper
*dumper
, const char *format
, va_list ap
)
612 if (!json_dumper_check_previous_error(dumper
)) {
616 if (!json_dumper_setting_value_ok(dumper
)) {
620 prepare_token(dumper
);
621 jd_vprintf(dumper
, format
, ap
);
623 dumper
->state
[dumper
->current_depth
] = JSON_DUMPER_TYPE_VALUE
;
627 json_dumper_value_anyf(json_dumper
*dumper
, const char *format
, ...)
631 va_start(ap
, format
);
632 json_dumper_value_va_list(dumper
, format
, ap
);
637 json_dumper_finish(json_dumper
*dumper
)
639 if (!json_dumper_check_previous_error(dumper
)) {
643 if (dumper
->current_depth
!= 0) {
644 json_dumper_bad(dumper
, "JSON dumper stack not empty at finish");
648 jd_putc(dumper
, '\n');
649 dumper
->state
[0] = JSON_DUMPER_TYPE_NONE
;
654 json_dumper_begin_base64(json_dumper
*dumper
)
656 json_dumper_begin_nested_element(dumper
, JSON_DUMPER_TYPE_BASE64
);
660 json_dumper_write_base64(json_dumper
* dumper
, const unsigned char *data
, size_t len
)
662 if (!json_dumper_check_previous_error(dumper
)) {
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");
673 #define CHUNK_SIZE 1024
674 char buf
[(CHUNK_SIZE
/ 3 + 1) * 4 + 4];
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
);
684 dumper
->state
[dumper
->current_depth
] = JSON_DUMPER_TYPE_BASE64
;
688 json_dumper_end_base64(json_dumper
*dumper
)
690 json_dumper_end_nested_element(dumper
, JSON_DUMPER_TYPE_BASE64
);