aboutsummaryrefslogtreecommitdiffstats
path: root/qobject/json-streamer.c
diff options
context:
space:
mode:
Diffstat (limited to 'qobject/json-streamer.c')
-rw-r--r--qobject/json-streamer.c134
1 files changed, 134 insertions, 0 deletions
diff --git a/qobject/json-streamer.c b/qobject/json-streamer.c
new file mode 100644
index 000000000..b93d97b99
--- /dev/null
+++ b/qobject/json-streamer.c
@@ -0,0 +1,134 @@
+/*
+ * JSON streaming support
+ *
+ * Copyright IBM, Corp. 2009
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "json-parser-int.h"
+
+#define MAX_TOKEN_SIZE (64ULL << 20)
+#define MAX_TOKEN_COUNT (2ULL << 20)
+#define MAX_NESTING (1 << 10)
+
+static void json_message_free_tokens(JSONMessageParser *parser)
+{
+ JSONToken *token;
+
+ while ((token = g_queue_pop_head(&parser->tokens))) {
+ g_free(token);
+ }
+}
+
+void json_message_process_token(JSONLexer *lexer, GString *input,
+ JSONTokenType type, int x, int y)
+{
+ JSONMessageParser *parser = container_of(lexer, JSONMessageParser, lexer);
+ QObject *json = NULL;
+ Error *err = NULL;
+ JSONToken *token;
+
+ switch (type) {
+ case JSON_LCURLY:
+ parser->brace_count++;
+ break;
+ case JSON_RCURLY:
+ parser->brace_count--;
+ break;
+ case JSON_LSQUARE:
+ parser->bracket_count++;
+ break;
+ case JSON_RSQUARE:
+ parser->bracket_count--;
+ break;
+ case JSON_ERROR:
+ error_setg(&err, "JSON parse error, stray '%s'", input->str);
+ goto out_emit;
+ case JSON_END_OF_INPUT:
+ if (g_queue_is_empty(&parser->tokens)) {
+ return;
+ }
+ json = json_parser_parse(&parser->tokens, parser->ap, &err);
+ goto out_emit;
+ default:
+ break;
+ }
+
+ /*
+ * Security consideration, we limit total memory allocated per object
+ * and the maximum recursion depth that a message can force.
+ */
+ if (parser->token_size + input->len + 1 > MAX_TOKEN_SIZE) {
+ error_setg(&err, "JSON token size limit exceeded");
+ goto out_emit;
+ }
+ if (g_queue_get_length(&parser->tokens) + 1 > MAX_TOKEN_COUNT) {
+ error_setg(&err, "JSON token count limit exceeded");
+ goto out_emit;
+ }
+ if (parser->bracket_count + parser->brace_count > MAX_NESTING) {
+ error_setg(&err, "JSON nesting depth limit exceeded");
+ goto out_emit;
+ }
+
+ token = json_token(type, x, y, input);
+ parser->token_size += input->len;
+
+ g_queue_push_tail(&parser->tokens, token);
+
+ if ((parser->brace_count > 0 || parser->bracket_count > 0)
+ && parser->brace_count >= 0 && parser->bracket_count >= 0) {
+ return;
+ }
+
+ json = json_parser_parse(&parser->tokens, parser->ap, &err);
+
+out_emit:
+ parser->brace_count = 0;
+ parser->bracket_count = 0;
+ json_message_free_tokens(parser);
+ parser->token_size = 0;
+ parser->emit(parser->opaque, json, err);
+}
+
+void json_message_parser_init(JSONMessageParser *parser,
+ void (*emit)(void *opaque, QObject *json,
+ Error *err),
+ void *opaque, va_list *ap)
+{
+ parser->emit = emit;
+ parser->opaque = opaque;
+ parser->ap = ap;
+ parser->brace_count = 0;
+ parser->bracket_count = 0;
+ g_queue_init(&parser->tokens);
+ parser->token_size = 0;
+
+ json_lexer_init(&parser->lexer, !!ap);
+}
+
+void json_message_parser_feed(JSONMessageParser *parser,
+ const char *buffer, size_t size)
+{
+ json_lexer_feed(&parser->lexer, buffer, size);
+}
+
+void json_message_parser_flush(JSONMessageParser *parser)
+{
+ json_lexer_flush(&parser->lexer);
+ assert(g_queue_is_empty(&parser->tokens));
+}
+
+void json_message_parser_destroy(JSONMessageParser *parser)
+{
+ json_lexer_destroy(&parser->lexer);
+ json_message_free_tokens(parser);
+}