123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567 |
- /*
- * JavaScript Object Notation (JSON) parser (RFC7159)
- * Copyright (c) 2017, Qualcomm Atheros, Inc.
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
- #include "includes.h"
- #include "common.h"
- #include "base64.h"
- #include "json.h"
- #define JSON_MAX_DEPTH 10
- #define JSON_MAX_TOKENS 500
- void json_escape_string(char *txt, size_t maxlen, const char *data, size_t len)
- {
- char *end = txt + maxlen;
- size_t i;
- for (i = 0; i < len; i++) {
- if (txt + 4 >= end)
- break;
- switch (data[i]) {
- case '\"':
- *txt++ = '\\';
- *txt++ = '\"';
- break;
- case '\\':
- *txt++ = '\\';
- *txt++ = '\\';
- break;
- case '\n':
- *txt++ = '\\';
- *txt++ = 'n';
- break;
- case '\r':
- *txt++ = '\\';
- *txt++ = 'r';
- break;
- case '\t':
- *txt++ = '\\';
- *txt++ = 't';
- break;
- default:
- if (data[i] >= 32 && data[i] <= 126) {
- *txt++ = data[i];
- } else {
- txt += os_snprintf(txt, end - txt, "\\u%04x",
- data[i]);
- }
- break;
- }
- }
- *txt = '\0';
- }
- static char * json_parse_string(const char **json_pos, const char *end)
- {
- const char *pos = *json_pos;
- char *str, *spos, *s_end;
- size_t max_len, buf_len;
- u8 bin[2];
- pos++; /* skip starting quote */
- max_len = end - pos + 1;
- buf_len = max_len > 10 ? 10 : max_len;
- str = os_malloc(buf_len);
- if (!str)
- return NULL;
- spos = str;
- s_end = str + buf_len;
- for (; pos < end; pos++) {
- if (buf_len < max_len && s_end - spos < 3) {
- char *tmp;
- int idx;
- idx = spos - str;
- buf_len *= 2;
- if (buf_len > max_len)
- buf_len = max_len;
- tmp = os_realloc(str, buf_len);
- if (!tmp)
- goto fail;
- str = tmp;
- spos = str + idx;
- s_end = str + buf_len;
- }
- switch (*pos) {
- case '\"': /* end string */
- *spos = '\0';
- /* caller will move to the next position */
- *json_pos = pos;
- return str;
- case '\\':
- pos++;
- switch (*pos) {
- case '"':
- case '\\':
- case '/':
- *spos++ = *pos;
- break;
- case 'n':
- *spos++ = '\n';
- break;
- case 'r':
- *spos++ = '\r';
- break;
- case 't':
- *spos++ = '\t';
- break;
- case 'u':
- if (end - pos < 5 ||
- hexstr2bin(pos + 1, bin, 2) < 0 ||
- bin[1] == 0x00) {
- wpa_printf(MSG_DEBUG,
- "JSON: Invalid \\u escape");
- goto fail;
- }
- if (bin[0] == 0x00) {
- *spos++ = bin[1];
- } else {
- *spos++ = bin[0];
- *spos++ = bin[1];
- }
- pos += 4;
- break;
- default:
- wpa_printf(MSG_DEBUG,
- "JSON: Unknown escape '%c'", *pos);
- goto fail;
- }
- break;
- default:
- *spos++ = *pos;
- break;
- }
- }
- fail:
- os_free(str);
- return NULL;
- }
- static int json_parse_number(const char **json_pos, const char *end,
- int *ret_val)
- {
- const char *pos = *json_pos;
- size_t len;
- char *str;
- for (; pos < end; pos++) {
- if (*pos != '-' && (*pos < '0' || *pos > '9')) {
- pos--;
- break;
- }
- }
- if (pos < *json_pos)
- return -1;
- len = pos - *json_pos + 1;
- str = os_malloc(len + 1);
- if (!str)
- return -1;
- os_memcpy(str, *json_pos, len);
- str[len] = '\0';
- *ret_val = atoi(str);
- os_free(str);
- *json_pos = pos;
- return 0;
- }
- static int json_check_tree_state(struct json_token *token)
- {
- if (!token)
- return 0;
- if (json_check_tree_state(token->child) < 0 ||
- json_check_tree_state(token->sibling) < 0)
- return -1;
- if (token->state != JSON_COMPLETED) {
- wpa_printf(MSG_DEBUG,
- "JSON: Unexpected token state %d (name=%s type=%d)",
- token->state, token->name ? token->name : "N/A",
- token->type);
- return -1;
- }
- return 0;
- }
- static struct json_token * json_alloc_token(unsigned int *tokens)
- {
- (*tokens)++;
- if (*tokens > JSON_MAX_TOKENS) {
- wpa_printf(MSG_DEBUG, "JSON: Maximum token limit exceeded");
- return NULL;
- }
- return os_zalloc(sizeof(struct json_token));
- }
- struct json_token * json_parse(const char *data, size_t data_len)
- {
- struct json_token *root = NULL, *curr_token = NULL, *token = NULL;
- const char *pos, *end;
- char *str;
- int num;
- unsigned int depth = 0;
- unsigned int tokens = 0;
- pos = data;
- end = data + data_len;
- for (; pos < end; pos++) {
- switch (*pos) {
- case '[': /* start array */
- case '{': /* start object */
- if (!curr_token) {
- token = json_alloc_token(&tokens);
- if (!token)
- goto fail;
- } else if (curr_token->state == JSON_WAITING_VALUE) {
- token = curr_token;
- } else if (curr_token->parent &&
- curr_token->parent->type == JSON_ARRAY &&
- curr_token->parent->state == JSON_STARTED &&
- curr_token->state == JSON_EMPTY) {
- token = curr_token;
- } else {
- wpa_printf(MSG_DEBUG,
- "JSON: Invalid state for start array/object");
- goto fail;
- }
- depth++;
- if (depth > JSON_MAX_DEPTH) {
- wpa_printf(MSG_DEBUG,
- "JSON: Max depth exceeded");
- goto fail;
- }
- token->type = *pos == '[' ? JSON_ARRAY : JSON_OBJECT;
- token->state = JSON_STARTED;
- token->child = json_alloc_token(&tokens);
- if (!token->child)
- goto fail;
- curr_token = token->child;
- curr_token->parent = token;
- curr_token->state = JSON_EMPTY;
- break;
- case ']': /* end array */
- case '}': /* end object */
- if (!curr_token || !curr_token->parent ||
- curr_token->parent->state != JSON_STARTED) {
- wpa_printf(MSG_DEBUG,
- "JSON: Invalid state for end array/object");
- goto fail;
- }
- depth--;
- curr_token = curr_token->parent;
- if ((*pos == ']' &&
- curr_token->type != JSON_ARRAY) ||
- (*pos == '}' &&
- curr_token->type != JSON_OBJECT)) {
- wpa_printf(MSG_DEBUG,
- "JSON: Array/Object mismatch");
- goto fail;
- }
- if (curr_token->child->state == JSON_EMPTY &&
- !curr_token->child->child &&
- !curr_token->child->sibling) {
- /* Remove pending child token since the
- * array/object was empty. */
- json_free(curr_token->child);
- curr_token->child = NULL;
- }
- curr_token->state = JSON_COMPLETED;
- break;
- case '\"': /* string */
- str = json_parse_string(&pos, end);
- if (!str)
- goto fail;
- if (!curr_token) {
- token = json_alloc_token(&tokens);
- if (!token)
- goto fail;
- token->type = JSON_STRING;
- token->string = str;
- token->state = JSON_COMPLETED;
- } else if (curr_token->parent &&
- curr_token->parent->type == JSON_ARRAY &&
- curr_token->parent->state == JSON_STARTED &&
- curr_token->state == JSON_EMPTY) {
- curr_token->string = str;
- curr_token->state = JSON_COMPLETED;
- curr_token->type = JSON_STRING;
- wpa_printf(MSG_MSGDUMP,
- "JSON: String value: '%s'",
- curr_token->string);
- } else if (curr_token->state == JSON_EMPTY) {
- curr_token->type = JSON_VALUE;
- curr_token->name = str;
- curr_token->state = JSON_STARTED;
- } else if (curr_token->state == JSON_WAITING_VALUE) {
- curr_token->string = str;
- curr_token->state = JSON_COMPLETED;
- curr_token->type = JSON_STRING;
- wpa_printf(MSG_MSGDUMP,
- "JSON: String value: '%s' = '%s'",
- curr_token->name,
- curr_token->string);
- } else {
- wpa_printf(MSG_DEBUG,
- "JSON: Invalid state for a string");
- os_free(str);
- goto fail;
- }
- break;
- case ' ':
- case '\t':
- case '\r':
- case '\n':
- /* ignore whitespace */
- break;
- case ':': /* name/value separator */
- if (!curr_token || curr_token->state != JSON_STARTED)
- goto fail;
- curr_token->state = JSON_WAITING_VALUE;
- break;
- case ',': /* member separator */
- if (!curr_token)
- goto fail;
- curr_token->sibling = json_alloc_token(&tokens);
- if (!curr_token->sibling)
- goto fail;
- curr_token->sibling->parent = curr_token->parent;
- curr_token = curr_token->sibling;
- curr_token->state = JSON_EMPTY;
- break;
- case 't': /* true */
- case 'f': /* false */
- case 'n': /* null */
- if (!((end - pos >= 4 &&
- os_strncmp(pos, "true", 4) == 0) ||
- (end - pos >= 5 &&
- os_strncmp(pos, "false", 5) == 0) ||
- (end - pos >= 4 &&
- os_strncmp(pos, "null", 4) == 0))) {
- wpa_printf(MSG_DEBUG,
- "JSON: Invalid literal name");
- goto fail;
- }
- if (!curr_token) {
- token = json_alloc_token(&tokens);
- if (!token)
- goto fail;
- curr_token = token;
- } else if (curr_token->state == JSON_WAITING_VALUE) {
- wpa_printf(MSG_MSGDUMP,
- "JSON: Literal name: '%s' = %c",
- curr_token->name, *pos);
- } else if (curr_token->parent &&
- curr_token->parent->type == JSON_ARRAY &&
- curr_token->parent->state == JSON_STARTED &&
- curr_token->state == JSON_EMPTY) {
- wpa_printf(MSG_MSGDUMP,
- "JSON: Literal name: %c", *pos);
- } else {
- wpa_printf(MSG_DEBUG,
- "JSON: Invalid state for a literal name");
- goto fail;
- }
- switch (*pos) {
- case 't':
- curr_token->type = JSON_BOOLEAN;
- curr_token->number = 1;
- pos += 3;
- break;
- case 'f':
- curr_token->type = JSON_BOOLEAN;
- curr_token->number = 0;
- pos += 4;
- break;
- case 'n':
- curr_token->type = JSON_NULL;
- pos += 3;
- break;
- }
- curr_token->state = JSON_COMPLETED;
- break;
- case '-':
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- /* number */
- if (json_parse_number(&pos, end, &num) < 0)
- goto fail;
- if (!curr_token) {
- token = json_alloc_token(&tokens);
- if (!token)
- goto fail;
- token->type = JSON_NUMBER;
- token->number = num;
- token->state = JSON_COMPLETED;
- } else if (curr_token->state == JSON_WAITING_VALUE) {
- curr_token->number = num;
- curr_token->state = JSON_COMPLETED;
- curr_token->type = JSON_NUMBER;
- wpa_printf(MSG_MSGDUMP,
- "JSON: Number value: '%s' = '%d'",
- curr_token->name,
- curr_token->number);
- } else if (curr_token->parent &&
- curr_token->parent->type == JSON_ARRAY &&
- curr_token->parent->state == JSON_STARTED &&
- curr_token->state == JSON_EMPTY) {
- curr_token->number = num;
- curr_token->state = JSON_COMPLETED;
- curr_token->type = JSON_NUMBER;
- wpa_printf(MSG_MSGDUMP,
- "JSON: Number value: %d",
- curr_token->number);
- } else {
- wpa_printf(MSG_DEBUG,
- "JSON: Invalid state for a number");
- goto fail;
- }
- break;
- default:
- wpa_printf(MSG_DEBUG,
- "JSON: Unexpected JSON character: %c", *pos);
- goto fail;
- }
- if (!root)
- root = token;
- if (!curr_token)
- curr_token = token;
- }
- if (json_check_tree_state(root) < 0) {
- wpa_printf(MSG_DEBUG, "JSON: Incomplete token in the tree");
- goto fail;
- }
- return root;
- fail:
- wpa_printf(MSG_DEBUG, "JSON: Parsing failed");
- json_free(root);
- return NULL;
- }
- void json_free(struct json_token *json)
- {
- if (!json)
- return;
- json_free(json->child);
- json_free(json->sibling);
- os_free(json->name);
- os_free(json->string);
- os_free(json);
- }
- struct json_token * json_get_member(struct json_token *json, const char *name)
- {
- struct json_token *token, *ret = NULL;
- if (!json || json->type != JSON_OBJECT)
- return NULL;
- /* Return last matching entry */
- for (token = json->child; token; token = token->sibling) {
- if (token->name && os_strcmp(token->name, name) == 0)
- ret = token;
- }
- return ret;
- }
- struct wpabuf * json_get_member_base64url(struct json_token *json,
- const char *name)
- {
- struct json_token *token;
- unsigned char *buf;
- size_t buflen;
- struct wpabuf *ret;
- token = json_get_member(json, name);
- if (!token || token->type != JSON_STRING)
- return NULL;
- buf = base64_url_decode((const unsigned char *) token->string,
- os_strlen(token->string), &buflen);
- if (!buf)
- return NULL;
- ret = wpabuf_alloc_ext_data(buf, buflen);
- if (!ret)
- os_free(buf);
- return ret;
- }
- static const char * json_type_str(enum json_type type)
- {
- switch (type) {
- case JSON_VALUE:
- return "VALUE";
- case JSON_OBJECT:
- return "OBJECT";
- case JSON_ARRAY:
- return "ARRAY";
- case JSON_STRING:
- return "STRING";
- case JSON_NUMBER:
- return "NUMBER";
- case JSON_BOOLEAN:
- return "BOOLEAN";
- case JSON_NULL:
- return "NULL";
- }
- return "??";
- }
- static void json_print_token(struct json_token *token, int depth,
- char *buf, size_t buflen)
- {
- size_t len;
- int ret;
- if (!token)
- return;
- len = os_strlen(buf);
- ret = os_snprintf(buf + len, buflen - len, "[%d:%s:%s]",
- depth, json_type_str(token->type),
- token->name ? token->name : "");
- if (os_snprintf_error(buflen - len, ret)) {
- buf[len] = '\0';
- return;
- }
- json_print_token(token->child, depth + 1, buf, buflen);
- json_print_token(token->sibling, depth, buf, buflen);
- }
- void json_print_tree(struct json_token *root, char *buf, size_t buflen)
- {
- buf[0] = '\0';
- json_print_token(root, 1, buf, buflen);
- }
|