From eaa8b96541ac8a82b78060008956968d4ca36953 Mon Sep 17 00:00:00 2001 From: Reyk Floeter Date: Mon, 7 May 2018 12:23:02 +0200 Subject: [PATCH] Add missing json files --- agent/jsmn.c | 332 +++++++++++++++++++++++++++++++++++++++++++++++++++ agent/jsmn.h | 97 +++++++++++++++ agent/json.c | 319 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 748 insertions(+) create mode 100644 agent/jsmn.c create mode 100644 agent/jsmn.h create mode 100644 agent/json.c diff --git a/agent/jsmn.c b/agent/jsmn.c new file mode 100644 index 0000000..26676fa --- /dev/null +++ b/agent/jsmn.c @@ -0,0 +1,332 @@ +/* + Copyright (c) 2010 Serge A. Zaitsev + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE.* + */ +#include "jsmn.h" + +/** + * Allocates a fresh unused token from the token pull. + */ +static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, + jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *tok; + if (parser->toknext >= num_tokens) { + return NULL; + } + tok = &tokens[parser->toknext++]; + tok->start = tok->end = -1; + tok->size = 0; +#ifdef JSMN_PARENT_LINKS + tok->parent = -1; +#endif + return tok; +} + +/** + * Fills token type and boundaries. + */ +static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, + int start, int end) { + token->type = type; + token->start = start; + token->end = end; + token->size = 0; +} + +/** + * Fills next available token with JSON primitive. + */ +static int jsmn_parse_primitive(jsmn_parser *parser, const char *js, + size_t len, jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *token; + int start; + + start = parser->pos; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + switch (js[parser->pos]) { +#ifndef JSMN_STRICT + /* In strict mode primitive must be followed by "," or "}" or "]" */ + case ':': +#endif + case '\t' : case '\r' : case '\n' : case ' ' : + case ',' : case ']' : case '}' : + goto found; + } + if (js[parser->pos] < 32 || js[parser->pos] >= 127) { + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } +#ifdef JSMN_STRICT + /* In strict mode primitive must be followed by a comma/object/array */ + parser->pos = start; + return JSMN_ERROR_PART; +#endif + +found: + if (tokens == NULL) { + parser->pos--; + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + parser->pos--; + return 0; +} + +/** + * Fills next token with JSON string. + */ +static int jsmn_parse_string(jsmn_parser *parser, const char *js, + size_t len, jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *token; + + int start = parser->pos; + + parser->pos++; + + /* Skip starting quote */ + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c = js[parser->pos]; + + /* Quote: end of string */ + if (c == '\"') { + if (tokens == NULL) { + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + return 0; + } + + /* Backslash: Quoted symbol expected */ + if (c == '\\' && parser->pos + 1 < len) { + int i; + parser->pos++; + switch (js[parser->pos]) { + /* Allowed escaped symbols */ + case '\"': case '/' : case '\\' : case 'b' : + case 'f' : case 'r' : case 'n' : case 't' : + break; + /* Allows escaped symbol \uXXXX */ + case 'u': + parser->pos++; + for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) { + /* If it isn't a hex character we have an error */ + if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ + (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ + (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ + parser->pos = start; + return JSMN_ERROR_INVAL; + } + parser->pos++; + } + parser->pos--; + break; + /* Unexpected symbol */ + default: + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } + } + parser->pos = start; + return JSMN_ERROR_PART; +} + +/** + * Parse JSON string and fill tokens. + */ +int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, + jsmntok_t *tokens, unsigned int num_tokens) { + int r; + int i; + jsmntok_t *token; + int count = parser->toknext; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c; + jsmntype_t type; + + c = js[parser->pos]; + switch (c) { + case '{': case '[': + count++; + if (tokens == NULL) { + break; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) + return JSMN_ERROR_NOMEM; + if (parser->toksuper != -1) { + tokens[parser->toksuper].size++; +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + } + token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); + token->start = parser->pos; + parser->toksuper = parser->toknext - 1; + break; + case '}': case ']': + if (tokens == NULL) + break; + type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); +#ifdef JSMN_PARENT_LINKS + if (parser->toknext < 1) { + return JSMN_ERROR_INVAL; + } + token = &tokens[parser->toknext - 1]; + for (;;) { + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + token->end = parser->pos + 1; + parser->toksuper = token->parent; + break; + } + if (token->parent == -1) { + break; + } + token = &tokens[token->parent]; + } +#else + for (i = parser->toknext - 1; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + parser->toksuper = -1; + token->end = parser->pos + 1; + break; + } + } + /* Error if unmatched closing bracket */ + if (i == -1) return JSMN_ERROR_INVAL; + for (; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + parser->toksuper = i; + break; + } + } +#endif + break; + case '\"': + r = jsmn_parse_string(parser, js, len, tokens, num_tokens); + if (r < 0) return r; + count++; + if (parser->toksuper != -1 && tokens != NULL) + tokens[parser->toksuper].size++; + break; + case '\t' : case '\r' : case '\n' : case ' ': + break; + case ':': + parser->toksuper = parser->toknext - 1; + break; + case ',': + if (tokens != NULL && parser->toksuper != -1 && + tokens[parser->toksuper].type != JSMN_ARRAY && + tokens[parser->toksuper].type != JSMN_OBJECT) { +#ifdef JSMN_PARENT_LINKS + parser->toksuper = tokens[parser->toksuper].parent; +#else + for (i = parser->toknext - 1; i >= 0; i--) { + if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { + if (tokens[i].start != -1 && tokens[i].end == -1) { + parser->toksuper = i; + break; + } + } + } +#endif + } + break; +#ifdef JSMN_STRICT + /* In strict mode primitives are: numbers and booleans */ + case '-': case '0': case '1' : case '2': case '3' : case '4': + case '5': case '6': case '7' : case '8': case '9': + case 't': case 'f': case 'n' : + /* And they must not be keys of the object */ + if (tokens != NULL && parser->toksuper != -1) { + jsmntok_t *t = &tokens[parser->toksuper]; + if (t->type == JSMN_OBJECT || + (t->type == JSMN_STRING && t->size != 0)) { + return JSMN_ERROR_INVAL; + } + } +#else + /* In non-strict mode every unquoted value is a primitive */ + default: +#endif + r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); + if (r < 0) return r; + count++; + if (parser->toksuper != -1 && tokens != NULL) + tokens[parser->toksuper].size++; + break; + +#ifdef JSMN_STRICT + /* Unexpected char in strict mode */ + default: + return JSMN_ERROR_INVAL; +#endif + } + } + + if (tokens != NULL) { + for (i = parser->toknext - 1; i >= 0; i--) { + /* Unmatched opened object or array */ + if (tokens[i].start != -1 && tokens[i].end == -1) { + return JSMN_ERROR_PART; + } + } + } + + return count; +} + +/** + * Creates a new parser based over a given buffer with an array of tokens + * available. + */ +void jsmn_init(jsmn_parser *parser) { + parser->pos = 0; + parser->toknext = 0; + parser->toksuper = -1; +} + diff --git a/agent/jsmn.h b/agent/jsmn.h new file mode 100644 index 0000000..0a3c5d7 --- /dev/null +++ b/agent/jsmn.h @@ -0,0 +1,97 @@ +/* + Copyright (c) 2010 Serge A. Zaitsev + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE.* + */ +#ifndef __JSMN_H_ +#define __JSMN_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * JSON type identifier. Basic types are: + * o Object + * o Array + * o String + * o Other primitive: number, boolean (true/false) or null + */ +typedef enum { + JSMN_UNDEFINED = 0, + JSMN_OBJECT = 1, + JSMN_ARRAY = 2, + JSMN_STRING = 3, + JSMN_PRIMITIVE = 4 +} jsmntype_t; + +enum jsmnerr { + /* Not enough tokens were provided */ + JSMN_ERROR_NOMEM = -1, + /* Invalid character inside JSON string */ + JSMN_ERROR_INVAL = -2, + /* The string is not a full JSON packet, more bytes expected */ + JSMN_ERROR_PART = -3 +}; + +/** + * JSON token description. + * @param type type (object, array, string etc.) + * @param start start position in JSON data string + * @param end end position in JSON data string + */ +typedef struct { + jsmntype_t type; + int start; + int end; + int size; +#ifdef JSMN_PARENT_LINKS + int parent; +#endif +} jsmntok_t; + +/** + * JSON parser. Contains an array of token blocks available. Also stores + * the string being parsed now and current position in that string + */ +typedef struct { + unsigned int pos; /* offset in the JSON string */ + unsigned int toknext; /* next token to allocate */ + int toksuper; /* superior token node, e.g parent object or array */ +} jsmn_parser; + +/** + * Create JSON parser over an array of tokens + */ +void jsmn_init(jsmn_parser *parser); + +/** + * Run JSON parser. It parses a JSON data string into and array of tokens, each describing + * a single JSON object. + */ +int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, + jsmntok_t *tokens, unsigned int num_tokens); + +#ifdef __cplusplus +} +#endif + +#endif /* __JSMN_H_ */ diff --git a/agent/json.c b/agent/json.c new file mode 100644 index 0000000..cb07e4e --- /dev/null +++ b/agent/json.c @@ -0,0 +1,319 @@ +/* $OpenBSD: json.c,v 1.9 2017/01/24 13:32:55 jsing Exp $ */ + +/* + * Copyright (c) 2016 Kristaps Dzonsons + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "jsmn.h" +#include "main.h" + +struct jsmnp; + +/* + * Objects consist of node pairs: the left-hand side (before the colon) + * and the right-hand side---the data. + */ +struct jsmnp { + struct jsmnn *lhs; /* left of colon */ + struct jsmnn *rhs; /* right of colon */ +}; + +/* + * Object for converting the JSMN token array into a tree. + */ +struct parse { + struct jsmnn *nodes; /* all nodes */ + size_t cur; /* current number */ + size_t max; /* nodes in "nodes" */ +}; + +/* + * Recursive part for convertin a JSMN token array into a tree. + * See "example/jsondump.c" for its construction (it's the same except + * for how it handles allocation errors). + */ +static ssize_t +build(struct parse *parse, struct jsmnn **np, + jsmntok_t *t, const char *js, size_t sz) +{ + size_t i, j; + struct jsmnn *n; + ssize_t tmp; + + if (sz == 0) + return 0; + + assert(parse->cur < parse->max); + n = *np = &parse->nodes[parse->cur++]; + n->p = parse; + n->type = t->type; + + switch (t->type) { + case JSMN_STRING: + /* FALLTHROUGH */ + case JSMN_PRIMITIVE: + n->fields = 1; + n->d.str = strndup + (js + t->start, + t->end - t->start); + if (n->d.str == NULL) + break; + return 1; + case JSMN_OBJECT: + n->fields = t->size; + n->d.obj = calloc(n->fields, + sizeof(struct jsmnp)); + if (n->d.obj == NULL) + break; + for (i = j = 0; i < (size_t)t->size; i++) { + tmp = build(parse, + &n->d.obj[i].lhs, + t + 1 + j, js, sz - j); + if (tmp < 0) + break; + j += tmp; + tmp = build(parse, + &n->d.obj[i].rhs, + t + 1 + j, js, sz - j); + if (tmp < 0) + break; + j += tmp; + } + if (i < (size_t)t->size) + break; + return j + 1; + case JSMN_ARRAY: + n->fields = t->size; + n->d.array = calloc(n->fields, + sizeof(struct jsmnn *)); + if (n->d.array == NULL) + break; + for (i = j = 0; i < (size_t)t->size; i++) { + tmp = build(parse, + &n->d.array[i], + t + 1 + j, js, sz - j); + if (tmp < 0) + break; + j += tmp; + } + if (i < (size_t)t->size) + break; + return j + 1; + default: + break; + } + + return -1; +} + +/* + * Fully free up a parse sequence. + * This handles all nodes sequentially, not recursively. + */ +static void +jsmnparse_free(struct parse *p) +{ + size_t i; + + if (p == NULL) + return; + for (i = 0; i < p->max; i++) { + struct jsmnn *n = &p->nodes[i]; + switch (n->type) { + case JSMN_ARRAY: + free(n->d.array); + break; + case JSMN_OBJECT: + free(n->d.obj); + break; + case JSMN_PRIMITIVE: + free(n->d.str); + break; + case JSMN_STRING: + free(n->d.str); + break; + case JSMN_UNDEFINED: + break; + } + } + free(p->nodes); + free(p); +} + +/* + * Allocate a tree representation of "t". + * This returns NULL on allocation failure or when sz is zero, in which + * case all resources allocated along the way are freed already. + */ +static struct jsmnn * +jsmntree_alloc(jsmntok_t *t, const char *js, size_t sz) +{ + struct jsmnn *first; + struct parse *p; + + if (sz == 0) + return NULL; + + p = calloc(1, sizeof(struct parse)); + if (p == NULL) + return NULL; + + p->max = sz; + p->nodes = calloc(p->max, sizeof(struct jsmnn)); + if (p->nodes == NULL) { + free(p); + return NULL; + } + + if (build(p, &first, t, js, sz) < 0) { + jsmnparse_free(p); + first = NULL; + } + + return first; +} + +/* + * Call through to free parse contents. + */ +void +json_free(struct jsmnn *first) +{ + + if (first != NULL) + jsmnparse_free(first->p); +} + +/* + * Just check that the array object is in fact an object. + */ +struct jsmnn * +json_getarrayobj(struct jsmnn *n) +{ + + return n->type != JSMN_OBJECT ? NULL : n; +} + +/* + * Extract an array from the returned JSON object, making sure that it's + * the correct type. + * Returns NULL on failure. + */ +struct jsmnn * +json_getarray(struct jsmnn *n, const char *name) +{ + size_t i; + + if (n->type != JSMN_OBJECT) + return NULL; + for (i = 0; i < n->fields; i++) { + if (n->d.obj[i].lhs->type != JSMN_STRING && + n->d.obj[i].lhs->type != JSMN_PRIMITIVE) + continue; + else if (strcmp(name, n->d.obj[i].lhs->d.str)) + continue; + break; + } + if (i == n->fields) + return NULL; + if (n->d.obj[i].rhs->type != JSMN_ARRAY) + return NULL; + return n->d.obj[i].rhs; +} + +/* + * Extract a single string from the returned JSON object, making sure + * that it's the correct type. + * Returns NULL on failure. + */ +char * +json_getstr(struct jsmnn *n, const char *name) +{ + size_t i; + char *cp; + + if (n->type != JSMN_OBJECT) + return NULL; + for (i = 0; i < n->fields; i++) { + if (n->d.obj[i].lhs->type != JSMN_STRING && + n->d.obj[i].lhs->type != JSMN_PRIMITIVE) + continue; + else if (strcmp(name, n->d.obj[i].lhs->d.str)) + continue; + break; + } + if (i == n->fields) + return NULL; + if (n->d.obj[i].rhs->type != JSMN_STRING && + n->d.obj[i].rhs->type != JSMN_PRIMITIVE) + return NULL; + + cp = strdup(n->d.obj[i].rhs->d.str); + if (cp == NULL) + warn("strdup"); + return cp; +} + +/* + * Parse an HTTP response body from a buffer of size "sz". + * Returns an opaque pointer on success, otherwise NULL on error. + */ +struct jsmnn * +json_parse(const char *buf, size_t sz) +{ + struct jsmnn *n; + jsmn_parser p; + jsmntok_t *tok; + int r; + size_t tokcount; + + jsmn_init(&p); + tokcount = 128; + + /* Do this until we don't need any more tokens. */ +again: + tok = calloc(tokcount, sizeof(jsmntok_t)); + if (tok == NULL) { + warn("calloc"); + return NULL; + } + + /* Actually try to parse the JSON into the tokens. */ + + r = jsmn_parse(&p, buf, sz, tok, tokcount); + if (r < 0 && r == JSMN_ERROR_NOMEM) { + tokcount *= 2; + free(tok); + goto again; + } else if (r < 0) { + warnx("jsmn_parse: %d", r); + free(tok); + return NULL; + } + + /* Now parse the tokens into a tree. */ + + n = jsmntree_alloc(tok, buf, r); + free(tok); + return n; +}