From 7853c79cd66394e13e9962c5cfebc20dbbf7cbe5 Mon Sep 17 00:00:00 2001 From: Toni Uhlig Date: Fri, 13 Nov 2020 14:15:23 +0100 Subject: Renamed misleading "contrib" subfolder to "dependencies". Signed-off-by: Toni Uhlig --- Makefile | 2 +- contrib/jsmn/.clang-format | 90 ------- contrib/jsmn/.travis.yml | 4 - contrib/jsmn/LICENSE | 20 -- contrib/jsmn/Makefile | 36 --- contrib/jsmn/README.md | 182 ------------- contrib/jsmn/example/jsondump.c | 134 ---------- contrib/jsmn/example/simple.c | 77 ------ contrib/jsmn/jsmn.h | 471 --------------------------------- contrib/jsmn/library.json | 16 -- contrib/jsmn/test/test.h | 31 --- contrib/jsmn/test/tests.c | 359 ------------------------- contrib/jsmn/test/testutil.h | 96 ------- contrib/nDPIsrvd.h | 223 ---------------- contrib/nDPIsrvd.py | 269 ------------------- contrib/update_jsmn.sh | 6 - dependencies/jsmn/.clang-format | 90 +++++++ dependencies/jsmn/.travis.yml | 4 + dependencies/jsmn/LICENSE | 20 ++ dependencies/jsmn/Makefile | 36 +++ dependencies/jsmn/README.md | 182 +++++++++++++ dependencies/jsmn/example/jsondump.c | 134 ++++++++++ dependencies/jsmn/example/simple.c | 77 ++++++ dependencies/jsmn/jsmn.h | 471 +++++++++++++++++++++++++++++++++ dependencies/jsmn/library.json | 16 ++ dependencies/jsmn/test/test.h | 31 +++ dependencies/jsmn/test/tests.c | 359 +++++++++++++++++++++++++ dependencies/jsmn/test/testutil.h | 96 +++++++ dependencies/nDPIsrvd.h | 223 ++++++++++++++++ dependencies/nDPIsrvd.py | 269 +++++++++++++++++++ dependencies/update_jsmn.sh | 6 + examples/c-json-stdout/c-json-stdout.c | 2 +- 32 files changed, 2016 insertions(+), 2016 deletions(-) delete mode 100644 contrib/jsmn/.clang-format delete mode 100644 contrib/jsmn/.travis.yml delete mode 100644 contrib/jsmn/LICENSE delete mode 100644 contrib/jsmn/Makefile delete mode 100644 contrib/jsmn/README.md delete mode 100644 contrib/jsmn/example/jsondump.c delete mode 100644 contrib/jsmn/example/simple.c delete mode 100644 contrib/jsmn/jsmn.h delete mode 100644 contrib/jsmn/library.json delete mode 100644 contrib/jsmn/test/test.h delete mode 100644 contrib/jsmn/test/tests.c delete mode 100644 contrib/jsmn/test/testutil.h delete mode 100644 contrib/nDPIsrvd.h delete mode 100644 contrib/nDPIsrvd.py delete mode 100755 contrib/update_jsmn.sh create mode 100644 dependencies/jsmn/.clang-format create mode 100644 dependencies/jsmn/.travis.yml create mode 100644 dependencies/jsmn/LICENSE create mode 100644 dependencies/jsmn/Makefile create mode 100644 dependencies/jsmn/README.md create mode 100644 dependencies/jsmn/example/jsondump.c create mode 100644 dependencies/jsmn/example/simple.c create mode 100644 dependencies/jsmn/jsmn.h create mode 100644 dependencies/jsmn/library.json create mode 100644 dependencies/jsmn/test/test.h create mode 100644 dependencies/jsmn/test/tests.c create mode 100644 dependencies/jsmn/test/testutil.h create mode 100644 dependencies/nDPIsrvd.h create mode 100644 dependencies/nDPIsrvd.py create mode 100755 dependencies/update_jsmn.sh diff --git a/Makefile b/Makefile index d5311a377..792916588 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ CC = gcc PROJECT_CFLAGS += -Wall -Wextra $(EXTRA_CFLAGS) -I. -JSMN_CFLAGS := -DJSMN_STATIC=1 -DJSMN_STRICT=1 -Icontrib -Icontrib/jsmn +JSMN_CFLAGS := -DJSMN_STATIC=1 -DJSMN_STRICT=1 -Idependencies LIBS += -pthread -lpcap -lm GOCC = diff --git a/contrib/jsmn/.clang-format b/contrib/jsmn/.clang-format deleted file mode 100644 index 3a5940ef6..000000000 --- a/contrib/jsmn/.clang-format +++ /dev/null @@ -1,90 +0,0 @@ ---- -Language: Cpp -# BasedOnStyle: LLVM -AccessModifierOffset: -2 -AlignAfterOpenBracket: Align -AlignConsecutiveAssignments: false -AlignConsecutiveDeclarations: false -AlignEscapedNewlinesLeft: false -AlignOperands: true -AlignTrailingComments: true -AllowAllParametersOfDeclarationOnNextLine: true -AllowShortBlocksOnASingleLine: false -AllowShortCaseLabelsOnASingleLine: false -AllowShortFunctionsOnASingleLine: All -AllowShortIfStatementsOnASingleLine: false -AllowShortLoopsOnASingleLine: false -AlwaysBreakAfterDefinitionReturnType: None -AlwaysBreakAfterReturnType: None -AlwaysBreakBeforeMultilineStrings: false -AlwaysBreakTemplateDeclarations: false -BinPackArguments: true -BinPackParameters: true -BraceWrapping: - AfterClass: false - AfterControlStatement: false - AfterEnum: false - AfterFunction: false - AfterNamespace: false - AfterObjCDeclaration: false - AfterStruct: false - AfterUnion: false - BeforeCatch: false - BeforeElse: false - IndentBraces: false -BreakBeforeBinaryOperators: None -BreakBeforeBraces: Attach -BreakBeforeTernaryOperators: true -BreakConstructorInitializersBeforeComma: false -ColumnLimit: 80 -CommentPragmas: '^ IWYU pragma:' -ConstructorInitializerAllOnOneLineOrOnePerLine: false -ConstructorInitializerIndentWidth: 4 -ContinuationIndentWidth: 4 -Cpp11BracedListStyle: true -DerivePointerAlignment: false -DisableFormat: false -ExperimentalAutoDetectBinPacking: false -ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] -IncludeCategories: - - Regex: '^"(llvm|llvm-c|clang|clang-c)/' - Priority: 2 - - Regex: '^(<|"(gtest|isl|json)/)' - Priority: 3 - - Regex: '.*' - Priority: 1 -IndentCaseLabels: false -IndentWidth: 2 -IndentWrappedFunctionNames: false -KeepEmptyLinesAtTheStartOfBlocks: true -MacroBlockBegin: '' -MacroBlockEnd: '' -MaxEmptyLinesToKeep: 1 -NamespaceIndentation: None -ObjCBlockIndentWidth: 2 -ObjCSpaceAfterProperty: false -ObjCSpaceBeforeProtocolList: true -PenaltyBreakBeforeFirstCallParameter: 19 -PenaltyBreakComment: 300 -PenaltyBreakFirstLessLess: 120 -PenaltyBreakString: 1000 -PenaltyExcessCharacter: 1000000 -PenaltyReturnTypeOnItsOwnLine: 60 -PointerAlignment: Right -ReflowComments: true -SortIncludes: true -SpaceAfterCStyleCast: false -SpaceBeforeAssignmentOperators: true -SpaceBeforeParens: ControlStatements -SpaceInEmptyParentheses: false -SpacesBeforeTrailingComments: 1 -SpacesInAngles: false -SpacesInContainerLiterals: true -SpacesInCStyleCastParentheses: false -SpacesInParentheses: false -SpacesInSquareBrackets: false -Standard: Cpp11 -TabWidth: 8 -UseTab: Never -... - diff --git a/contrib/jsmn/.travis.yml b/contrib/jsmn/.travis.yml deleted file mode 100644 index 1c8ebd327..000000000 --- a/contrib/jsmn/.travis.yml +++ /dev/null @@ -1,4 +0,0 @@ -language: c -sudo: false -script: - - make test diff --git a/contrib/jsmn/LICENSE b/contrib/jsmn/LICENSE deleted file mode 100644 index c84fb2e97..000000000 --- a/contrib/jsmn/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -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. - diff --git a/contrib/jsmn/Makefile b/contrib/jsmn/Makefile deleted file mode 100644 index dcbdd89d7..000000000 --- a/contrib/jsmn/Makefile +++ /dev/null @@ -1,36 +0,0 @@ -# You can put your build options here --include config.mk - -test: test_default test_strict test_links test_strict_links -test_default: test/tests.c jsmn.h - $(CC) $(CFLAGS) $(LDFLAGS) $< -o test/$@ - ./test/$@ -test_strict: test/tests.c jsmn.h - $(CC) -DJSMN_STRICT=1 $(CFLAGS) $(LDFLAGS) $< -o test/$@ - ./test/$@ -test_links: test/tests.c jsmn.h - $(CC) -DJSMN_PARENT_LINKS=1 $(CFLAGS) $(LDFLAGS) $< -o test/$@ - ./test/$@ -test_strict_links: test/tests.c jsmn.h - $(CC) -DJSMN_STRICT=1 -DJSMN_PARENT_LINKS=1 $(CFLAGS) $(LDFLAGS) $< -o test/$@ - ./test/$@ - -simple_example: example/simple.c jsmn.h - $(CC) $(LDFLAGS) $< -o $@ - -jsondump: example/jsondump.c jsmn.h - $(CC) $(LDFLAGS) $< -o $@ - -fmt: - clang-format -i jsmn.h test/*.[ch] example/*.[ch] - -lint: - clang-tidy jsmn.h --checks='*' - -clean: - rm -f *.o example/*.o - rm -f simple_example - rm -f jsondump - -.PHONY: clean test - diff --git a/contrib/jsmn/README.md b/contrib/jsmn/README.md deleted file mode 100644 index f8249f3dd..000000000 --- a/contrib/jsmn/README.md +++ /dev/null @@ -1,182 +0,0 @@ -JSMN -==== - -[![Build Status](https://travis-ci.org/zserge/jsmn.svg?branch=master)](https://travis-ci.org/zserge/jsmn) - -jsmn (pronounced like 'jasmine') is a minimalistic JSON parser in C. It can be -easily integrated into resource-limited or embedded projects. - -You can find more information about JSON format at [json.org][1] - -Library sources are available at https://github.com/zserge/jsmn - -The web page with some information about jsmn can be found at -[http://zserge.com/jsmn.html][2] - -Philosophy ----------- - -Most JSON parsers offer you a bunch of functions to load JSON data, parse it -and extract any value by its name. jsmn proves that checking the correctness of -every JSON packet or allocating temporary objects to store parsed JSON fields -often is an overkill. - -JSON format itself is extremely simple, so why should we complicate it? - -jsmn is designed to be **robust** (it should work fine even with erroneous -data), **fast** (it should parse data on the fly), **portable** (no superfluous -dependencies or non-standard C extensions). And of course, **simplicity** is a -key feature - simple code style, simple algorithm, simple integration into -other projects. - -Features --------- - -* compatible with C89 -* no dependencies (even libc!) -* highly portable (tested on x86/amd64, ARM, AVR) -* about 200 lines of code -* extremely small code footprint -* API contains only 2 functions -* no dynamic memory allocation -* incremental single-pass parsing -* library code is covered with unit-tests - -Design ------- - -The rudimentary jsmn object is a **token**. Let's consider a JSON string: - - '{ "name" : "Jack", "age" : 27 }' - -It holds the following tokens: - -* Object: `{ "name" : "Jack", "age" : 27}` (the whole object) -* Strings: `"name"`, `"Jack"`, `"age"` (keys and some values) -* Number: `27` - -In jsmn, tokens do not hold any data, but point to token boundaries in JSON -string instead. In the example above jsmn will create tokens like: Object -[0..31], String [3..7], String [12..16], String [20..23], Number [27..29]. - -Every jsmn token has a type, which indicates the type of corresponding JSON -token. jsmn supports the following token types: - -* Object - a container of key-value pairs, e.g.: - `{ "foo":"bar", "x":0.3 }` -* Array - a sequence of values, e.g.: - `[ 1, 2, 3 ]` -* String - a quoted sequence of chars, e.g.: `"foo"` -* Primitive - a number, a boolean (`true`, `false`) or `null` - -Besides start/end positions, jsmn tokens for complex types (like arrays -or objects) also contain a number of child items, so you can easily follow -object hierarchy. - -This approach provides enough information for parsing any JSON data and makes -it possible to use zero-copy techniques. - -Usage ------ - -Download `jsmn.h`, include it, done. - -``` -#include "jsmn.h" - -... -jsmn_parser p; -jsmntok_t t[128]; /* We expect no more than 128 JSON tokens */ - -jsmn_init(&p); -r = jsmn_parse(&p, s, strlen(s), t, 128); -``` - -Since jsmn is a single-header, header-only library, for more complex use cases -you might need to define additional macros. `#define JSMN_STATIC` hides all -jsmn API symbols by making them static. Also, if you want to include `jsmn.h` -from multiple C files, to avoid duplication of symbols you may define `JSMN_HEADER` macro. - -``` -/* In every .c file that uses jsmn include only declarations: */ -#define JSMN_HEADER -#include "jsmn.h" - -/* Additionally, create one jsmn.c file for jsmn implementation: */ -#include "jsmn.h" -``` - -API ---- - -Token types are described by `jsmntype_t`: - - typedef enum { - JSMN_UNDEFINED = 0, - JSMN_OBJECT = 1, - JSMN_ARRAY = 2, - JSMN_STRING = 3, - JSMN_PRIMITIVE = 4 - } jsmntype_t; - -**Note:** Unlike JSON data types, primitive tokens are not divided into -numbers, booleans and null, because one can easily tell the type using the -first character: - -* 't', 'f' - boolean -* 'n' - null -* '-', '0'..'9' - number - -Token is an object of `jsmntok_t` type: - - typedef struct { - jsmntype_t type; // Token type - int start; // Token start position - int end; // Token end position - int size; // Number of child (nested) tokens - } jsmntok_t; - -**Note:** string tokens point to the first character after -the opening quote and the previous symbol before final quote. This was made -to simplify string extraction from JSON data. - -All job is done by `jsmn_parser` object. You can initialize a new parser using: - - jsmn_parser parser; - jsmntok_t tokens[10]; - - jsmn_init(&parser); - - // js - pointer to JSON string - // tokens - an array of tokens available - // 10 - number of tokens available - jsmn_parse(&parser, js, strlen(js), tokens, 10); - -This will create a parser, and then it tries to parse up to 10 JSON tokens from -the `js` string. - -A non-negative return value of `jsmn_parse` is the number of tokens actually -used by the parser. -Passing NULL instead of the tokens array would not store parsing results, but -instead the function will return the number of tokens needed to parse the given -string. This can be useful if you don't know yet how many tokens to allocate. - -If something goes wrong, you will get an error. Error will be one of these: - -* `JSMN_ERROR_INVAL` - bad token, JSON string is corrupted -* `JSMN_ERROR_NOMEM` - not enough tokens, JSON string is too large -* `JSMN_ERROR_PART` - JSON string is too short, expecting more JSON data - -If you get `JSMN_ERROR_NOMEM`, you can re-allocate more tokens and call -`jsmn_parse` once more. If you read json data from the stream, you can -periodically call `jsmn_parse` and check if return value is `JSMN_ERROR_PART`. -You will get this error until you reach the end of JSON data. - -Other info ----------- - -This software is distributed under [MIT license](http://www.opensource.org/licenses/mit-license.php), - so feel free to integrate it in your commercial products. - -[1]: http://www.json.org/ -[2]: http://zserge.com/jsmn.html diff --git a/contrib/jsmn/example/jsondump.c b/contrib/jsmn/example/jsondump.c deleted file mode 100644 index 1eb620640..000000000 --- a/contrib/jsmn/example/jsondump.c +++ /dev/null @@ -1,134 +0,0 @@ -#include "../jsmn.h" -#include -#include -#include -#include -#include - -/* Function realloc_it() is a wrapper function for standard realloc() - * with one difference - it frees old memory pointer in case of realloc - * failure. Thus, DO NOT use old data pointer in anyway after call to - * realloc_it(). If your code has some kind of fallback algorithm if - * memory can't be re-allocated - use standard realloc() instead. - */ -static inline void *realloc_it(void *ptrmem, size_t size) { - void *p = realloc(ptrmem, size); - if (!p) { - free(ptrmem); - fprintf(stderr, "realloc(): errno=%d\n", errno); - } - return p; -} - -/* - * An example of reading JSON from stdin and printing its content to stdout. - * The output looks like YAML, but I'm not sure if it's really compatible. - */ - -static int dump(const char *js, jsmntok_t *t, size_t count, int indent) { - int i, j, k; - jsmntok_t *key; - if (count == 0) { - return 0; - } - if (t->type == JSMN_PRIMITIVE) { - printf("%.*s", t->end - t->start, js + t->start); - return 1; - } else if (t->type == JSMN_STRING) { - printf("'%.*s'", t->end - t->start, js + t->start); - return 1; - } else if (t->type == JSMN_OBJECT) { - printf("\n"); - j = 0; - for (i = 0; i < t->size; i++) { - for (k = 0; k < indent; k++) { - printf(" "); - } - key = t + 1 + j; - j += dump(js, key, count - j, indent + 1); - if (key->size > 0) { - printf(": "); - j += dump(js, t + 1 + j, count - j, indent + 1); - } - printf("\n"); - } - return j + 1; - } else if (t->type == JSMN_ARRAY) { - j = 0; - printf("\n"); - for (i = 0; i < t->size; i++) { - for (k = 0; k < indent - 1; k++) { - printf(" "); - } - printf(" - "); - j += dump(js, t + 1 + j, count - j, indent + 1); - printf("\n"); - } - return j + 1; - } - return 0; -} - -int main() { - int r; - int eof_expected = 0; - char *js = NULL; - size_t jslen = 0; - char buf[BUFSIZ]; - - jsmn_parser p; - jsmntok_t *tok; - size_t tokcount = 2; - - /* Prepare parser */ - jsmn_init(&p); - - /* Allocate some tokens as a start */ - tok = malloc(sizeof(*tok) * tokcount); - if (tok == NULL) { - fprintf(stderr, "malloc(): errno=%d\n", errno); - return 3; - } - - for (;;) { - /* Read another chunk */ - r = fread(buf, 1, sizeof(buf), stdin); - if (r < 0) { - fprintf(stderr, "fread(): %d, errno=%d\n", r, errno); - return 1; - } - if (r == 0) { - if (eof_expected != 0) { - return 0; - } else { - fprintf(stderr, "fread(): unexpected EOF\n"); - return 2; - } - } - - js = realloc_it(js, jslen + r + 1); - if (js == NULL) { - return 3; - } - strncpy(js + jslen, buf, r); - jslen = jslen + r; - - again: - r = jsmn_parse(&p, js, jslen, tok, tokcount); - if (r < 0) { - if (r == JSMN_ERROR_NOMEM) { - tokcount = tokcount * 2; - tok = realloc_it(tok, sizeof(*tok) * tokcount); - if (tok == NULL) { - return 3; - } - goto again; - } - } else { - dump(js, tok, p.toknext, 0); - eof_expected = 1; - } - } - - return EXIT_SUCCESS; -} diff --git a/contrib/jsmn/example/simple.c b/contrib/jsmn/example/simple.c deleted file mode 100644 index 1254575a1..000000000 --- a/contrib/jsmn/example/simple.c +++ /dev/null @@ -1,77 +0,0 @@ -#include "../jsmn.h" -#include -#include -#include - -/* - * A small example of jsmn parsing when JSON structure is known and number of - * tokens is predictable. - */ - -static const char *JSON_STRING = - "{\"user\": \"johndoe\", \"admin\": false, \"uid\": 1000,\n " - "\"groups\": [\"users\", \"wheel\", \"audio\", \"video\"]}"; - -static int jsoneq(const char *json, jsmntok_t *tok, const char *s) { - if (tok->type == JSMN_STRING && (int)strlen(s) == tok->end - tok->start && - strncmp(json + tok->start, s, tok->end - tok->start) == 0) { - return 0; - } - return -1; -} - -int main() { - int i; - int r; - jsmn_parser p; - jsmntok_t t[128]; /* We expect no more than 128 tokens */ - - jsmn_init(&p); - r = jsmn_parse(&p, JSON_STRING, strlen(JSON_STRING), t, - sizeof(t) / sizeof(t[0])); - if (r < 0) { - printf("Failed to parse JSON: %d\n", r); - return 1; - } - - /* Assume the top-level element is an object */ - if (r < 1 || t[0].type != JSMN_OBJECT) { - printf("Object expected\n"); - return 1; - } - - /* Loop over all keys of the root object */ - for (i = 1; i < r; i++) { - if (jsoneq(JSON_STRING, &t[i], "user") == 0) { - /* We may use strndup() to fetch string value */ - printf("- User: %.*s\n", t[i + 1].end - t[i + 1].start, - JSON_STRING + t[i + 1].start); - i++; - } else if (jsoneq(JSON_STRING, &t[i], "admin") == 0) { - /* We may additionally check if the value is either "true" or "false" */ - printf("- Admin: %.*s\n", t[i + 1].end - t[i + 1].start, - JSON_STRING + t[i + 1].start); - i++; - } else if (jsoneq(JSON_STRING, &t[i], "uid") == 0) { - /* We may want to do strtol() here to get numeric value */ - printf("- UID: %.*s\n", t[i + 1].end - t[i + 1].start, - JSON_STRING + t[i + 1].start); - i++; - } else if (jsoneq(JSON_STRING, &t[i], "groups") == 0) { - int j; - printf("- Groups:\n"); - if (t[i + 1].type != JSMN_ARRAY) { - continue; /* We expect groups to be an array of strings */ - } - for (j = 0; j < t[i + 1].size; j++) { - jsmntok_t *g = &t[i + j + 2]; - printf(" * %.*s\n", g->end - g->start, JSON_STRING + g->start); - } - i += t[i + 1].size + 1; - } else { - printf("Unexpected key: %.*s\n", t[i].end - t[i].start, - JSON_STRING + t[i].start); - } - } - return EXIT_SUCCESS; -} diff --git a/contrib/jsmn/jsmn.h b/contrib/jsmn/jsmn.h deleted file mode 100644 index 3178dcc97..000000000 --- a/contrib/jsmn/jsmn.h +++ /dev/null @@ -1,471 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2010 Serge 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 - -#ifdef JSMN_STATIC -#define JSMN_API static -#else -#define JSMN_API extern -#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. - * type type (object, array, string etc.) - * start start position in JSON data string - * end end position in JSON data string - */ -typedef struct jsmntok { - 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 jsmn_parser { - 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 - */ -JSMN_API 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. - */ -JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len, - jsmntok_t *tokens, const unsigned int num_tokens); - -#ifndef JSMN_HEADER -/** - * Allocates a fresh unused token from the token pool. - */ -static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens, - const 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, const jsmntype_t type, - const int start, const 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, - const size_t len, jsmntok_t *tokens, - const 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; - default: - /* to quiet a warning from gcc*/ - break; - } - 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, - const size_t len, jsmntok_t *tokens, - const 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. - */ -JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len, - jsmntok_t *tokens, const 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) { - jsmntok_t *t = &tokens[parser->toksuper]; -#ifdef JSMN_STRICT - /* In strict mode an object or array can't become a key */ - if (t->type == JSMN_OBJECT) { - return JSMN_ERROR_INVAL; - } -#endif - t->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) { - if (token->type != type || parser->toksuper == -1) { - return JSMN_ERROR_INVAL; - } - 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) { - const 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. - */ -JSMN_API void jsmn_init(jsmn_parser *parser) { - parser->pos = 0; - parser->toknext = 0; - parser->toksuper = -1; -} - -#endif /* JSMN_HEADER */ - -#ifdef __cplusplus -} -#endif - -#endif /* JSMN_H */ diff --git a/contrib/jsmn/library.json b/contrib/jsmn/library.json deleted file mode 100644 index 8e2f5c257..000000000 --- a/contrib/jsmn/library.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "jsmn", - "keywords": "json", - "description": "Minimalistic JSON parser/tokenizer in C. It can be easily integrated into resource-limited or embedded projects", - "repository": - { - "type": "git", - "url": "https://github.com/zserge/jsmn.git" - }, - "frameworks": "*", - "platforms": "*", - "examples": [ - "example/*.c" - ], - "exclude": "test" -} diff --git a/contrib/jsmn/test/test.h b/contrib/jsmn/test/test.h deleted file mode 100644 index a1c0957a7..000000000 --- a/contrib/jsmn/test/test.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef __TEST_H__ -#define __TEST_H__ - -static int test_passed = 0; -static int test_failed = 0; - -/* Terminate current test with error */ -#define fail() return __LINE__ - -/* Successful end of the test case */ -#define done() return 0 - -/* Check single condition */ -#define check(cond) \ - do { \ - if (!(cond)) \ - fail(); \ - } while (0) - -/* Test runner */ -static void test(int (*func)(void), const char *name) { - int r = func(); - if (r == 0) { - test_passed++; - } else { - test_failed++; - printf("FAILED: %s (at line %d)\n", name, r); - } -} - -#endif /* __TEST_H__ */ diff --git a/contrib/jsmn/test/tests.c b/contrib/jsmn/test/tests.c deleted file mode 100644 index d8a4d922e..000000000 --- a/contrib/jsmn/test/tests.c +++ /dev/null @@ -1,359 +0,0 @@ -#include -#include -#include -#include - -#include "test.h" -#include "testutil.h" - -int test_empty(void) { - check(parse("{}", 1, 1, JSMN_OBJECT, 0, 2, 0)); - check(parse("[]", 1, 1, JSMN_ARRAY, 0, 2, 0)); - check(parse("[{},{}]", 3, 3, JSMN_ARRAY, 0, 7, 2, JSMN_OBJECT, 1, 3, 0, - JSMN_OBJECT, 4, 6, 0)); - return 0; -} - -int test_object(void) { - check(parse("{\"a\":0}", 3, 3, JSMN_OBJECT, 0, 7, 1, JSMN_STRING, "a", 1, - JSMN_PRIMITIVE, "0")); - check(parse("{\"a\":[]}", 3, 3, JSMN_OBJECT, 0, 8, 1, JSMN_STRING, "a", 1, - JSMN_ARRAY, 5, 7, 0)); - check(parse("{\"a\":{},\"b\":{}}", 5, 5, JSMN_OBJECT, -1, -1, 2, JSMN_STRING, - "a", 1, JSMN_OBJECT, -1, -1, 0, JSMN_STRING, "b", 1, JSMN_OBJECT, - -1, -1, 0)); - check(parse("{\n \"Day\": 26,\n \"Month\": 9,\n \"Year\": 12\n }", 7, 7, - JSMN_OBJECT, -1, -1, 3, JSMN_STRING, "Day", 1, JSMN_PRIMITIVE, - "26", JSMN_STRING, "Month", 1, JSMN_PRIMITIVE, "9", JSMN_STRING, - "Year", 1, JSMN_PRIMITIVE, "12")); - check(parse("{\"a\": 0, \"b\": \"c\"}", 5, 5, JSMN_OBJECT, -1, -1, 2, - JSMN_STRING, "a", 1, JSMN_PRIMITIVE, "0", JSMN_STRING, "b", 1, - JSMN_STRING, "c", 0)); - -#ifdef JSMN_STRICT - check(parse("{\"a\"\n0}", JSMN_ERROR_INVAL, 3)); - check(parse("{\"a\", 0}", JSMN_ERROR_INVAL, 3)); - check(parse("{\"a\": {2}}", JSMN_ERROR_INVAL, 3)); - check(parse("{\"a\": {2: 3}}", JSMN_ERROR_INVAL, 3)); - check(parse("{\"a\": {\"a\": 2 3}}", JSMN_ERROR_INVAL, 5)); -/* FIXME */ -/*check(parse("{\"a\"}", JSMN_ERROR_INVAL, 2));*/ -/*check(parse("{\"a\": 1, \"b\"}", JSMN_ERROR_INVAL, 4));*/ -/*check(parse("{\"a\",\"b\":1}", JSMN_ERROR_INVAL, 4));*/ -/*check(parse("{\"a\":1,}", JSMN_ERROR_INVAL, 4));*/ -/*check(parse("{\"a\":\"b\":\"c\"}", JSMN_ERROR_INVAL, 4));*/ -/*check(parse("{,}", JSMN_ERROR_INVAL, 4));*/ -#endif - return 0; -} - -int test_array(void) { - /* FIXME */ - /*check(parse("[10}", JSMN_ERROR_INVAL, 3));*/ - /*check(parse("[1,,3]", JSMN_ERROR_INVAL, 3)*/ - check(parse("[10]", 2, 2, JSMN_ARRAY, -1, -1, 1, JSMN_PRIMITIVE, "10")); - check(parse("{\"a\": 1]", JSMN_ERROR_INVAL, 3)); - /* FIXME */ - /*check(parse("[\"a\": 1]", JSMN_ERROR_INVAL, 3));*/ - return 0; -} - -int test_primitive(void) { - check(parse("{\"boolVar\" : true }", 3, 3, JSMN_OBJECT, -1, -1, 1, - JSMN_STRING, "boolVar", 1, JSMN_PRIMITIVE, "true")); - check(parse("{\"boolVar\" : false }", 3, 3, JSMN_OBJECT, -1, -1, 1, - JSMN_STRING, "boolVar", 1, JSMN_PRIMITIVE, "false")); - check(parse("{\"nullVar\" : null }", 3, 3, JSMN_OBJECT, -1, -1, 1, - JSMN_STRING, "nullVar", 1, JSMN_PRIMITIVE, "null")); - check(parse("{\"intVar\" : 12}", 3, 3, JSMN_OBJECT, -1, -1, 1, JSMN_STRING, - "intVar", 1, JSMN_PRIMITIVE, "12")); - check(parse("{\"floatVar\" : 12.345}", 3, 3, JSMN_OBJECT, -1, -1, 1, - JSMN_STRING, "floatVar", 1, JSMN_PRIMITIVE, "12.345")); - return 0; -} - -int test_string(void) { - check(parse("{\"strVar\" : \"hello world\"}", 3, 3, JSMN_OBJECT, -1, -1, 1, - JSMN_STRING, "strVar", 1, JSMN_STRING, "hello world", 0)); - check(parse("{\"strVar\" : \"escapes: \\/\\r\\n\\t\\b\\f\\\"\\\\\"}", 3, 3, - JSMN_OBJECT, -1, -1, 1, JSMN_STRING, "strVar", 1, JSMN_STRING, - "escapes: \\/\\r\\n\\t\\b\\f\\\"\\\\", 0)); - check(parse("{\"strVar\": \"\"}", 3, 3, JSMN_OBJECT, -1, -1, 1, JSMN_STRING, - "strVar", 1, JSMN_STRING, "", 0)); - check(parse("{\"a\":\"\\uAbcD\"}", 3, 3, JSMN_OBJECT, -1, -1, 1, JSMN_STRING, - "a", 1, JSMN_STRING, "\\uAbcD", 0)); - check(parse("{\"a\":\"str\\u0000\"}", 3, 3, JSMN_OBJECT, -1, -1, 1, - JSMN_STRING, "a", 1, JSMN_STRING, "str\\u0000", 0)); - check(parse("{\"a\":\"\\uFFFFstr\"}", 3, 3, JSMN_OBJECT, -1, -1, 1, - JSMN_STRING, "a", 1, JSMN_STRING, "\\uFFFFstr", 0)); - check(parse("{\"a\":[\"\\u0280\"]}", 4, 4, JSMN_OBJECT, -1, -1, 1, - JSMN_STRING, "a", 1, JSMN_ARRAY, -1, -1, 1, JSMN_STRING, - "\\u0280", 0)); - - check(parse("{\"a\":\"str\\uFFGFstr\"}", JSMN_ERROR_INVAL, 3)); - check(parse("{\"a\":\"str\\u@FfF\"}", JSMN_ERROR_INVAL, 3)); - check(parse("{{\"a\":[\"\\u028\"]}", JSMN_ERROR_INVAL, 4)); - return 0; -} - -int test_partial_string(void) { - int r; - unsigned long i; - jsmn_parser p; - jsmntok_t tok[5]; - const char *js = "{\"x\": \"va\\\\ue\", \"y\": \"value y\"}"; - - jsmn_init(&p); - for (i = 1; i <= strlen(js); i++) { - r = jsmn_parse(&p, js, i, tok, sizeof(tok) / sizeof(tok[0])); - if (i == strlen(js)) { - check(r == 5); - check(tokeq(js, tok, 5, JSMN_OBJECT, -1, -1, 2, JSMN_STRING, "x", 1, - JSMN_STRING, "va\\\\ue", 0, JSMN_STRING, "y", 1, JSMN_STRING, - "value y", 0)); - } else { - check(r == JSMN_ERROR_PART); - } - } - return 0; -} - -int test_partial_array(void) { -#ifdef JSMN_STRICT - int r; - unsigned long i; - jsmn_parser p; - jsmntok_t tok[10]; - const char *js = "[ 1, true, [123, \"hello\"]]"; - - jsmn_init(&p); - for (i = 1; i <= strlen(js); i++) { - r = jsmn_parse(&p, js, i, tok, sizeof(tok) / sizeof(tok[0])); - if (i == strlen(js)) { - check(r == 6); - check(tokeq(js, tok, 6, JSMN_ARRAY, -1, -1, 3, JSMN_PRIMITIVE, "1", - JSMN_PRIMITIVE, "true", JSMN_ARRAY, -1, -1, 2, JSMN_PRIMITIVE, - "123", JSMN_STRING, "hello", 0)); - } else { - check(r == JSMN_ERROR_PART); - } - } -#endif - return 0; -} - -int test_array_nomem(void) { - int i; - int r; - jsmn_parser p; - jsmntok_t toksmall[10], toklarge[10]; - const char *js; - - js = " [ 1, true, [123, \"hello\"]]"; - - for (i = 0; i < 6; i++) { - jsmn_init(&p); - memset(toksmall, 0, sizeof(toksmall)); - memset(toklarge, 0, sizeof(toklarge)); - r = jsmn_parse(&p, js, strlen(js), toksmall, i); - check(r == JSMN_ERROR_NOMEM); - - memcpy(toklarge, toksmall, sizeof(toksmall)); - - r = jsmn_parse(&p, js, strlen(js), toklarge, 10); - check(r >= 0); - check(tokeq(js, toklarge, 4, JSMN_ARRAY, -1, -1, 3, JSMN_PRIMITIVE, "1", - JSMN_PRIMITIVE, "true", JSMN_ARRAY, -1, -1, 2, JSMN_PRIMITIVE, - "123", JSMN_STRING, "hello", 0)); - } - return 0; -} - -int test_unquoted_keys(void) { -#ifndef JSMN_STRICT - int r; - jsmn_parser p; - jsmntok_t tok[10]; - const char *js; - - jsmn_init(&p); - js = "key1: \"value\"\nkey2 : 123"; - - r = jsmn_parse(&p, js, strlen(js), tok, 10); - check(r >= 0); - check(tokeq(js, tok, 4, JSMN_PRIMITIVE, "key1", JSMN_STRING, "value", 0, - JSMN_PRIMITIVE, "key2", JSMN_PRIMITIVE, "123")); -#endif - return 0; -} - -int test_issue_22(void) { - int r; - jsmn_parser p; - jsmntok_t tokens[128]; - const char *js; - - js = - "{ \"height\":10, \"layers\":[ { \"data\":[6,6], \"height\":10, " - "\"name\":\"Calque de Tile 1\", \"opacity\":1, \"type\":\"tilelayer\", " - "\"visible\":true, \"width\":10, \"x\":0, \"y\":0 }], " - "\"orientation\":\"orthogonal\", \"properties\": { }, \"tileheight\":32, " - "\"tilesets\":[ { \"firstgid\":1, \"image\":\"..\\/images\\/tiles.png\", " - "\"imageheight\":64, \"imagewidth\":160, \"margin\":0, " - "\"name\":\"Tiles\", " - "\"properties\":{}, \"spacing\":0, \"tileheight\":32, \"tilewidth\":32 " - "}], " - "\"tilewidth\":32, \"version\":1, \"width\":10 }"; - jsmn_init(&p); - r = jsmn_parse(&p, js, strlen(js), tokens, 128); - check(r >= 0); - return 0; -} - -int test_issue_27(void) { - const char *js = - "{ \"name\" : \"Jack\", \"age\" : 27 } { \"name\" : \"Anna\", "; - check(parse(js, JSMN_ERROR_PART, 8)); - return 0; -} - -int test_input_length(void) { - const char *js; - int r; - jsmn_parser p; - jsmntok_t tokens[10]; - - js = "{\"a\": 0}garbage"; - - jsmn_init(&p); - r = jsmn_parse(&p, js, 8, tokens, 10); - check(r == 3); - check(tokeq(js, tokens, 3, JSMN_OBJECT, -1, -1, 1, JSMN_STRING, "a", 1, - JSMN_PRIMITIVE, "0")); - return 0; -} - -int test_count(void) { - jsmn_parser p; - const char *js; - - js = "{}"; - jsmn_init(&p); - check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 1); - - js = "[]"; - jsmn_init(&p); - check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 1); - - js = "[[]]"; - jsmn_init(&p); - check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 2); - - js = "[[], []]"; - jsmn_init(&p); - check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 3); - - js = "[[], []]"; - jsmn_init(&p); - check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 3); - - js = "[[], [[]], [[], []]]"; - jsmn_init(&p); - check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 7); - - js = "[\"a\", [[], []]]"; - jsmn_init(&p); - check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 5); - - js = "[[], \"[], [[]]\", [[]]]"; - jsmn_init(&p); - check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 5); - - js = "[1, 2, 3]"; - jsmn_init(&p); - check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 4); - - js = "[1, 2, [3, \"a\"], null]"; - jsmn_init(&p); - check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 7); - - return 0; -} - -int test_nonstrict(void) { -#ifndef JSMN_STRICT - const char *js; - js = "a: 0garbage"; - check(parse(js, 2, 2, JSMN_PRIMITIVE, "a", JSMN_PRIMITIVE, "0garbage")); - - js = "Day : 26\nMonth : Sep\n\nYear: 12"; - check(parse(js, 6, 6, JSMN_PRIMITIVE, "Day", JSMN_PRIMITIVE, "26", - JSMN_PRIMITIVE, "Month", JSMN_PRIMITIVE, "Sep", JSMN_PRIMITIVE, - "Year", JSMN_PRIMITIVE, "12")); - - /* nested {s don't cause a parse error. */ - js = "\"key {1\": 1234"; - check(parse(js, 2, 2, JSMN_STRING, "key {1", 1, JSMN_PRIMITIVE, "1234")); - -#endif - return 0; -} - -int test_unmatched_brackets(void) { - const char *js; - js = "\"key 1\": 1234}"; - check(parse(js, JSMN_ERROR_INVAL, 2)); - js = "{\"key 1\": 1234"; - check(parse(js, JSMN_ERROR_PART, 3)); - js = "{\"key 1\": 1234}}"; - check(parse(js, JSMN_ERROR_INVAL, 3)); - js = "\"key 1\"}: 1234"; - check(parse(js, JSMN_ERROR_INVAL, 3)); - js = "{\"key {1\": 1234}"; - check(parse(js, 3, 3, JSMN_OBJECT, 0, 16, 1, JSMN_STRING, "key {1", 1, - JSMN_PRIMITIVE, "1234")); - js = "{\"key 1\":{\"key 2\": 1234}"; - check(parse(js, JSMN_ERROR_PART, 5)); - return 0; -} - -int test_object_key(void) { - const char *js; - - js = "{\"key\": 1}"; - check(parse(js, 3, 3, JSMN_OBJECT, 0, 10, 1, JSMN_STRING, "key", 1, - JSMN_PRIMITIVE, "1")); -#ifdef JSMN_STRICT - js = "{true: 1}"; - check(parse(js, JSMN_ERROR_INVAL, 3)); - js = "{1: 1}"; - check(parse(js, JSMN_ERROR_INVAL, 3)); - js = "{{\"key\": 1}: 2}"; - check(parse(js, JSMN_ERROR_INVAL, 5)); - js = "{[1,2]: 2}"; - check(parse(js, JSMN_ERROR_INVAL, 5)); -#endif - return 0; -} - -int main(void) { - test(test_empty, "test for a empty JSON objects/arrays"); - test(test_object, "test for a JSON objects"); - test(test_array, "test for a JSON arrays"); - test(test_primitive, "test primitive JSON data types"); - test(test_string, "test string JSON data types"); - - test(test_partial_string, "test partial JSON string parsing"); - test(test_partial_array, "test partial array reading"); - test(test_array_nomem, "test array reading with a smaller number of tokens"); - test(test_unquoted_keys, "test unquoted keys (like in JavaScript)"); - test(test_input_length, "test strings that are not null-terminated"); - test(test_issue_22, "test issue #22"); - test(test_issue_27, "test issue #27"); - test(test_count, "test tokens count estimation"); - test(test_nonstrict, "test for non-strict mode"); - test(test_unmatched_brackets, "test for unmatched brackets"); - test(test_object_key, "test for key type"); - printf("\nPASSED: %d\nFAILED: %d\n", test_passed, test_failed); - return (test_failed > 0); -} diff --git a/contrib/jsmn/test/testutil.h b/contrib/jsmn/test/testutil.h deleted file mode 100644 index bdee13934..000000000 --- a/contrib/jsmn/test/testutil.h +++ /dev/null @@ -1,96 +0,0 @@ -#ifndef __TEST_UTIL_H__ -#define __TEST_UTIL_H__ - -#include "../jsmn.h" - -static int vtokeq(const char *s, jsmntok_t *t, unsigned long numtok, - va_list ap) { - if (numtok > 0) { - unsigned long i; - int start, end, size; - jsmntype_t type; - char *value; - - size = -1; - value = NULL; - for (i = 0; i < numtok; i++) { - type = va_arg(ap, jsmntype_t); - if (type == JSMN_STRING) { - value = va_arg(ap, char *); - size = va_arg(ap, int); - start = end = -1; - } else if (type == JSMN_PRIMITIVE) { - value = va_arg(ap, char *); - start = end = size = -1; - } else { - start = va_arg(ap, int); - end = va_arg(ap, int); - size = va_arg(ap, int); - value = NULL; - } - if (t[i].type != type) { - printf("token %lu type is %d, not %d\n", i, t[i].type, type); - return 0; - } - if (start != -1 && end != -1) { - if (t[i].start != start) { - printf("token %lu start is %d, not %d\n", i, t[i].start, start); - return 0; - } - if (t[i].end != end) { - printf("token %lu end is %d, not %d\n", i, t[i].end, end); - return 0; - } - } - if (size != -1 && t[i].size != size) { - printf("token %lu size is %d, not %d\n", i, t[i].size, size); - return 0; - } - - if (s != NULL && value != NULL) { - const char *p = s + t[i].start; - if (strlen(value) != (unsigned long)(t[i].end - t[i].start) || - strncmp(p, value, t[i].end - t[i].start) != 0) { - printf("token %lu value is %.*s, not %s\n", i, t[i].end - t[i].start, - s + t[i].start, value); - return 0; - } - } - } - } - return 1; -} - -static int tokeq(const char *s, jsmntok_t *tokens, unsigned long numtok, ...) { - int ok; - va_list args; - va_start(args, numtok); - ok = vtokeq(s, tokens, numtok, args); - va_end(args); - return ok; -} - -static int parse(const char *s, int status, unsigned long numtok, ...) { - int r; - int ok = 1; - va_list args; - jsmn_parser p; - jsmntok_t *t = malloc(numtok * sizeof(jsmntok_t)); - - jsmn_init(&p); - r = jsmn_parse(&p, s, strlen(s), t, numtok); - if (r != status) { - printf("status is %d, not %d\n", r, status); - return 0; - } - - if (status >= 0) { - va_start(args, numtok); - ok = vtokeq(s, t, numtok, args); - va_end(args); - } - free(t); - return ok; -} - -#endif /* __TEST_UTIL_H__ */ diff --git a/contrib/nDPIsrvd.h b/contrib/nDPIsrvd.h deleted file mode 100644 index 320fdac08..000000000 --- a/contrib/nDPIsrvd.h +++ /dev/null @@ -1,223 +0,0 @@ -#ifndef NDPISRVD_H -#define NDPISRVD_H 1 - -#include -#include -#include -#include -#include -#include - -#include "config.h" -#include "jsmn/jsmn.h" - -struct nDPIsrvd_socket -{ - int fd; - int socket_family; - - union { - struct - { - char const * dst_ip; - unsigned short dst_port; - } ip_socket; - struct - { - char * path; - } unix_socket; - } address; - - struct - { - char raw[NETWORK_BUFFER_MAX_SIZE]; - size_t used; - char * json_string; - size_t json_string_start; - unsigned long long int json_string_length; - } buffer; - - struct - { - jsmn_parser parser; - jsmntok_t tokens[128]; - int current_token; - int tokens_found; - } jsmn; -}; - -#define FIRST_ENUM_VALUE 1 - -enum nDPIsrvd_connect_return -{ - CONNECT_OK = FIRST_ENUM_VALUE, - CONNECT_ERROR_SOCKET, - CONNECT_ERROR_PTON, - CONNECT_ERROR, - CONNECT_LAST_ENUM_VALUE -}; - -enum nDPIsrvd_read_return -{ - READ_OK = CONNECT_LAST_ENUM_VALUE, - READ_PEER_DISCONNECT, - READ_ERROR, - READ_LAST_ENUM_VALUE -}; - -enum nDPIsrvd_parse_return -{ - PARSE_OK = READ_LAST_ENUM_VALUE, - PARSE_INVALID_OPENING_CHAR, - PARSE_SIZE_EXCEEDS_CONVERSION_LIMIT, - PARSE_SIZE_MISSING, - PARSE_STRING_TOO_BIG, - PARSE_INVALID_CLOSING_CHAR, - PARSE_JSMN_ERROR, - PARSE_LAST_ENUM_VALUE -}; - -enum nDPIsrvd_callback_return -{ - CALLBACK_OK = PARSE_LAST_ENUM_VALUE, - CALLBACK_ERROR, - CALLBACK_LAST_ENUM_VALUE -}; - -typedef enum nDPIsrvd_callback_return (*json_callback)(struct nDPIsrvd_socket * const sock, void * user_data); - -static inline struct nDPIsrvd_socket * nDPIsrvd_init(void) -{ - struct nDPIsrvd_socket * sock = (struct nDPIsrvd_socket *)malloc(sizeof(*sock)); - - if (sock != NULL) - { - sock->fd = -1; - sock->socket_family = -1; - } - return sock; -} - -static inline enum nDPIsrvd_connect_return nDPIsrvd_connect_ip(struct nDPIsrvd_socket * const sock, - char const * dst_ip, - unsigned short dst_port) -{ - struct sockaddr_in remote_addr = {}; - - sock->socket_family = remote_addr.sin_family = AF_INET; - sock->fd = socket(sock->socket_family, SOCK_STREAM, 0); - - if (sock->fd < 0) - { - return CONNECT_ERROR_SOCKET; - } - - if (inet_pton(sock->socket_family, &dst_ip[0], &remote_addr.sin_addr) != 1) - { - return CONNECT_ERROR_PTON; - } - remote_addr.sin_port = htons(dst_port); - - if (connect(sock->fd, (struct sockaddr *)&remote_addr, sizeof(remote_addr)) != 0) - { - return CONNECT_ERROR; - } - - return CONNECT_OK; -} - -static inline enum nDPIsrvd_connect_return nDPIsrvd_connect_unix(struct nDPIsrvd_socket * const sock, - char const * const path) -{ - (void)sock; - (void)path; - - return CONNECT_OK; -} - -static inline enum nDPIsrvd_read_return nDPIsrvd_read(struct nDPIsrvd_socket * const sock) -{ - ssize_t bytes_read = - read(sock->fd, sock->buffer.raw + sock->buffer.used, sizeof(sock->buffer.raw) - sock->buffer.used); - - if (bytes_read == 0) - { - return READ_PEER_DISCONNECT; - } - if (bytes_read < 0) - { - return READ_ERROR; - } - - sock->buffer.used += bytes_read; - - return READ_OK; -} - -static inline enum nDPIsrvd_parse_return nDPIsrvd_parse(struct nDPIsrvd_socket * const sock, - json_callback cb, - void * user_data) -{ - while (sock->buffer.used >= nDPIsrvd_JSON_BYTES + 1) - { - if (sock->buffer.raw[nDPIsrvd_JSON_BYTES] != '{') - { - return PARSE_INVALID_OPENING_CHAR; - } - - errno = 0; - sock->buffer.json_string_length = strtoull((const char *)sock->buffer.raw, &sock->buffer.json_string, 10); - sock->buffer.json_string_length += sock->buffer.json_string - sock->buffer.raw; - sock->buffer.json_string_start = sock->buffer.json_string - sock->buffer.raw; - - if (errno == ERANGE) - { - return PARSE_SIZE_EXCEEDS_CONVERSION_LIMIT; - } - if (sock->buffer.json_string == sock->buffer.raw) - { - return PARSE_SIZE_MISSING; - } - if (sock->buffer.json_string_length > sizeof(sock->buffer.raw)) - { - return PARSE_STRING_TOO_BIG; - } - if (sock->buffer.json_string_length > sock->buffer.used) - { - break; - } - - if (sock->buffer.raw[sock->buffer.json_string_length - 1] != '}') - { - return PARSE_INVALID_CLOSING_CHAR; - } - - jsmn_init(&sock->jsmn.parser); - sock->jsmn.tokens_found = jsmn_parse(&sock->jsmn.parser, - (char *)(sock->buffer.raw + sock->buffer.json_string_start), - sock->buffer.json_string_length - sock->buffer.json_string_start, - sock->jsmn.tokens, - sizeof(sock->jsmn.tokens) / sizeof(sock->jsmn.tokens[0])); - if (sock->jsmn.tokens_found < 0 || sock->jsmn.tokens[0].type != JSMN_OBJECT) - { - return PARSE_JSMN_ERROR; - } - - for (sock->jsmn.current_token = 1; sock->jsmn.current_token < sock->jsmn.tokens_found; - sock->jsmn.current_token++) - { - cb(sock, user_data); - } - - memmove(sock->buffer.raw, - sock->buffer.raw + sock->buffer.json_string_length, - sock->buffer.used - sock->buffer.json_string_length); - sock->buffer.used -= sock->buffer.json_string_length; - sock->buffer.json_string_length = 0; - sock->buffer.json_string_start = 0; - } - - return PARSE_OK; -} - -#endif diff --git a/contrib/nDPIsrvd.py b/contrib/nDPIsrvd.py deleted file mode 100644 index 6bcaf516c..000000000 --- a/contrib/nDPIsrvd.py +++ /dev/null @@ -1,269 +0,0 @@ -#!/usr/bin/env python3 - -import argparse -import array -import json -import re -import os -import scapy.all -import stat -import socket - -DEFAULT_HOST = '127.0.0.1' -DEFAULT_PORT = 7000 -DEFAULT_UNIX = '/tmp/ndpid-distributor.sock' - -NETWORK_BUFFER_MIN_SIZE = 5 -NETWORK_BUFFER_MAX_SIZE = 9216 # Please keep this value in sync with the one in config.h - -PKT_TYPE_ETH_IP4 = 0x0800 -PKT_TYPE_ETH_IP6 = 0x86DD - -EVENT_UNKNOWN = 'Unknown' -# Event tuple: (pretty-name, real-name) -DAEMON_EVENTS = [ ('Invalid','invalid'), ('Init','init'), \ - ('Reconnect','reconnect'), ('Shutdown','shutdown') ] -BASIC_EVENTS = ['Invalid', 'Unknown-Datalink-Layer', 'Unknown-Layer3-Protocol', 'Non-IP-Packet', - 'Ethernet-Packet-Too-Short', 'Ethernet-Packet-Unknown', 'IP4-Packet-Too-Short', - 'IP4-Size-Smaller-Than-Header', 'IP4-Layer4-Payload-Detection-Failed', 'IP6-Packet-Too-Short', - 'IP6-Size-Smaller-Than-Header', 'IP6-Layer4-Payload-Detection-Failed', 'TCP-Packet-Too-Short', - 'UDP-Packet-Too-Short', 'Capture-Size-Smaller-Than-Packet-Size', 'Max-Flow-To-Track', - 'Flow-Memory-Allocation-Failed', 'NDPI-Flow-Memory-Allocation-Failed', - 'NDPI-ID-Memory-Allocation-Failed'] -PACKET_EVENTS = [ ('Invalid','invalid'), ('Packet','packet'), ('Packet-Flow','packet-flow') ] -FLOW_EVENTS = [ ('Invalid','invalid'), ('New','new'), ('End','end'), ('Idle','idle'), ('Guessed','guessed'), \ - ('Detected','detected'), ('Detection-Update','detection-update'), ('Not-Detected','not-detected') ] - -class TermColor: - WARNING = '\033[93m' - FAIL = '\033[91m' - BOLD = '\033[1m' - END = '\033[0m' - BLINK = "\x1b[5m" - -class nDPIsrvdSocket: - def __init__(self): - self.sock_family = None - - def connect(self, addr): - if type(addr) is tuple: - self.sock_family = socket.AF_INET - elif type(addr) is str: - self.sock_family = socket.AF_UNIX - else: - raise RuntimeError('Unsupported address type:: {}'.format(str(addr))) - - self.sock = socket.socket(self.sock_family, socket.SOCK_STREAM) - self.sock.connect(addr) - self.buffer = bytes() - self.msglen = 0 - self.digitlen = 0 - - def receive(self): - recvd = self.sock.recv(NETWORK_BUFFER_MAX_SIZE - len(self.buffer)) - - if len(recvd) == 0: - raise RuntimeError('socket connection broken') - self.buffer += recvd - - retval = [] - while self.msglen + self.digitlen < len(self.buffer): - - if self.msglen == 0: - starts_with_digits = re.match(r'(^\d+){', self.buffer[:NETWORK_BUFFER_MIN_SIZE].decode(errors='strict')) - if starts_with_digits is None: - if len(self.buffer) < NETWORK_BUFFER_MIN_SIZE: - break - raise RuntimeError('Invalid packet received: {}'.format(self.buffer)) - self.msglen = int(starts_with_digits[1]) - self.digitlen = len(starts_with_digits[1]) - - if len(self.buffer) >= self.msglen + self.digitlen: - recvd = self.buffer[self.digitlen:self.msglen + self.digitlen] - self.buffer = self.buffer[self.msglen + self.digitlen:] - retval += [(recvd,self.msglen,self.digitlen)] - - self.msglen = 0 - self.digitlen = 0 - - return retval - -class PcapPacket: - def __init__(self, flow_id=-1): - self.pktdump = None - self.was_dumped = False - self.was_detected = False - self.flow_id = flow_id - self.packets = [] - - def addPacket(self, pkt, pkt_type, pkt_ipoffset): - self.packets += [ ( pkt, pkt_type, pkt_ipoffset ) ] - - @staticmethod - def getIp(packet): - if packet[1] == PKT_TYPE_ETH_IP4: - return scapy.all.IP(packet[0][packet[2]:]) - elif packet[1] == PKT_TYPE_ETH_IP6: - return scapy.all.IPv6(packet[0][packet[2]:]) - else: - raise RuntimeError('packet type unknown: {}'.format(packet[1])) - - @staticmethod - def getTCPorUDP(packet): - p = PcapPacket.getIp(packet) - if p.haslayer(scapy.all.TCP): - return p.getlayer(scapy.all.TCP) - elif p.haslayer(scapy.all.UDP): - return p.getlayer(scapy.all.UDP) - else: - return None - - def detected(self): - self.was_detected = True - - def fin(self, filename_suffix): - if self.was_dumped is True: - return 'Flow already dumped.' - if self.was_detected is True: - return 'Flow detected.' - - emptyTCPorUDPcount = 0; - for packet in self.packets: - p = PcapPacket.getTCPorUDP(packet) - if p is not None: - if p.haslayer(scapy.all.Padding) and len(p.payload) - len(p[scapy.all.Padding]) == 0: - emptyTCPorUDPcount += 1 - if len(p.payload) == 0: - emptyTCPorUDPcount += 1 - - if emptyTCPorUDPcount == len(self.packets): - return 'Flow does not contain any packets with non-empty layer4 payload.' - - if self.pktdump is None: - if self.flow_id == -1: - self.pktdump = scapy.all.PcapWriter('packet-{}.pcap'.format(filename_suffix), - append=True, sync=True) - else: - self.pktdump = scapy.all.PcapWriter('flow-{}-{}.pcap'.format(filename_suffix, self.flow_id), - append=False, sync=True) - - for packet in self.packets: - self.pktdump.write(PcapPacket.getIp(packet)) - - self.pktdump.close() - self.was_dumped = True - - return 'Success.' - -def JsonParseBytes(json_bytes): - return json.loads(json_bytes.decode('ascii', errors='replace'), strict=False) - -class nDPIdEvent: - isValid = False - DaemonEventID = -1 - DaemonEventName = None - DaemonEventPrettyName = EVENT_UNKNOWN - BasicEventID = -1 - BasicEventName = None - BasicEventPrettyName = EVENT_UNKNOWN - PacketEventID = -1 - PacketEventName = None - PacketEventPrettyName = EVENT_UNKNOWN - FlowEventID = -1 - FlowEventName = None - FlowEventPrettyName = EVENT_UNKNOWN - - def validateEvent(self, event_id, event_name, list_of_event_tuples): - if self.isValid is True: - raise RuntimeError('nDPId event already validated. Multiple Events in one JSON strings are not allowed.\n' \ - '[EVENTS]\n' - 'current: {}\n' \ - 'daemon.: {}\n' \ - 'basic..: {}\n' \ - 'packet.: {}\n' \ - 'flow...: {}\n'.format(event_name, - self.DaemonEventName, self.BasicEventName, \ - self.PacketEventName, self.FlowEventName)) - - if type(event_id) is not int: - raise RuntimeError('Argument is not an Integer/EventID!') - - if event_id < 0 or event_id >= len(list_of_event_tuples): - raise RuntimeError('Unknown event id: {} aka {}.'.format(event_id, event_name)) - - if type(list_of_event_tuples[0]) == tuple and list_of_event_tuples[event_id][1] != event_name: - raise RuntimeError('Unknown event name: {}.'.format(event_name)) - - self.isValid = True - return list_of_event_tuples[event_id][0] if type(list_of_event_tuples[0]) == tuple \ - else list_of_event_tuples[event_id] - - def validateFlowEvent(self): - return self.validateEvent(self.FlowEventID, self.FlowEventName, FLOW_EVENTS) - - def validatePacketEvent(self): - return self.validateEvent(self.PacketEventID, self.PacketEventName, PACKET_EVENTS) - - def validateBasicEvent(self): - return self.validateEvent(self.BasicEventID, self.BasicEventName, BASIC_EVENTS) - - def validateDaemonEvent(self): - return self.validateEvent(self.DaemonEventID, self.DaemonEventName, DAEMON_EVENTS) - - @staticmethod - def validateJsonEventTypes(json_dict): - if type(json_dict) is not dict: - raise RuntimeError('Argument is not a dictionary!') - - nev = nDPIdEvent() - - if 'daemon_event_id' in json_dict: - nev.DaemonEventID = json_dict['daemon_event_id'] - nev.DaemonEventName = json_dict['daemon_event_name'] - nev.DaemonEventPrettyName = nev.validateDaemonEvent() - if 'basic_event_id' in json_dict: - nev.BasicEventID = json_dict['basic_event_id'] - nev.BasicEventName = json_dict['basic_event_name'] - nev.BasicEventPrettyName = nev.validateBasicEvent() - if 'packet_event_id' in json_dict: - nev.PacketEventID = json_dict['packet_event_id'] - nev.PacketEventName = json_dict['packet_event_name'] - nev.PacketEventPrettyName = nev.validatePacketEvent() - if 'flow_event_id' in json_dict: - nev.FlowEventID = json_dict['flow_event_id'] - nev.FlowEventName = json_dict['flow_event_name'] - nev.FlowEventPrettyName = nev.validateFlowEvent() - - return nev - -def defaultArgumentParser(): - parser = argparse.ArgumentParser(description='nDPIsrvd options', formatter_class=argparse.ArgumentDefaultsHelpFormatter) - parser.add_argument('--host', type=str, help='nDPIsrvd host IP') - parser.add_argument('--port', type=int, default=DEFAULT_PORT, help='nDPIsrvd TCP port') - parser.add_argument('--unix', type=str, help='nDPIsrvd unix socket path') - return parser - -def validateAddress(args): - address = None - - if args.host is None: - address_tcpip = (DEFAULT_HOST, DEFAULT_PORT) - else: - address_tcpip = (args.host, args.port) - - if args.unix is None: - address_unix = DEFAULT_UNIX - else: - address_unix = args.unix - - possible_sock_mode = 0 - try: - possible_sock_mode = os.stat(address_unix).st_mode - except: - pass - if stat.S_ISSOCK(possible_sock_mode): - address = address_unix - else: - address = address_tcpip - - return address diff --git a/contrib/update_jsmn.sh b/contrib/update_jsmn.sh deleted file mode 100755 index bc6a51ca1..000000000 --- a/contrib/update_jsmn.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env sh - -MYDIR="$(dirname ${0})" -cd "${MYDIR}/.." - -git subtree pull --squash --prefix=contrib/jsmn https://github.com/zserge/jsmn.git master diff --git a/dependencies/jsmn/.clang-format b/dependencies/jsmn/.clang-format new file mode 100644 index 000000000..3a5940ef6 --- /dev/null +++ b/dependencies/jsmn/.clang-format @@ -0,0 +1,90 @@ +--- +Language: Cpp +# BasedOnStyle: LLVM +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlinesLeft: false +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: false +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Attach +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +ColumnLimit: 80 +CommentPragmas: '^ IWYU pragma:' +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + - Regex: '^(<|"(gtest|isl|json)/)' + Priority: 3 + - Regex: '.*' + Priority: 1 +IndentCaseLabels: false +IndentWidth: 2 +IndentWrappedFunctionNames: false +KeepEmptyLinesAtTheStartOfBlocks: true +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Right +ReflowComments: true +SortIncludes: true +SpaceAfterCStyleCast: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp11 +TabWidth: 8 +UseTab: Never +... + diff --git a/dependencies/jsmn/.travis.yml b/dependencies/jsmn/.travis.yml new file mode 100644 index 000000000..1c8ebd327 --- /dev/null +++ b/dependencies/jsmn/.travis.yml @@ -0,0 +1,4 @@ +language: c +sudo: false +script: + - make test diff --git a/dependencies/jsmn/LICENSE b/dependencies/jsmn/LICENSE new file mode 100644 index 000000000..c84fb2e97 --- /dev/null +++ b/dependencies/jsmn/LICENSE @@ -0,0 +1,20 @@ +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. + diff --git a/dependencies/jsmn/Makefile b/dependencies/jsmn/Makefile new file mode 100644 index 000000000..dcbdd89d7 --- /dev/null +++ b/dependencies/jsmn/Makefile @@ -0,0 +1,36 @@ +# You can put your build options here +-include config.mk + +test: test_default test_strict test_links test_strict_links +test_default: test/tests.c jsmn.h + $(CC) $(CFLAGS) $(LDFLAGS) $< -o test/$@ + ./test/$@ +test_strict: test/tests.c jsmn.h + $(CC) -DJSMN_STRICT=1 $(CFLAGS) $(LDFLAGS) $< -o test/$@ + ./test/$@ +test_links: test/tests.c jsmn.h + $(CC) -DJSMN_PARENT_LINKS=1 $(CFLAGS) $(LDFLAGS) $< -o test/$@ + ./test/$@ +test_strict_links: test/tests.c jsmn.h + $(CC) -DJSMN_STRICT=1 -DJSMN_PARENT_LINKS=1 $(CFLAGS) $(LDFLAGS) $< -o test/$@ + ./test/$@ + +simple_example: example/simple.c jsmn.h + $(CC) $(LDFLAGS) $< -o $@ + +jsondump: example/jsondump.c jsmn.h + $(CC) $(LDFLAGS) $< -o $@ + +fmt: + clang-format -i jsmn.h test/*.[ch] example/*.[ch] + +lint: + clang-tidy jsmn.h --checks='*' + +clean: + rm -f *.o example/*.o + rm -f simple_example + rm -f jsondump + +.PHONY: clean test + diff --git a/dependencies/jsmn/README.md b/dependencies/jsmn/README.md new file mode 100644 index 000000000..f8249f3dd --- /dev/null +++ b/dependencies/jsmn/README.md @@ -0,0 +1,182 @@ +JSMN +==== + +[![Build Status](https://travis-ci.org/zserge/jsmn.svg?branch=master)](https://travis-ci.org/zserge/jsmn) + +jsmn (pronounced like 'jasmine') is a minimalistic JSON parser in C. It can be +easily integrated into resource-limited or embedded projects. + +You can find more information about JSON format at [json.org][1] + +Library sources are available at https://github.com/zserge/jsmn + +The web page with some information about jsmn can be found at +[http://zserge.com/jsmn.html][2] + +Philosophy +---------- + +Most JSON parsers offer you a bunch of functions to load JSON data, parse it +and extract any value by its name. jsmn proves that checking the correctness of +every JSON packet or allocating temporary objects to store parsed JSON fields +often is an overkill. + +JSON format itself is extremely simple, so why should we complicate it? + +jsmn is designed to be **robust** (it should work fine even with erroneous +data), **fast** (it should parse data on the fly), **portable** (no superfluous +dependencies or non-standard C extensions). And of course, **simplicity** is a +key feature - simple code style, simple algorithm, simple integration into +other projects. + +Features +-------- + +* compatible with C89 +* no dependencies (even libc!) +* highly portable (tested on x86/amd64, ARM, AVR) +* about 200 lines of code +* extremely small code footprint +* API contains only 2 functions +* no dynamic memory allocation +* incremental single-pass parsing +* library code is covered with unit-tests + +Design +------ + +The rudimentary jsmn object is a **token**. Let's consider a JSON string: + + '{ "name" : "Jack", "age" : 27 }' + +It holds the following tokens: + +* Object: `{ "name" : "Jack", "age" : 27}` (the whole object) +* Strings: `"name"`, `"Jack"`, `"age"` (keys and some values) +* Number: `27` + +In jsmn, tokens do not hold any data, but point to token boundaries in JSON +string instead. In the example above jsmn will create tokens like: Object +[0..31], String [3..7], String [12..16], String [20..23], Number [27..29]. + +Every jsmn token has a type, which indicates the type of corresponding JSON +token. jsmn supports the following token types: + +* Object - a container of key-value pairs, e.g.: + `{ "foo":"bar", "x":0.3 }` +* Array - a sequence of values, e.g.: + `[ 1, 2, 3 ]` +* String - a quoted sequence of chars, e.g.: `"foo"` +* Primitive - a number, a boolean (`true`, `false`) or `null` + +Besides start/end positions, jsmn tokens for complex types (like arrays +or objects) also contain a number of child items, so you can easily follow +object hierarchy. + +This approach provides enough information for parsing any JSON data and makes +it possible to use zero-copy techniques. + +Usage +----- + +Download `jsmn.h`, include it, done. + +``` +#include "jsmn.h" + +... +jsmn_parser p; +jsmntok_t t[128]; /* We expect no more than 128 JSON tokens */ + +jsmn_init(&p); +r = jsmn_parse(&p, s, strlen(s), t, 128); +``` + +Since jsmn is a single-header, header-only library, for more complex use cases +you might need to define additional macros. `#define JSMN_STATIC` hides all +jsmn API symbols by making them static. Also, if you want to include `jsmn.h` +from multiple C files, to avoid duplication of symbols you may define `JSMN_HEADER` macro. + +``` +/* In every .c file that uses jsmn include only declarations: */ +#define JSMN_HEADER +#include "jsmn.h" + +/* Additionally, create one jsmn.c file for jsmn implementation: */ +#include "jsmn.h" +``` + +API +--- + +Token types are described by `jsmntype_t`: + + typedef enum { + JSMN_UNDEFINED = 0, + JSMN_OBJECT = 1, + JSMN_ARRAY = 2, + JSMN_STRING = 3, + JSMN_PRIMITIVE = 4 + } jsmntype_t; + +**Note:** Unlike JSON data types, primitive tokens are not divided into +numbers, booleans and null, because one can easily tell the type using the +first character: + +* 't', 'f' - boolean +* 'n' - null +* '-', '0'..'9' - number + +Token is an object of `jsmntok_t` type: + + typedef struct { + jsmntype_t type; // Token type + int start; // Token start position + int end; // Token end position + int size; // Number of child (nested) tokens + } jsmntok_t; + +**Note:** string tokens point to the first character after +the opening quote and the previous symbol before final quote. This was made +to simplify string extraction from JSON data. + +All job is done by `jsmn_parser` object. You can initialize a new parser using: + + jsmn_parser parser; + jsmntok_t tokens[10]; + + jsmn_init(&parser); + + // js - pointer to JSON string + // tokens - an array of tokens available + // 10 - number of tokens available + jsmn_parse(&parser, js, strlen(js), tokens, 10); + +This will create a parser, and then it tries to parse up to 10 JSON tokens from +the `js` string. + +A non-negative return value of `jsmn_parse` is the number of tokens actually +used by the parser. +Passing NULL instead of the tokens array would not store parsing results, but +instead the function will return the number of tokens needed to parse the given +string. This can be useful if you don't know yet how many tokens to allocate. + +If something goes wrong, you will get an error. Error will be one of these: + +* `JSMN_ERROR_INVAL` - bad token, JSON string is corrupted +* `JSMN_ERROR_NOMEM` - not enough tokens, JSON string is too large +* `JSMN_ERROR_PART` - JSON string is too short, expecting more JSON data + +If you get `JSMN_ERROR_NOMEM`, you can re-allocate more tokens and call +`jsmn_parse` once more. If you read json data from the stream, you can +periodically call `jsmn_parse` and check if return value is `JSMN_ERROR_PART`. +You will get this error until you reach the end of JSON data. + +Other info +---------- + +This software is distributed under [MIT license](http://www.opensource.org/licenses/mit-license.php), + so feel free to integrate it in your commercial products. + +[1]: http://www.json.org/ +[2]: http://zserge.com/jsmn.html diff --git a/dependencies/jsmn/example/jsondump.c b/dependencies/jsmn/example/jsondump.c new file mode 100644 index 000000000..1eb620640 --- /dev/null +++ b/dependencies/jsmn/example/jsondump.c @@ -0,0 +1,134 @@ +#include "../jsmn.h" +#include +#include +#include +#include +#include + +/* Function realloc_it() is a wrapper function for standard realloc() + * with one difference - it frees old memory pointer in case of realloc + * failure. Thus, DO NOT use old data pointer in anyway after call to + * realloc_it(). If your code has some kind of fallback algorithm if + * memory can't be re-allocated - use standard realloc() instead. + */ +static inline void *realloc_it(void *ptrmem, size_t size) { + void *p = realloc(ptrmem, size); + if (!p) { + free(ptrmem); + fprintf(stderr, "realloc(): errno=%d\n", errno); + } + return p; +} + +/* + * An example of reading JSON from stdin and printing its content to stdout. + * The output looks like YAML, but I'm not sure if it's really compatible. + */ + +static int dump(const char *js, jsmntok_t *t, size_t count, int indent) { + int i, j, k; + jsmntok_t *key; + if (count == 0) { + return 0; + } + if (t->type == JSMN_PRIMITIVE) { + printf("%.*s", t->end - t->start, js + t->start); + return 1; + } else if (t->type == JSMN_STRING) { + printf("'%.*s'", t->end - t->start, js + t->start); + return 1; + } else if (t->type == JSMN_OBJECT) { + printf("\n"); + j = 0; + for (i = 0; i < t->size; i++) { + for (k = 0; k < indent; k++) { + printf(" "); + } + key = t + 1 + j; + j += dump(js, key, count - j, indent + 1); + if (key->size > 0) { + printf(": "); + j += dump(js, t + 1 + j, count - j, indent + 1); + } + printf("\n"); + } + return j + 1; + } else if (t->type == JSMN_ARRAY) { + j = 0; + printf("\n"); + for (i = 0; i < t->size; i++) { + for (k = 0; k < indent - 1; k++) { + printf(" "); + } + printf(" - "); + j += dump(js, t + 1 + j, count - j, indent + 1); + printf("\n"); + } + return j + 1; + } + return 0; +} + +int main() { + int r; + int eof_expected = 0; + char *js = NULL; + size_t jslen = 0; + char buf[BUFSIZ]; + + jsmn_parser p; + jsmntok_t *tok; + size_t tokcount = 2; + + /* Prepare parser */ + jsmn_init(&p); + + /* Allocate some tokens as a start */ + tok = malloc(sizeof(*tok) * tokcount); + if (tok == NULL) { + fprintf(stderr, "malloc(): errno=%d\n", errno); + return 3; + } + + for (;;) { + /* Read another chunk */ + r = fread(buf, 1, sizeof(buf), stdin); + if (r < 0) { + fprintf(stderr, "fread(): %d, errno=%d\n", r, errno); + return 1; + } + if (r == 0) { + if (eof_expected != 0) { + return 0; + } else { + fprintf(stderr, "fread(): unexpected EOF\n"); + return 2; + } + } + + js = realloc_it(js, jslen + r + 1); + if (js == NULL) { + return 3; + } + strncpy(js + jslen, buf, r); + jslen = jslen + r; + + again: + r = jsmn_parse(&p, js, jslen, tok, tokcount); + if (r < 0) { + if (r == JSMN_ERROR_NOMEM) { + tokcount = tokcount * 2; + tok = realloc_it(tok, sizeof(*tok) * tokcount); + if (tok == NULL) { + return 3; + } + goto again; + } + } else { + dump(js, tok, p.toknext, 0); + eof_expected = 1; + } + } + + return EXIT_SUCCESS; +} diff --git a/dependencies/jsmn/example/simple.c b/dependencies/jsmn/example/simple.c new file mode 100644 index 000000000..1254575a1 --- /dev/null +++ b/dependencies/jsmn/example/simple.c @@ -0,0 +1,77 @@ +#include "../jsmn.h" +#include +#include +#include + +/* + * A small example of jsmn parsing when JSON structure is known and number of + * tokens is predictable. + */ + +static const char *JSON_STRING = + "{\"user\": \"johndoe\", \"admin\": false, \"uid\": 1000,\n " + "\"groups\": [\"users\", \"wheel\", \"audio\", \"video\"]}"; + +static int jsoneq(const char *json, jsmntok_t *tok, const char *s) { + if (tok->type == JSMN_STRING && (int)strlen(s) == tok->end - tok->start && + strncmp(json + tok->start, s, tok->end - tok->start) == 0) { + return 0; + } + return -1; +} + +int main() { + int i; + int r; + jsmn_parser p; + jsmntok_t t[128]; /* We expect no more than 128 tokens */ + + jsmn_init(&p); + r = jsmn_parse(&p, JSON_STRING, strlen(JSON_STRING), t, + sizeof(t) / sizeof(t[0])); + if (r < 0) { + printf("Failed to parse JSON: %d\n", r); + return 1; + } + + /* Assume the top-level element is an object */ + if (r < 1 || t[0].type != JSMN_OBJECT) { + printf("Object expected\n"); + return 1; + } + + /* Loop over all keys of the root object */ + for (i = 1; i < r; i++) { + if (jsoneq(JSON_STRING, &t[i], "user") == 0) { + /* We may use strndup() to fetch string value */ + printf("- User: %.*s\n", t[i + 1].end - t[i + 1].start, + JSON_STRING + t[i + 1].start); + i++; + } else if (jsoneq(JSON_STRING, &t[i], "admin") == 0) { + /* We may additionally check if the value is either "true" or "false" */ + printf("- Admin: %.*s\n", t[i + 1].end - t[i + 1].start, + JSON_STRING + t[i + 1].start); + i++; + } else if (jsoneq(JSON_STRING, &t[i], "uid") == 0) { + /* We may want to do strtol() here to get numeric value */ + printf("- UID: %.*s\n", t[i + 1].end - t[i + 1].start, + JSON_STRING + t[i + 1].start); + i++; + } else if (jsoneq(JSON_STRING, &t[i], "groups") == 0) { + int j; + printf("- Groups:\n"); + if (t[i + 1].type != JSMN_ARRAY) { + continue; /* We expect groups to be an array of strings */ + } + for (j = 0; j < t[i + 1].size; j++) { + jsmntok_t *g = &t[i + j + 2]; + printf(" * %.*s\n", g->end - g->start, JSON_STRING + g->start); + } + i += t[i + 1].size + 1; + } else { + printf("Unexpected key: %.*s\n", t[i].end - t[i].start, + JSON_STRING + t[i].start); + } + } + return EXIT_SUCCESS; +} diff --git a/dependencies/jsmn/jsmn.h b/dependencies/jsmn/jsmn.h new file mode 100644 index 000000000..3178dcc97 --- /dev/null +++ b/dependencies/jsmn/jsmn.h @@ -0,0 +1,471 @@ +/* + * MIT License + * + * Copyright (c) 2010 Serge 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 + +#ifdef JSMN_STATIC +#define JSMN_API static +#else +#define JSMN_API extern +#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. + * type type (object, array, string etc.) + * start start position in JSON data string + * end end position in JSON data string + */ +typedef struct jsmntok { + 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 jsmn_parser { + 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 + */ +JSMN_API 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. + */ +JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len, + jsmntok_t *tokens, const unsigned int num_tokens); + +#ifndef JSMN_HEADER +/** + * Allocates a fresh unused token from the token pool. + */ +static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens, + const 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, const jsmntype_t type, + const int start, const 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, + const size_t len, jsmntok_t *tokens, + const 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; + default: + /* to quiet a warning from gcc*/ + break; + } + 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, + const size_t len, jsmntok_t *tokens, + const 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. + */ +JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len, + jsmntok_t *tokens, const 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) { + jsmntok_t *t = &tokens[parser->toksuper]; +#ifdef JSMN_STRICT + /* In strict mode an object or array can't become a key */ + if (t->type == JSMN_OBJECT) { + return JSMN_ERROR_INVAL; + } +#endif + t->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) { + if (token->type != type || parser->toksuper == -1) { + return JSMN_ERROR_INVAL; + } + 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) { + const 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. + */ +JSMN_API void jsmn_init(jsmn_parser *parser) { + parser->pos = 0; + parser->toknext = 0; + parser->toksuper = -1; +} + +#endif /* JSMN_HEADER */ + +#ifdef __cplusplus +} +#endif + +#endif /* JSMN_H */ diff --git a/dependencies/jsmn/library.json b/dependencies/jsmn/library.json new file mode 100644 index 000000000..8e2f5c257 --- /dev/null +++ b/dependencies/jsmn/library.json @@ -0,0 +1,16 @@ +{ + "name": "jsmn", + "keywords": "json", + "description": "Minimalistic JSON parser/tokenizer in C. It can be easily integrated into resource-limited or embedded projects", + "repository": + { + "type": "git", + "url": "https://github.com/zserge/jsmn.git" + }, + "frameworks": "*", + "platforms": "*", + "examples": [ + "example/*.c" + ], + "exclude": "test" +} diff --git a/dependencies/jsmn/test/test.h b/dependencies/jsmn/test/test.h new file mode 100644 index 000000000..a1c0957a7 --- /dev/null +++ b/dependencies/jsmn/test/test.h @@ -0,0 +1,31 @@ +#ifndef __TEST_H__ +#define __TEST_H__ + +static int test_passed = 0; +static int test_failed = 0; + +/* Terminate current test with error */ +#define fail() return __LINE__ + +/* Successful end of the test case */ +#define done() return 0 + +/* Check single condition */ +#define check(cond) \ + do { \ + if (!(cond)) \ + fail(); \ + } while (0) + +/* Test runner */ +static void test(int (*func)(void), const char *name) { + int r = func(); + if (r == 0) { + test_passed++; + } else { + test_failed++; + printf("FAILED: %s (at line %d)\n", name, r); + } +} + +#endif /* __TEST_H__ */ diff --git a/dependencies/jsmn/test/tests.c b/dependencies/jsmn/test/tests.c new file mode 100644 index 000000000..d8a4d922e --- /dev/null +++ b/dependencies/jsmn/test/tests.c @@ -0,0 +1,359 @@ +#include +#include +#include +#include + +#include "test.h" +#include "testutil.h" + +int test_empty(void) { + check(parse("{}", 1, 1, JSMN_OBJECT, 0, 2, 0)); + check(parse("[]", 1, 1, JSMN_ARRAY, 0, 2, 0)); + check(parse("[{},{}]", 3, 3, JSMN_ARRAY, 0, 7, 2, JSMN_OBJECT, 1, 3, 0, + JSMN_OBJECT, 4, 6, 0)); + return 0; +} + +int test_object(void) { + check(parse("{\"a\":0}", 3, 3, JSMN_OBJECT, 0, 7, 1, JSMN_STRING, "a", 1, + JSMN_PRIMITIVE, "0")); + check(parse("{\"a\":[]}", 3, 3, JSMN_OBJECT, 0, 8, 1, JSMN_STRING, "a", 1, + JSMN_ARRAY, 5, 7, 0)); + check(parse("{\"a\":{},\"b\":{}}", 5, 5, JSMN_OBJECT, -1, -1, 2, JSMN_STRING, + "a", 1, JSMN_OBJECT, -1, -1, 0, JSMN_STRING, "b", 1, JSMN_OBJECT, + -1, -1, 0)); + check(parse("{\n \"Day\": 26,\n \"Month\": 9,\n \"Year\": 12\n }", 7, 7, + JSMN_OBJECT, -1, -1, 3, JSMN_STRING, "Day", 1, JSMN_PRIMITIVE, + "26", JSMN_STRING, "Month", 1, JSMN_PRIMITIVE, "9", JSMN_STRING, + "Year", 1, JSMN_PRIMITIVE, "12")); + check(parse("{\"a\": 0, \"b\": \"c\"}", 5, 5, JSMN_OBJECT, -1, -1, 2, + JSMN_STRING, "a", 1, JSMN_PRIMITIVE, "0", JSMN_STRING, "b", 1, + JSMN_STRING, "c", 0)); + +#ifdef JSMN_STRICT + check(parse("{\"a\"\n0}", JSMN_ERROR_INVAL, 3)); + check(parse("{\"a\", 0}", JSMN_ERROR_INVAL, 3)); + check(parse("{\"a\": {2}}", JSMN_ERROR_INVAL, 3)); + check(parse("{\"a\": {2: 3}}", JSMN_ERROR_INVAL, 3)); + check(parse("{\"a\": {\"a\": 2 3}}", JSMN_ERROR_INVAL, 5)); +/* FIXME */ +/*check(parse("{\"a\"}", JSMN_ERROR_INVAL, 2));*/ +/*check(parse("{\"a\": 1, \"b\"}", JSMN_ERROR_INVAL, 4));*/ +/*check(parse("{\"a\",\"b\":1}", JSMN_ERROR_INVAL, 4));*/ +/*check(parse("{\"a\":1,}", JSMN_ERROR_INVAL, 4));*/ +/*check(parse("{\"a\":\"b\":\"c\"}", JSMN_ERROR_INVAL, 4));*/ +/*check(parse("{,}", JSMN_ERROR_INVAL, 4));*/ +#endif + return 0; +} + +int test_array(void) { + /* FIXME */ + /*check(parse("[10}", JSMN_ERROR_INVAL, 3));*/ + /*check(parse("[1,,3]", JSMN_ERROR_INVAL, 3)*/ + check(parse("[10]", 2, 2, JSMN_ARRAY, -1, -1, 1, JSMN_PRIMITIVE, "10")); + check(parse("{\"a\": 1]", JSMN_ERROR_INVAL, 3)); + /* FIXME */ + /*check(parse("[\"a\": 1]", JSMN_ERROR_INVAL, 3));*/ + return 0; +} + +int test_primitive(void) { + check(parse("{\"boolVar\" : true }", 3, 3, JSMN_OBJECT, -1, -1, 1, + JSMN_STRING, "boolVar", 1, JSMN_PRIMITIVE, "true")); + check(parse("{\"boolVar\" : false }", 3, 3, JSMN_OBJECT, -1, -1, 1, + JSMN_STRING, "boolVar", 1, JSMN_PRIMITIVE, "false")); + check(parse("{\"nullVar\" : null }", 3, 3, JSMN_OBJECT, -1, -1, 1, + JSMN_STRING, "nullVar", 1, JSMN_PRIMITIVE, "null")); + check(parse("{\"intVar\" : 12}", 3, 3, JSMN_OBJECT, -1, -1, 1, JSMN_STRING, + "intVar", 1, JSMN_PRIMITIVE, "12")); + check(parse("{\"floatVar\" : 12.345}", 3, 3, JSMN_OBJECT, -1, -1, 1, + JSMN_STRING, "floatVar", 1, JSMN_PRIMITIVE, "12.345")); + return 0; +} + +int test_string(void) { + check(parse("{\"strVar\" : \"hello world\"}", 3, 3, JSMN_OBJECT, -1, -1, 1, + JSMN_STRING, "strVar", 1, JSMN_STRING, "hello world", 0)); + check(parse("{\"strVar\" : \"escapes: \\/\\r\\n\\t\\b\\f\\\"\\\\\"}", 3, 3, + JSMN_OBJECT, -1, -1, 1, JSMN_STRING, "strVar", 1, JSMN_STRING, + "escapes: \\/\\r\\n\\t\\b\\f\\\"\\\\", 0)); + check(parse("{\"strVar\": \"\"}", 3, 3, JSMN_OBJECT, -1, -1, 1, JSMN_STRING, + "strVar", 1, JSMN_STRING, "", 0)); + check(parse("{\"a\":\"\\uAbcD\"}", 3, 3, JSMN_OBJECT, -1, -1, 1, JSMN_STRING, + "a", 1, JSMN_STRING, "\\uAbcD", 0)); + check(parse("{\"a\":\"str\\u0000\"}", 3, 3, JSMN_OBJECT, -1, -1, 1, + JSMN_STRING, "a", 1, JSMN_STRING, "str\\u0000", 0)); + check(parse("{\"a\":\"\\uFFFFstr\"}", 3, 3, JSMN_OBJECT, -1, -1, 1, + JSMN_STRING, "a", 1, JSMN_STRING, "\\uFFFFstr", 0)); + check(parse("{\"a\":[\"\\u0280\"]}", 4, 4, JSMN_OBJECT, -1, -1, 1, + JSMN_STRING, "a", 1, JSMN_ARRAY, -1, -1, 1, JSMN_STRING, + "\\u0280", 0)); + + check(parse("{\"a\":\"str\\uFFGFstr\"}", JSMN_ERROR_INVAL, 3)); + check(parse("{\"a\":\"str\\u@FfF\"}", JSMN_ERROR_INVAL, 3)); + check(parse("{{\"a\":[\"\\u028\"]}", JSMN_ERROR_INVAL, 4)); + return 0; +} + +int test_partial_string(void) { + int r; + unsigned long i; + jsmn_parser p; + jsmntok_t tok[5]; + const char *js = "{\"x\": \"va\\\\ue\", \"y\": \"value y\"}"; + + jsmn_init(&p); + for (i = 1; i <= strlen(js); i++) { + r = jsmn_parse(&p, js, i, tok, sizeof(tok) / sizeof(tok[0])); + if (i == strlen(js)) { + check(r == 5); + check(tokeq(js, tok, 5, JSMN_OBJECT, -1, -1, 2, JSMN_STRING, "x", 1, + JSMN_STRING, "va\\\\ue", 0, JSMN_STRING, "y", 1, JSMN_STRING, + "value y", 0)); + } else { + check(r == JSMN_ERROR_PART); + } + } + return 0; +} + +int test_partial_array(void) { +#ifdef JSMN_STRICT + int r; + unsigned long i; + jsmn_parser p; + jsmntok_t tok[10]; + const char *js = "[ 1, true, [123, \"hello\"]]"; + + jsmn_init(&p); + for (i = 1; i <= strlen(js); i++) { + r = jsmn_parse(&p, js, i, tok, sizeof(tok) / sizeof(tok[0])); + if (i == strlen(js)) { + check(r == 6); + check(tokeq(js, tok, 6, JSMN_ARRAY, -1, -1, 3, JSMN_PRIMITIVE, "1", + JSMN_PRIMITIVE, "true", JSMN_ARRAY, -1, -1, 2, JSMN_PRIMITIVE, + "123", JSMN_STRING, "hello", 0)); + } else { + check(r == JSMN_ERROR_PART); + } + } +#endif + return 0; +} + +int test_array_nomem(void) { + int i; + int r; + jsmn_parser p; + jsmntok_t toksmall[10], toklarge[10]; + const char *js; + + js = " [ 1, true, [123, \"hello\"]]"; + + for (i = 0; i < 6; i++) { + jsmn_init(&p); + memset(toksmall, 0, sizeof(toksmall)); + memset(toklarge, 0, sizeof(toklarge)); + r = jsmn_parse(&p, js, strlen(js), toksmall, i); + check(r == JSMN_ERROR_NOMEM); + + memcpy(toklarge, toksmall, sizeof(toksmall)); + + r = jsmn_parse(&p, js, strlen(js), toklarge, 10); + check(r >= 0); + check(tokeq(js, toklarge, 4, JSMN_ARRAY, -1, -1, 3, JSMN_PRIMITIVE, "1", + JSMN_PRIMITIVE, "true", JSMN_ARRAY, -1, -1, 2, JSMN_PRIMITIVE, + "123", JSMN_STRING, "hello", 0)); + } + return 0; +} + +int test_unquoted_keys(void) { +#ifndef JSMN_STRICT + int r; + jsmn_parser p; + jsmntok_t tok[10]; + const char *js; + + jsmn_init(&p); + js = "key1: \"value\"\nkey2 : 123"; + + r = jsmn_parse(&p, js, strlen(js), tok, 10); + check(r >= 0); + check(tokeq(js, tok, 4, JSMN_PRIMITIVE, "key1", JSMN_STRING, "value", 0, + JSMN_PRIMITIVE, "key2", JSMN_PRIMITIVE, "123")); +#endif + return 0; +} + +int test_issue_22(void) { + int r; + jsmn_parser p; + jsmntok_t tokens[128]; + const char *js; + + js = + "{ \"height\":10, \"layers\":[ { \"data\":[6,6], \"height\":10, " + "\"name\":\"Calque de Tile 1\", \"opacity\":1, \"type\":\"tilelayer\", " + "\"visible\":true, \"width\":10, \"x\":0, \"y\":0 }], " + "\"orientation\":\"orthogonal\", \"properties\": { }, \"tileheight\":32, " + "\"tilesets\":[ { \"firstgid\":1, \"image\":\"..\\/images\\/tiles.png\", " + "\"imageheight\":64, \"imagewidth\":160, \"margin\":0, " + "\"name\":\"Tiles\", " + "\"properties\":{}, \"spacing\":0, \"tileheight\":32, \"tilewidth\":32 " + "}], " + "\"tilewidth\":32, \"version\":1, \"width\":10 }"; + jsmn_init(&p); + r = jsmn_parse(&p, js, strlen(js), tokens, 128); + check(r >= 0); + return 0; +} + +int test_issue_27(void) { + const char *js = + "{ \"name\" : \"Jack\", \"age\" : 27 } { \"name\" : \"Anna\", "; + check(parse(js, JSMN_ERROR_PART, 8)); + return 0; +} + +int test_input_length(void) { + const char *js; + int r; + jsmn_parser p; + jsmntok_t tokens[10]; + + js = "{\"a\": 0}garbage"; + + jsmn_init(&p); + r = jsmn_parse(&p, js, 8, tokens, 10); + check(r == 3); + check(tokeq(js, tokens, 3, JSMN_OBJECT, -1, -1, 1, JSMN_STRING, "a", 1, + JSMN_PRIMITIVE, "0")); + return 0; +} + +int test_count(void) { + jsmn_parser p; + const char *js; + + js = "{}"; + jsmn_init(&p); + check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 1); + + js = "[]"; + jsmn_init(&p); + check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 1); + + js = "[[]]"; + jsmn_init(&p); + check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 2); + + js = "[[], []]"; + jsmn_init(&p); + check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 3); + + js = "[[], []]"; + jsmn_init(&p); + check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 3); + + js = "[[], [[]], [[], []]]"; + jsmn_init(&p); + check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 7); + + js = "[\"a\", [[], []]]"; + jsmn_init(&p); + check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 5); + + js = "[[], \"[], [[]]\", [[]]]"; + jsmn_init(&p); + check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 5); + + js = "[1, 2, 3]"; + jsmn_init(&p); + check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 4); + + js = "[1, 2, [3, \"a\"], null]"; + jsmn_init(&p); + check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 7); + + return 0; +} + +int test_nonstrict(void) { +#ifndef JSMN_STRICT + const char *js; + js = "a: 0garbage"; + check(parse(js, 2, 2, JSMN_PRIMITIVE, "a", JSMN_PRIMITIVE, "0garbage")); + + js = "Day : 26\nMonth : Sep\n\nYear: 12"; + check(parse(js, 6, 6, JSMN_PRIMITIVE, "Day", JSMN_PRIMITIVE, "26", + JSMN_PRIMITIVE, "Month", JSMN_PRIMITIVE, "Sep", JSMN_PRIMITIVE, + "Year", JSMN_PRIMITIVE, "12")); + + /* nested {s don't cause a parse error. */ + js = "\"key {1\": 1234"; + check(parse(js, 2, 2, JSMN_STRING, "key {1", 1, JSMN_PRIMITIVE, "1234")); + +#endif + return 0; +} + +int test_unmatched_brackets(void) { + const char *js; + js = "\"key 1\": 1234}"; + check(parse(js, JSMN_ERROR_INVAL, 2)); + js = "{\"key 1\": 1234"; + check(parse(js, JSMN_ERROR_PART, 3)); + js = "{\"key 1\": 1234}}"; + check(parse(js, JSMN_ERROR_INVAL, 3)); + js = "\"key 1\"}: 1234"; + check(parse(js, JSMN_ERROR_INVAL, 3)); + js = "{\"key {1\": 1234}"; + check(parse(js, 3, 3, JSMN_OBJECT, 0, 16, 1, JSMN_STRING, "key {1", 1, + JSMN_PRIMITIVE, "1234")); + js = "{\"key 1\":{\"key 2\": 1234}"; + check(parse(js, JSMN_ERROR_PART, 5)); + return 0; +} + +int test_object_key(void) { + const char *js; + + js = "{\"key\": 1}"; + check(parse(js, 3, 3, JSMN_OBJECT, 0, 10, 1, JSMN_STRING, "key", 1, + JSMN_PRIMITIVE, "1")); +#ifdef JSMN_STRICT + js = "{true: 1}"; + check(parse(js, JSMN_ERROR_INVAL, 3)); + js = "{1: 1}"; + check(parse(js, JSMN_ERROR_INVAL, 3)); + js = "{{\"key\": 1}: 2}"; + check(parse(js, JSMN_ERROR_INVAL, 5)); + js = "{[1,2]: 2}"; + check(parse(js, JSMN_ERROR_INVAL, 5)); +#endif + return 0; +} + +int main(void) { + test(test_empty, "test for a empty JSON objects/arrays"); + test(test_object, "test for a JSON objects"); + test(test_array, "test for a JSON arrays"); + test(test_primitive, "test primitive JSON data types"); + test(test_string, "test string JSON data types"); + + test(test_partial_string, "test partial JSON string parsing"); + test(test_partial_array, "test partial array reading"); + test(test_array_nomem, "test array reading with a smaller number of tokens"); + test(test_unquoted_keys, "test unquoted keys (like in JavaScript)"); + test(test_input_length, "test strings that are not null-terminated"); + test(test_issue_22, "test issue #22"); + test(test_issue_27, "test issue #27"); + test(test_count, "test tokens count estimation"); + test(test_nonstrict, "test for non-strict mode"); + test(test_unmatched_brackets, "test for unmatched brackets"); + test(test_object_key, "test for key type"); + printf("\nPASSED: %d\nFAILED: %d\n", test_passed, test_failed); + return (test_failed > 0); +} diff --git a/dependencies/jsmn/test/testutil.h b/dependencies/jsmn/test/testutil.h new file mode 100644 index 000000000..bdee13934 --- /dev/null +++ b/dependencies/jsmn/test/testutil.h @@ -0,0 +1,96 @@ +#ifndef __TEST_UTIL_H__ +#define __TEST_UTIL_H__ + +#include "../jsmn.h" + +static int vtokeq(const char *s, jsmntok_t *t, unsigned long numtok, + va_list ap) { + if (numtok > 0) { + unsigned long i; + int start, end, size; + jsmntype_t type; + char *value; + + size = -1; + value = NULL; + for (i = 0; i < numtok; i++) { + type = va_arg(ap, jsmntype_t); + if (type == JSMN_STRING) { + value = va_arg(ap, char *); + size = va_arg(ap, int); + start = end = -1; + } else if (type == JSMN_PRIMITIVE) { + value = va_arg(ap, char *); + start = end = size = -1; + } else { + start = va_arg(ap, int); + end = va_arg(ap, int); + size = va_arg(ap, int); + value = NULL; + } + if (t[i].type != type) { + printf("token %lu type is %d, not %d\n", i, t[i].type, type); + return 0; + } + if (start != -1 && end != -1) { + if (t[i].start != start) { + printf("token %lu start is %d, not %d\n", i, t[i].start, start); + return 0; + } + if (t[i].end != end) { + printf("token %lu end is %d, not %d\n", i, t[i].end, end); + return 0; + } + } + if (size != -1 && t[i].size != size) { + printf("token %lu size is %d, not %d\n", i, t[i].size, size); + return 0; + } + + if (s != NULL && value != NULL) { + const char *p = s + t[i].start; + if (strlen(value) != (unsigned long)(t[i].end - t[i].start) || + strncmp(p, value, t[i].end - t[i].start) != 0) { + printf("token %lu value is %.*s, not %s\n", i, t[i].end - t[i].start, + s + t[i].start, value); + return 0; + } + } + } + } + return 1; +} + +static int tokeq(const char *s, jsmntok_t *tokens, unsigned long numtok, ...) { + int ok; + va_list args; + va_start(args, numtok); + ok = vtokeq(s, tokens, numtok, args); + va_end(args); + return ok; +} + +static int parse(const char *s, int status, unsigned long numtok, ...) { + int r; + int ok = 1; + va_list args; + jsmn_parser p; + jsmntok_t *t = malloc(numtok * sizeof(jsmntok_t)); + + jsmn_init(&p); + r = jsmn_parse(&p, s, strlen(s), t, numtok); + if (r != status) { + printf("status is %d, not %d\n", r, status); + return 0; + } + + if (status >= 0) { + va_start(args, numtok); + ok = vtokeq(s, t, numtok, args); + va_end(args); + } + free(t); + return ok; +} + +#endif /* __TEST_UTIL_H__ */ diff --git a/dependencies/nDPIsrvd.h b/dependencies/nDPIsrvd.h new file mode 100644 index 000000000..320fdac08 --- /dev/null +++ b/dependencies/nDPIsrvd.h @@ -0,0 +1,223 @@ +#ifndef NDPISRVD_H +#define NDPISRVD_H 1 + +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "jsmn/jsmn.h" + +struct nDPIsrvd_socket +{ + int fd; + int socket_family; + + union { + struct + { + char const * dst_ip; + unsigned short dst_port; + } ip_socket; + struct + { + char * path; + } unix_socket; + } address; + + struct + { + char raw[NETWORK_BUFFER_MAX_SIZE]; + size_t used; + char * json_string; + size_t json_string_start; + unsigned long long int json_string_length; + } buffer; + + struct + { + jsmn_parser parser; + jsmntok_t tokens[128]; + int current_token; + int tokens_found; + } jsmn; +}; + +#define FIRST_ENUM_VALUE 1 + +enum nDPIsrvd_connect_return +{ + CONNECT_OK = FIRST_ENUM_VALUE, + CONNECT_ERROR_SOCKET, + CONNECT_ERROR_PTON, + CONNECT_ERROR, + CONNECT_LAST_ENUM_VALUE +}; + +enum nDPIsrvd_read_return +{ + READ_OK = CONNECT_LAST_ENUM_VALUE, + READ_PEER_DISCONNECT, + READ_ERROR, + READ_LAST_ENUM_VALUE +}; + +enum nDPIsrvd_parse_return +{ + PARSE_OK = READ_LAST_ENUM_VALUE, + PARSE_INVALID_OPENING_CHAR, + PARSE_SIZE_EXCEEDS_CONVERSION_LIMIT, + PARSE_SIZE_MISSING, + PARSE_STRING_TOO_BIG, + PARSE_INVALID_CLOSING_CHAR, + PARSE_JSMN_ERROR, + PARSE_LAST_ENUM_VALUE +}; + +enum nDPIsrvd_callback_return +{ + CALLBACK_OK = PARSE_LAST_ENUM_VALUE, + CALLBACK_ERROR, + CALLBACK_LAST_ENUM_VALUE +}; + +typedef enum nDPIsrvd_callback_return (*json_callback)(struct nDPIsrvd_socket * const sock, void * user_data); + +static inline struct nDPIsrvd_socket * nDPIsrvd_init(void) +{ + struct nDPIsrvd_socket * sock = (struct nDPIsrvd_socket *)malloc(sizeof(*sock)); + + if (sock != NULL) + { + sock->fd = -1; + sock->socket_family = -1; + } + return sock; +} + +static inline enum nDPIsrvd_connect_return nDPIsrvd_connect_ip(struct nDPIsrvd_socket * const sock, + char const * dst_ip, + unsigned short dst_port) +{ + struct sockaddr_in remote_addr = {}; + + sock->socket_family = remote_addr.sin_family = AF_INET; + sock->fd = socket(sock->socket_family, SOCK_STREAM, 0); + + if (sock->fd < 0) + { + return CONNECT_ERROR_SOCKET; + } + + if (inet_pton(sock->socket_family, &dst_ip[0], &remote_addr.sin_addr) != 1) + { + return CONNECT_ERROR_PTON; + } + remote_addr.sin_port = htons(dst_port); + + if (connect(sock->fd, (struct sockaddr *)&remote_addr, sizeof(remote_addr)) != 0) + { + return CONNECT_ERROR; + } + + return CONNECT_OK; +} + +static inline enum nDPIsrvd_connect_return nDPIsrvd_connect_unix(struct nDPIsrvd_socket * const sock, + char const * const path) +{ + (void)sock; + (void)path; + + return CONNECT_OK; +} + +static inline enum nDPIsrvd_read_return nDPIsrvd_read(struct nDPIsrvd_socket * const sock) +{ + ssize_t bytes_read = + read(sock->fd, sock->buffer.raw + sock->buffer.used, sizeof(sock->buffer.raw) - sock->buffer.used); + + if (bytes_read == 0) + { + return READ_PEER_DISCONNECT; + } + if (bytes_read < 0) + { + return READ_ERROR; + } + + sock->buffer.used += bytes_read; + + return READ_OK; +} + +static inline enum nDPIsrvd_parse_return nDPIsrvd_parse(struct nDPIsrvd_socket * const sock, + json_callback cb, + void * user_data) +{ + while (sock->buffer.used >= nDPIsrvd_JSON_BYTES + 1) + { + if (sock->buffer.raw[nDPIsrvd_JSON_BYTES] != '{') + { + return PARSE_INVALID_OPENING_CHAR; + } + + errno = 0; + sock->buffer.json_string_length = strtoull((const char *)sock->buffer.raw, &sock->buffer.json_string, 10); + sock->buffer.json_string_length += sock->buffer.json_string - sock->buffer.raw; + sock->buffer.json_string_start = sock->buffer.json_string - sock->buffer.raw; + + if (errno == ERANGE) + { + return PARSE_SIZE_EXCEEDS_CONVERSION_LIMIT; + } + if (sock->buffer.json_string == sock->buffer.raw) + { + return PARSE_SIZE_MISSING; + } + if (sock->buffer.json_string_length > sizeof(sock->buffer.raw)) + { + return PARSE_STRING_TOO_BIG; + } + if (sock->buffer.json_string_length > sock->buffer.used) + { + break; + } + + if (sock->buffer.raw[sock->buffer.json_string_length - 1] != '}') + { + return PARSE_INVALID_CLOSING_CHAR; + } + + jsmn_init(&sock->jsmn.parser); + sock->jsmn.tokens_found = jsmn_parse(&sock->jsmn.parser, + (char *)(sock->buffer.raw + sock->buffer.json_string_start), + sock->buffer.json_string_length - sock->buffer.json_string_start, + sock->jsmn.tokens, + sizeof(sock->jsmn.tokens) / sizeof(sock->jsmn.tokens[0])); + if (sock->jsmn.tokens_found < 0 || sock->jsmn.tokens[0].type != JSMN_OBJECT) + { + return PARSE_JSMN_ERROR; + } + + for (sock->jsmn.current_token = 1; sock->jsmn.current_token < sock->jsmn.tokens_found; + sock->jsmn.current_token++) + { + cb(sock, user_data); + } + + memmove(sock->buffer.raw, + sock->buffer.raw + sock->buffer.json_string_length, + sock->buffer.used - sock->buffer.json_string_length); + sock->buffer.used -= sock->buffer.json_string_length; + sock->buffer.json_string_length = 0; + sock->buffer.json_string_start = 0; + } + + return PARSE_OK; +} + +#endif diff --git a/dependencies/nDPIsrvd.py b/dependencies/nDPIsrvd.py new file mode 100644 index 000000000..6bcaf516c --- /dev/null +++ b/dependencies/nDPIsrvd.py @@ -0,0 +1,269 @@ +#!/usr/bin/env python3 + +import argparse +import array +import json +import re +import os +import scapy.all +import stat +import socket + +DEFAULT_HOST = '127.0.0.1' +DEFAULT_PORT = 7000 +DEFAULT_UNIX = '/tmp/ndpid-distributor.sock' + +NETWORK_BUFFER_MIN_SIZE = 5 +NETWORK_BUFFER_MAX_SIZE = 9216 # Please keep this value in sync with the one in config.h + +PKT_TYPE_ETH_IP4 = 0x0800 +PKT_TYPE_ETH_IP6 = 0x86DD + +EVENT_UNKNOWN = 'Unknown' +# Event tuple: (pretty-name, real-name) +DAEMON_EVENTS = [ ('Invalid','invalid'), ('Init','init'), \ + ('Reconnect','reconnect'), ('Shutdown','shutdown') ] +BASIC_EVENTS = ['Invalid', 'Unknown-Datalink-Layer', 'Unknown-Layer3-Protocol', 'Non-IP-Packet', + 'Ethernet-Packet-Too-Short', 'Ethernet-Packet-Unknown', 'IP4-Packet-Too-Short', + 'IP4-Size-Smaller-Than-Header', 'IP4-Layer4-Payload-Detection-Failed', 'IP6-Packet-Too-Short', + 'IP6-Size-Smaller-Than-Header', 'IP6-Layer4-Payload-Detection-Failed', 'TCP-Packet-Too-Short', + 'UDP-Packet-Too-Short', 'Capture-Size-Smaller-Than-Packet-Size', 'Max-Flow-To-Track', + 'Flow-Memory-Allocation-Failed', 'NDPI-Flow-Memory-Allocation-Failed', + 'NDPI-ID-Memory-Allocation-Failed'] +PACKET_EVENTS = [ ('Invalid','invalid'), ('Packet','packet'), ('Packet-Flow','packet-flow') ] +FLOW_EVENTS = [ ('Invalid','invalid'), ('New','new'), ('End','end'), ('Idle','idle'), ('Guessed','guessed'), \ + ('Detected','detected'), ('Detection-Update','detection-update'), ('Not-Detected','not-detected') ] + +class TermColor: + WARNING = '\033[93m' + FAIL = '\033[91m' + BOLD = '\033[1m' + END = '\033[0m' + BLINK = "\x1b[5m" + +class nDPIsrvdSocket: + def __init__(self): + self.sock_family = None + + def connect(self, addr): + if type(addr) is tuple: + self.sock_family = socket.AF_INET + elif type(addr) is str: + self.sock_family = socket.AF_UNIX + else: + raise RuntimeError('Unsupported address type:: {}'.format(str(addr))) + + self.sock = socket.socket(self.sock_family, socket.SOCK_STREAM) + self.sock.connect(addr) + self.buffer = bytes() + self.msglen = 0 + self.digitlen = 0 + + def receive(self): + recvd = self.sock.recv(NETWORK_BUFFER_MAX_SIZE - len(self.buffer)) + + if len(recvd) == 0: + raise RuntimeError('socket connection broken') + self.buffer += recvd + + retval = [] + while self.msglen + self.digitlen < len(self.buffer): + + if self.msglen == 0: + starts_with_digits = re.match(r'(^\d+){', self.buffer[:NETWORK_BUFFER_MIN_SIZE].decode(errors='strict')) + if starts_with_digits is None: + if len(self.buffer) < NETWORK_BUFFER_MIN_SIZE: + break + raise RuntimeError('Invalid packet received: {}'.format(self.buffer)) + self.msglen = int(starts_with_digits[1]) + self.digitlen = len(starts_with_digits[1]) + + if len(self.buffer) >= self.msglen + self.digitlen: + recvd = self.buffer[self.digitlen:self.msglen + self.digitlen] + self.buffer = self.buffer[self.msglen + self.digitlen:] + retval += [(recvd,self.msglen,self.digitlen)] + + self.msglen = 0 + self.digitlen = 0 + + return retval + +class PcapPacket: + def __init__(self, flow_id=-1): + self.pktdump = None + self.was_dumped = False + self.was_detected = False + self.flow_id = flow_id + self.packets = [] + + def addPacket(self, pkt, pkt_type, pkt_ipoffset): + self.packets += [ ( pkt, pkt_type, pkt_ipoffset ) ] + + @staticmethod + def getIp(packet): + if packet[1] == PKT_TYPE_ETH_IP4: + return scapy.all.IP(packet[0][packet[2]:]) + elif packet[1] == PKT_TYPE_ETH_IP6: + return scapy.all.IPv6(packet[0][packet[2]:]) + else: + raise RuntimeError('packet type unknown: {}'.format(packet[1])) + + @staticmethod + def getTCPorUDP(packet): + p = PcapPacket.getIp(packet) + if p.haslayer(scapy.all.TCP): + return p.getlayer(scapy.all.TCP) + elif p.haslayer(scapy.all.UDP): + return p.getlayer(scapy.all.UDP) + else: + return None + + def detected(self): + self.was_detected = True + + def fin(self, filename_suffix): + if self.was_dumped is True: + return 'Flow already dumped.' + if self.was_detected is True: + return 'Flow detected.' + + emptyTCPorUDPcount = 0; + for packet in self.packets: + p = PcapPacket.getTCPorUDP(packet) + if p is not None: + if p.haslayer(scapy.all.Padding) and len(p.payload) - len(p[scapy.all.Padding]) == 0: + emptyTCPorUDPcount += 1 + if len(p.payload) == 0: + emptyTCPorUDPcount += 1 + + if emptyTCPorUDPcount == len(self.packets): + return 'Flow does not contain any packets with non-empty layer4 payload.' + + if self.pktdump is None: + if self.flow_id == -1: + self.pktdump = scapy.all.PcapWriter('packet-{}.pcap'.format(filename_suffix), + append=True, sync=True) + else: + self.pktdump = scapy.all.PcapWriter('flow-{}-{}.pcap'.format(filename_suffix, self.flow_id), + append=False, sync=True) + + for packet in self.packets: + self.pktdump.write(PcapPacket.getIp(packet)) + + self.pktdump.close() + self.was_dumped = True + + return 'Success.' + +def JsonParseBytes(json_bytes): + return json.loads(json_bytes.decode('ascii', errors='replace'), strict=False) + +class nDPIdEvent: + isValid = False + DaemonEventID = -1 + DaemonEventName = None + DaemonEventPrettyName = EVENT_UNKNOWN + BasicEventID = -1 + BasicEventName = None + BasicEventPrettyName = EVENT_UNKNOWN + PacketEventID = -1 + PacketEventName = None + PacketEventPrettyName = EVENT_UNKNOWN + FlowEventID = -1 + FlowEventName = None + FlowEventPrettyName = EVENT_UNKNOWN + + def validateEvent(self, event_id, event_name, list_of_event_tuples): + if self.isValid is True: + raise RuntimeError('nDPId event already validated. Multiple Events in one JSON strings are not allowed.\n' \ + '[EVENTS]\n' + 'current: {}\n' \ + 'daemon.: {}\n' \ + 'basic..: {}\n' \ + 'packet.: {}\n' \ + 'flow...: {}\n'.format(event_name, + self.DaemonEventName, self.BasicEventName, \ + self.PacketEventName, self.FlowEventName)) + + if type(event_id) is not int: + raise RuntimeError('Argument is not an Integer/EventID!') + + if event_id < 0 or event_id >= len(list_of_event_tuples): + raise RuntimeError('Unknown event id: {} aka {}.'.format(event_id, event_name)) + + if type(list_of_event_tuples[0]) == tuple and list_of_event_tuples[event_id][1] != event_name: + raise RuntimeError('Unknown event name: {}.'.format(event_name)) + + self.isValid = True + return list_of_event_tuples[event_id][0] if type(list_of_event_tuples[0]) == tuple \ + else list_of_event_tuples[event_id] + + def validateFlowEvent(self): + return self.validateEvent(self.FlowEventID, self.FlowEventName, FLOW_EVENTS) + + def validatePacketEvent(self): + return self.validateEvent(self.PacketEventID, self.PacketEventName, PACKET_EVENTS) + + def validateBasicEvent(self): + return self.validateEvent(self.BasicEventID, self.BasicEventName, BASIC_EVENTS) + + def validateDaemonEvent(self): + return self.validateEvent(self.DaemonEventID, self.DaemonEventName, DAEMON_EVENTS) + + @staticmethod + def validateJsonEventTypes(json_dict): + if type(json_dict) is not dict: + raise RuntimeError('Argument is not a dictionary!') + + nev = nDPIdEvent() + + if 'daemon_event_id' in json_dict: + nev.DaemonEventID = json_dict['daemon_event_id'] + nev.DaemonEventName = json_dict['daemon_event_name'] + nev.DaemonEventPrettyName = nev.validateDaemonEvent() + if 'basic_event_id' in json_dict: + nev.BasicEventID = json_dict['basic_event_id'] + nev.BasicEventName = json_dict['basic_event_name'] + nev.BasicEventPrettyName = nev.validateBasicEvent() + if 'packet_event_id' in json_dict: + nev.PacketEventID = json_dict['packet_event_id'] + nev.PacketEventName = json_dict['packet_event_name'] + nev.PacketEventPrettyName = nev.validatePacketEvent() + if 'flow_event_id' in json_dict: + nev.FlowEventID = json_dict['flow_event_id'] + nev.FlowEventName = json_dict['flow_event_name'] + nev.FlowEventPrettyName = nev.validateFlowEvent() + + return nev + +def defaultArgumentParser(): + parser = argparse.ArgumentParser(description='nDPIsrvd options', formatter_class=argparse.ArgumentDefaultsHelpFormatter) + parser.add_argument('--host', type=str, help='nDPIsrvd host IP') + parser.add_argument('--port', type=int, default=DEFAULT_PORT, help='nDPIsrvd TCP port') + parser.add_argument('--unix', type=str, help='nDPIsrvd unix socket path') + return parser + +def validateAddress(args): + address = None + + if args.host is None: + address_tcpip = (DEFAULT_HOST, DEFAULT_PORT) + else: + address_tcpip = (args.host, args.port) + + if args.unix is None: + address_unix = DEFAULT_UNIX + else: + address_unix = args.unix + + possible_sock_mode = 0 + try: + possible_sock_mode = os.stat(address_unix).st_mode + except: + pass + if stat.S_ISSOCK(possible_sock_mode): + address = address_unix + else: + address = address_tcpip + + return address diff --git a/dependencies/update_jsmn.sh b/dependencies/update_jsmn.sh new file mode 100755 index 000000000..6b187e177 --- /dev/null +++ b/dependencies/update_jsmn.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env sh + +MYDIR="$(dirname ${0})" +cd "${MYDIR}/.." + +git subtree pull --squash --prefix=dependencies/jsmn https://github.com/zserge/jsmn.git master diff --git a/examples/c-json-stdout/c-json-stdout.c b/examples/c-json-stdout/c-json-stdout.c index eb56470e7..b70d7bac5 100644 --- a/examples/c-json-stdout/c-json-stdout.c +++ b/examples/c-json-stdout/c-json-stdout.c @@ -8,7 +8,7 @@ #include #include "config.h" -#include "jsmn.h" +#include "jsmn/jsmn.h" static char serv_listen_addr[INET_ADDRSTRLEN] = DISTRIBUTOR_HOST; static uint16_t serv_listen_port = DISTRIBUTOR_PORT; -- cgit v1.2.3