/* * 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 "qemu-common.h" #include "qapi/qmp/json-lexer.h" #include "qapi/qmp/json-streamer.h" #define MAX_TOKEN_SIZE (64ULL << 20) #define MAX_TOKEN_COUNT (2ULL << 20) #define MAX_NESTING (1ULL << 10) static void json_message_free_token(void *token, void *opaque) { g_free(token); } static void json_message_free_tokens(JSONMessageParser *parser) { if (parser->tokens) { g_queue_foreach(parser->tokens, json_message_free_token, NULL); g_queue_free(parser->tokens); parser->tokens = NULL; } } static void json_message_process_token(JSONLexer *lexer, GString *input, JSONTokenType type, int x, int y) { JSONMessageParser *parser = container_of(lexer, JSONMessageParser, lexer); JSONToken *token; GQueue *tokens; 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; default: break; } token = g_malloc(sizeof(JSONToken) + input->len + 1); token->type = type; memcpy(token->str, input->str, input->len); token->str[input->len] = 0; token->x = x; token->y = y; parser->token_size += input->len; g_queue_push_tail(parser->tokens, token); if (type == JSON_ERROR) { goto out_emit_bad; } else if (parser->brace_count < 0 || parser->bracket_count < 0 || (parser->brace_count == 0 && parser->bracket_count == 0)) { goto out_emit; } else if (parser->token_size > MAX_TOKEN_SIZE || g_queue_get_length(parser->tokens) > MAX_TOKEN_COUNT || parser->bracket_count + parser->brace_count > MAX_NESTING) { /* Security consideration, we limit total memory allocated per object * and the maximum recursion depth that a message can force. */ goto out_emit_bad; } return; out_emit_bad: /* * Clear out token list and tell the parser to emit an error * indication by passing it a NULL list */ json_message_free_tokens(parser); out_emit: /* send current list of tokens to parser and reset tokenizer */ parser->brace_count = 0; parser->bracket_count = 0; /* parser->emit takes ownership of parser->tokens. Remove our own * reference to parser->tokens before handing it out to parser->emit. */ tokens = parser->tokens; parser->tokens = g_queue_new(); parser->emit(parser, tokens); parser->token_size = 0; } void json_message_parser_init(JSONMessageParser *parser, void (*func)(JSONMessageParser *, GQueue *)) { parser->emit = func; parser->brace_count = 0; parser->bracket_count = 0; parser->tokens = g_queue_new(); parser->token_size = 0; json_lexer_init(&parser->lexer, json_message_process_token); } int json_message_parser_feed(JSONMessageParser *parser, const char *buffer, size_t size) { return json_lexer_feed(&parser->lexer, buffer, size); } int json_message_parser_flush(JSONMessageParser *parser) { return json_lexer_flush(&parser->lexer); } void json_message_parser_destroy(JSONMessageParser *parser) { json_lexer_destroy(&parser->lexer); json_message_free_tokens(parser); }