diff --git a/depend/json-c-0.16.tar.gz b/depend/json-c-0.16.tar.gz new file mode 100644 index 0000000..53ee6be Binary files /dev/null and b/depend/json-c-0.16.tar.gz differ diff --git a/depend/third_libs.cmake b/depend/third_libs.cmake index 43f01ab..85f7eec 100644 --- a/depend/third_libs.cmake +++ b/depend/third_libs.cmake @@ -4,6 +4,14 @@ INCLUDE(FetchContent) PKG_SEARCH_MODULE(LIBUV QUIET libuv) PKG_SEARCH_MODULE(LIBCONFIG QUIET libconfig) FIND_LIBRARY(LIBZLOG zlog PATHS "/usr/local/lib") +PKG_CHECK_MODULES(LIBJSON-C QUIET json-c>=0.13) + +IF (NOT LIBJSON-C_FOUND) + FETCHCONTENT_DECLARE(libjson-c + URL file://${CMAKE_SOURCE_DIR}/depend/json-c-0.16.tar.gz + URL_MD5 a549a833f886d427148fb5710c3e613e + ) +ENDIF () IF (NOT LIBUV_FOUND) FETCHCONTENT_DECLARE(libuv @@ -35,6 +43,10 @@ IF (LIBZLOG STREQUAL "LIBZLOG-NOTFOUND") FETCHCONTENT_MAKEAVAILABLE(zlog) ENDIF () +IF (NOT LIBJSON-C_FOUND) + FETCHCONTENT_MAKEAVAILABLE(libjson-c) +ENDIF () + IF (NOT LIBUV_FOUND) MESSAGE(STATUS "libuv not found, will be used source code to build it ...") FETCHCONTENT_GETPROPERTIES(libuv) @@ -75,4 +87,22 @@ IF (LIBZLOG STREQUAL "LIBZLOG-NOTFOUND") ELSE () MESSAGE(STATUS "zlog found of ${LIBZLOG}") LIST(APPEND COMMON_LIBS "${LIBZLOG}") +ENDIF () + +IF (NOT LIBJSON-C_FOUND) + MESSAGE(STATUS "json-c not found, will be used source code to build it ...") + FETCHCONTENT_GETPROPERTIES(libjson-c) + IF (NOT libjson-c_POPULATED) + FETCHCONTENT_POPULATE(libjson-c) + ADD_SUBDIRECTORY(${libjson-c_SOURCE_DIR} ${libjson-c_BINARY_DIR}) + ENDIF () + EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} -E make_directory "json-c" + COMMAND ${CMAKE_COMMAND} -E copy_if_different "${libjson-c_BINARY_DIR}/json.h" "./json-c/" + COMMAND ${CMAKE_COMMAND} -E copy_if_different "${libjson-c_BINARY_DIR}/json_config.h" "." + WORKING_DIRECTORY ${libjson-c_SOURCE_DIR}) + INCLUDE_DIRECTORIES(${libjson-c_SOURCE_DIR}) + LIST(APPEND COMMON_LIBS "json-c") +ELSE () + MESSAGE(STATUS "json-c found ${LIBJSON-C_VERSION} at ${LIBJSON-C_LIBRARY_DIRS}") + LIST(APPEND COMMON_LIBS "${LIBJSON-C_LDFLAGS}") ENDIF () \ No newline at end of file diff --git a/srcs/libs/CMakeLists.txt b/srcs/libs/CMakeLists.txt index b9924bf..2a69bed 100644 --- a/srcs/libs/CMakeLists.txt +++ b/srcs/libs/CMakeLists.txt @@ -2,6 +2,8 @@ SET(LIB_PROJECT_TARGET common) PROJECT(${LIB_PROJECT_TARGET} VERSION 1.1.0) +ADD_SUBDIRECTORY(./json/json_schema) + STRING(REPLACE ";" ", " BUILD_CONFIG_INFO "${COMMON_DEFINE}") CONFIGURE_FILE(lib_config.h.in lib_config.h) @@ -62,6 +64,8 @@ ADD_LIBRARY(${LIB_PROJECT_TARGET} ${C_SRC} ${C_HEADS}) TARGET_LINK_LIBRARIES(${LIB_PROJECT_TARGET} ${COMMON_LIBS}) +TARGET_LINK_LIBRARIES(${LIB_PROJECT_TARGET} jsoncdac) + IF (USED_OPENDHCPD) TARGET_LINK_LIBRARIES(${LIB_PROJECT_TARGET} opendhcpd) ENDIF () diff --git a/srcs/libs/docs/json_schema/README.md b/srcs/libs/docs/json_schema/README.md new file mode 100644 index 0000000..9ac96c3 --- /dev/null +++ b/srcs/libs/docs/json_schema/README.md @@ -0,0 +1,130 @@ +# json-c d'accord library (libjsoncdac) + +jsonc-daccord is a lightweight JSON Schema validation library written in C, and is taking advantage of the libjson-c library. + +## Design Goals + +The goal is to have a lightweight JSON Schema validation implementation in C using json-c. json-c is popular in OpenWRT communities. Initially I just wanted it to support a small subset of JSON Schema to suit a need to validate simple json files. See the minimum build supports below. However to suit a broader audience, supporting more JSON Schema is important. + +Currently the footprint of libjsoncdac.so is 8KB. The keep the footprint from bloating out, new features should be selectable using CMake options. + +Minimal build supports: +- all: type, enum, required, properties, const. +- objects: +- strings: minLength, maxLength. +- integers and doubles: minimum, maximum. +- arrays: minItems, maxItems, uniqeItems, items. + +## Example Use + +Public headers: + +See [jsoncdaccord.h](include/jsoncdaccord.h) + +```C + int jdac_validate_file(const char *jsonfile, const char *jsonschemafile); + int jdac_validate(json_object *jobj, json_object *jschema); + int jdac_ref_set_localpath(const char *_localpath); + + const char* jdac_errorstr(unsigned int jdac_errors); +``` + +Link your binary to: `-ljsoncdac -ljson-c` + +Use the #include header: `#include ` + +Example C code: + +```C +#include +#include +#include + +int main(int argc, char *argv[]) +{ + char *json_file = "test.json"; + char *schema_file = "schema.json"; + + // optional: load referenced schema files from filesystem + char *localpath = "/my/path/to_json_files/"; + jdac_ref_set_localpath(localpath); + + printf("validating %s with %s\n", json_file, schema_file); + int err = jdac_validate_file(json_file, schema_file); + if (err==JDAC_ERR_VALID) { + printf("validation ok\n"); + } else { + printf("validate failed %d: %s\n", err, jdac_errorstr(err)); + } + return err; +} +``` + +See [jdac-cli.c](libjsoncdac/jdac-cli.c) as well. + +## Install + +Building from source: + +Install json-c and libcmocka-dev (used in the debug builds). + +- Release version: + +``` +git clone --branch libjsoncdac-0.2 https://github.com/domoslabs/jsonc-daccord &&\ +cd jsonc-daccord && mkdir build && cd build &&\ +cmake .. -DCMAKE_BUILD_TYPE=Release && make && sudo make install +``` + +- Debug version: +``` +git clone --branch libjsoncdac-0.2 https://github.com/domoslabs/jsonc-daccord &&\ +cd jsonc-daccord && mkdir build && cd build &&\ +cmake .. -DCMAKE_BUILD_TYPE=Debug && make && sudo make install +``` + +Note: After install you might need to run `sudo ldconfig`. + +## CMake Options + +build options: + +| option | description | +| :------------------------- | :------------------------------------------------------- | +| CMAKE_BUILD_TYPE | Build as Release or Debug. Default: Release. | +| RUN_TEST_SUITE | Run JSON Schema test suite (CMAKE_BUILD_TYPE=Debug Only) | +| BUILD_PATTERN | Support *pattern*. | +| BUILD_PATTERNPROPERTIES | Support *patternProperties* | +| BUILD_ADDITIONALPROPERTIES | Support *additionalProperties* | +| BUILD_PROPERTYNAMES | Support *propertyNames* | +| BUILD_SUBSCHEMALOGIC | Support *allOf*, *anyOf*, *oneOf*, *not*, *if-then-else* | +| BUILD_CONTAINS | Support *contains*, *minContains*, and *maxContains* | +| BUILD_DOWNLOAD | Support downloading referenced schema files | +| BUILD_STORE | Support build a list of schema uri, id, and anchors | +| BUILD_REF | Support *$ref* keyword. load schemas by file. | + + Note: All BUILD_* options are selected by default + +## Run tests +For debug builds: +``` +ctest +ctest -V # to see output of tests +``` + +Running test suites are currently optional, and are select with `RUN_TEST_SUITE=ON` in the cmake options. + +## Command Line Interface +You can try the library with the jdac-cli command. + +```/tmp/domos/domosqos-sta_statistics_json +jdac-cli -h +``` +## To do +- prevent infinite recursion + +## Related links + +- https://json-schema.org/specification.html +- https://github.com/json-schema-org/JSON-Schema-Test-Suite +- https://github.com/json-c/json-c diff --git a/srcs/libs/docs/json_schema/json/flows.json b/srcs/libs/docs/json_schema/json/flows.json new file mode 100644 index 0000000..43d7ca9 --- /dev/null +++ b/srcs/libs/docs/json_schema/json/flows.json @@ -0,0 +1,1132 @@ +{ + "flows": [ + { + "flow_id": "91", + "service_name": "Cloudflare", + "master_protocol": "HTTP", + "sport": 58692, + "dport": 80, + "src": "192.168.1.190", + "dst": "104.20.145.10", + "proto": "6", + "alive": "1", + "active": false + }, + { + "flow_id": "120", + "service_name": "Cloudflare", + "master_protocol": "HTTP", + "sport": 58694, + "dport": 80, + "src": "192.168.1.190", + "dst": "104.20.144.10", + "proto": "6", + "alive": "1", + "active": false + }, + { + "flow_id": "93", + "service_name": "SkypeCall", + "master_protocol": "STUN", + "sport": 13389, + "dport": 1434, + "src": "192.168.1.223", + "dst": "10.20.32.177", + "proto": "17", + "alive": "1", + "active": false + }, + { + "flow_id": "59", + "service_name": "Unknown", + "master_protocol": "Unknown", + "sport": 80, + "dport": 60029, + "src": "104.199.241.133", + "dst": "192.168.1.223", + "proto": "6", + "alive": "1", + "active": false + }, + { + "flow_id": "36", + "service_name": "Dropbox", + "master_protocol": "Unknown", + "sport": 17500, + "dport": 17500, + "src": "192.168.1.190", + "dst": "192.168.1.255", + "proto": "17", + "alive": "1", + "active": false + }, + { + "flow_id": "119", + "service_name": "DNS", + "master_protocol": "Unknown", + "sport": 58707, + "dport": 53, + "src": "35.97.167.44", + "dst": "0.0.0.1", + "proto": "17", + "alive": "1", + "active": false + }, + { + "flow_id": "137", + "service_name": "Slack", + "master_protocol": "DNS", + "sport": 61379, + "dport": 53, + "src": "35.97.167.44", + "dst": "0.0.0.1", + "proto": "17", + "alive": "1", + "active": false + }, + { + "flow_id": "139", + "service_name": "Slack", + "master_protocol": "TLS", + "sport": 58695, + "dport": 443, + "src": "192.168.1.190", + "dst": "143.204.52.239", + "proto": "6", + "alive": "1", + "active": false + }, + { + "flow_id": "135", + "service_name": "UbuntuONE", + "master_protocol": "DNS", + "sport": 52351, + "dport": 53, + "src": "192.168.1.169", + "dst": "192.168.1.1", + "proto": "17", + "alive": "1", + "active": false + }, + { + "flow_id": "103", + "service_name": "Skype", + "master_protocol": "DNS", + "sport": 53519, + "dport": 53, + "src": "192.168.1.223", + "dst": "192.168.1.1", + "proto": "17", + "alive": "1", + "active": false + }, + { + "flow_id": "39", + "service_name": "Unknown", + "master_protocol": "Unknown", + "sport": 54990, + "dport": 80, + "src": "192.168.1.169", + "dst": "216.58.211.3", + "proto": "6", + "alive": "1", + "active": false + }, + { + "flow_id": "40", + "service_name": "Unknown", + "master_protocol": "Unknown", + "sport": 54988, + "dport": 80, + "src": "192.168.1.169", + "dst": "216.58.211.3", + "proto": "6", + "alive": "1", + "active": false + }, + { + "flow_id": "46", + "service_name": "Unknown", + "master_protocol": "Unknown", + "sport": 54986, + "dport": 80, + "src": "192.168.1.169", + "dst": "216.58.211.3", + "proto": "6", + "alive": "1", + "active": false + }, + { + "flow_id": "63", + "service_name": "Unknown", + "master_protocol": "Unknown", + "sport": 51871, + "dport": 443, + "src": "192.168.1.190", + "dst": "216.58.211.14", + "proto": "17", + "alive": "1", + "active": false + }, + { + "flow_id": "132", + "service_name": "ICMPV6", + "master_protocol": "Unknown", + "sport": 0, + "dport": 0, + "src": "140.242.122.109", + "dst": "189.93.205.231", + "proto": "58", + "alive": "1", + "active": false + }, + { + "flow_id": "45", + "service_name": "SSH", + "master_protocol": "Unknown", + "sport": 65192, + "dport": 22, + "src": "192.168.1.223", + "dst": "192.168.1.1", + "proto": "6", + "alive": "1", + "active": false + }, + { + "flow_id": "101", + "service_name": "Skype", + "master_protocol": "DNS", + "sport": 53519, + "dport": 53, + "src": "189.93.205.231", + "dst": "0.0.0.1", + "proto": "17", + "alive": "1", + "active": false + }, + { + "flow_id": "114", + "service_name": "SSDP", + "master_protocol": "Unknown", + "sport": 57431, + "dport": 1900, + "src": "192.168.1.190", + "dst": "239.255.255.250", + "proto": "17", + "alive": "1", + "active": false + }, + { + "flow_id": "109", + "service_name": "Microsoft", + "master_protocol": "DNS", + "sport": 58229, + "dport": 53, + "src": "189.93.205.231", + "dst": "0.0.0.1", + "proto": "17", + "alive": "1", + "active": false + }, + { + "flow_id": "96", + "service_name": "Unknown", + "master_protocol": "Unknown", + "sport": 58693, + "dport": 80, + "src": "192.168.1.190", + "dst": "8.8.8.8", + "proto": "6", + "alive": "1", + "active": false + }, + { + "flow_id": "129", + "service_name": "GoogleServices", + "master_protocol": "DNS", + "sport": 49308, + "dport": 53, + "src": "226.158.40.91", + "dst": "0.0.0.1", + "proto": "17", + "alive": "1", + "active": false + }, + { + "flow_id": "9", + "service_name": "TLS", + "master_protocol": "Unknown", + "sport": 59997, + "dport": 443, + "src": "192.168.1.223", + "dst": "3.9.202.151", + "proto": "6", + "alive": "1", + "active": false + }, + { + "flow_id": "16", + "service_name": "TLS", + "master_protocol": "Unknown", + "sport": 60001, + "dport": 443, + "src": "192.168.1.223", + "dst": "3.9.202.151", + "proto": "6", + "alive": "1", + "active": false + }, + { + "flow_id": "100", + "service_name": "Skype", + "master_protocol": "DNS", + "sport": 59849, + "dport": 53, + "src": "189.93.205.231", + "dst": "0.0.0.1", + "proto": "17", + "alive": "1", + "active": false + }, + { + "flow_id": "94", + "service_name": "SkypeCall", + "master_protocol": "STUN", + "sport": 13389, + "dport": 61850, + "src": "192.168.1.223", + "dst": "195.159.234.190", + "proto": "17", + "alive": "1", + "active": false + }, + { + "flow_id": "131", + "service_name": "GoogleServices", + "master_protocol": "DNS", + "sport": 51594, + "dport": 53, + "src": "226.158.40.91", + "dst": "0.0.0.1", + "proto": "17", + "alive": "1", + "active": false + }, + { + "flow_id": "130", + "service_name": "GoogleServices", + "master_protocol": "DNS", + "sport": 52788, + "dport": 53, + "src": "226.158.40.91", + "dst": "0.0.0.1", + "proto": "17", + "alive": "1", + "active": false + }, + { + "flow_id": "140", + "service_name": "Slack", + "master_protocol": "TLS", + "sport": 58696, + "dport": 443, + "src": "192.168.1.190", + "dst": "35.176.156.206", + "proto": "6", + "alive": "1", + "active": false + }, + { + "flow_id": "115", + "service_name": "Google", + "master_protocol": "DNS", + "sport": 56772, + "dport": 53, + "src": "226.158.40.91", + "dst": "0.0.0.1", + "proto": "17", + "alive": "1", + "active": false + }, + { + "flow_id": "126", + "service_name": "Google", + "master_protocol": "DNS", + "sport": 56940, + "dport": 53, + "src": "226.158.40.91", + "dst": "0.0.0.1", + "proto": "17", + "alive": "1", + "active": false + }, + { + "flow_id": "4", + "service_name": "CiscoVPN", + "master_protocol": "Unknown", + "sport": 58164, + "dport": 8009, + "src": "192.168.1.190", + "dst": "10.20.32.54", + "proto": "6", + "alive": "1", + "active": false + }, + { + "flow_id": "122", + "service_name": "Google", + "master_protocol": "DNS", + "sport": 57160, + "dport": 53, + "src": "226.158.40.91", + "dst": "0.0.0.1", + "proto": "17", + "alive": "1", + "active": false + }, + { + "flow_id": "5", + "service_name": "Unknown", + "master_protocol": "Unknown", + "sport": 443, + "dport": 51173, + "src": "64.233.165.189", + "dst": "192.168.1.190", + "proto": "17", + "alive": "1", + "active": false + }, + { + "flow_id": "86", + "service_name": "Skype", + "master_protocol": "TLS", + "sport": 443, + "dport": 65171, + "src": "20.185.212.106", + "dst": "192.168.1.223", + "proto": "6", + "alive": "1", + "active": false + }, + { + "flow_id": "98", + "service_name": "Skype", + "master_protocol": "TLS", + "sport": 65201, + "dport": 443, + "src": "192.168.1.223", + "dst": "20.185.212.106", + "proto": "6", + "alive": "1", + "active": false + }, + { + "flow_id": "87", + "service_name": "TLS", + "master_protocol": "Unknown", + "sport": 59955, + "dport": 443, + "src": "192.168.1.223", + "dst": "51.105.249.239", + "proto": "6", + "alive": "1", + "active": false + }, + { + "flow_id": "116", + "service_name": "Google", + "master_protocol": "DNS", + "sport": 61762, + "dport": 53, + "src": "226.158.40.91", + "dst": "0.0.0.1", + "proto": "17", + "alive": "1", + "active": false + }, + { + "flow_id": "23", + "service_name": "Google", + "master_protocol": "TLS", + "sport": 65102, + "dport": 443, + "src": "192.168.1.223", + "dst": "35.186.224.53", + "proto": "6", + "alive": "1", + "active": false + }, + { + "flow_id": "117", + "service_name": "Google", + "master_protocol": "DNS", + "sport": 63080, + "dport": 53, + "src": "226.158.40.91", + "dst": "0.0.0.1", + "proto": "17", + "alive": "1", + "active": false + }, + { + "flow_id": "124", + "service_name": "Google", + "master_protocol": "DNS", + "sport": 64198, + "dport": 53, + "src": "226.158.40.91", + "dst": "0.0.0.1", + "proto": "17", + "alive": "1", + "active": false + }, + { + "flow_id": "24", + "service_name": "Unknown", + "master_protocol": "Unknown", + "sport": 55022, + "dport": 80, + "src": "192.168.1.169", + "dst": "91.135.34.42", + "proto": "6", + "alive": "1", + "active": false + }, + { + "flow_id": "83", + "service_name": "Skype", + "master_protocol": "TLS", + "sport": 65198, + "dport": 443, + "src": "192.168.1.223", + "dst": "52.114.158.20", + "proto": "6", + "alive": "1", + "active": false + }, + { + "flow_id": "110", + "service_name": "Microsoft", + "master_protocol": "TLS", + "sport": 65204, + "dport": 443, + "src": "192.168.1.223", + "dst": "52.114.158.91", + "proto": "6", + "alive": "1", + "active": false + }, + { + "flow_id": "136", + "service_name": "Slack", + "master_protocol": "DNS", + "sport": 50672, + "dport": 53, + "src": "35.97.167.44", + "dst": "0.0.0.1", + "proto": "17", + "alive": "1", + "active": false + }, + { + "flow_id": "7", + "service_name": "Google", + "master_protocol": "Unknown", + "sport": 58162, + "dport": 5228, + "src": "192.168.1.190", + "dst": "64.233.162.188", + "proto": "6", + "alive": "1", + "active": false + }, + { + "flow_id": "99", + "service_name": "Microsoft", + "master_protocol": "TLS", + "sport": 65202, + "dport": 443, + "src": "192.168.1.223", + "dst": "68.232.34.200", + "proto": "6", + "alive": "1", + "active": false + }, + { + "flow_id": "33", + "service_name": "Unknown", + "master_protocol": "Unknown", + "sport": 443, + "dport": 58169, + "src": "104.75.64.84", + "dst": "192.168.1.190", + "proto": "6", + "alive": "1", + "active": false + }, + { + "flow_id": "89", + "service_name": "Cloudflare", + "master_protocol": "HTTP", + "sport": 58691, + "dport": 80, + "src": "192.168.1.190", + "dst": "104.20.145.10", + "proto": "6", + "alive": "1", + "active": false + }, + { + "flow_id": "88", + "service_name": "DNS", + "master_protocol": "Unknown", + "sport": 57430, + "dport": 53, + "src": "35.97.167.44", + "dst": "0.0.0.1", + "proto": "17", + "alive": "1", + "active": false + }, + { + "flow_id": "138", + "service_name": "Slack", + "master_protocol": "DNS", + "sport": 58654, + "dport": 53, + "src": "35.97.167.44", + "dst": "0.0.0.1", + "proto": "17", + "alive": "1", + "active": false + }, + { + "flow_id": "42", + "service_name": "ICMPV6", + "master_protocol": "Unknown", + "sport": 0, + "dport": 0, + "src": "226.158.40.91", + "dst": "0.0.0.1", + "proto": "58", + "alive": "1", + "active": false + }, + { + "flow_id": "97", + "service_name": "Skype", + "master_protocol": "TLS", + "sport": 65200, + "dport": 443, + "src": "192.168.1.223", + "dst": "104.40.250.127", + "proto": "6", + "alive": "1", + "active": false + }, + { + "flow_id": "50", + "service_name": "TLS", + "master_protocol": "Unknown", + "sport": 58674, + "dport": 443, + "src": "192.168.1.190", + "dst": "143.204.48.158", + "proto": "6", + "alive": "1", + "active": false + }, + { + "flow_id": "72", + "service_name": "SkypeCall", + "master_protocol": "STUN", + "sport": 13389, + "dport": 3478, + "src": "192.168.1.223", + "dst": "52.149.126.77", + "proto": "17", + "alive": "1", + "active": false + }, + { + "flow_id": "95", + "service_name": "SkypeCall", + "master_protocol": "STUN", + "sport": 13389, + "dport": 3480, + "src": "192.168.1.223", + "dst": "52.149.126.41", + "proto": "17", + "alive": "1", + "active": false + }, + { + "flow_id": "84", + "service_name": "Spotify", + "master_protocol": "Unknown", + "sport": 57621, + "dport": 57621, + "src": "192.168.1.223", + "dst": "192.168.1.255", + "proto": "17", + "alive": "1", + "active": false + }, + { + "flow_id": "58", + "service_name": "TLS", + "master_protocol": "Unknown", + "sport": 65182, + "dport": 443, + "src": "192.168.1.223", + "dst": "143.204.48.158", + "proto": "6", + "alive": "1", + "active": false + }, + { + "flow_id": "71", + "service_name": "SkypeCall", + "master_protocol": "STUN", + "sport": 17391, + "dport": 3478, + "src": "192.168.1.223", + "dst": "52.137.56.8", + "proto": "17", + "alive": "1", + "active": false + }, + { + "flow_id": "73", + "service_name": "SkypeCall", + "master_protocol": "STUN", + "sport": 17391, + "dport": 3478, + "src": "192.168.1.223", + "dst": "52.149.125.240", + "proto": "17", + "alive": "1", + "active": false + }, + { + "flow_id": "35", + "service_name": "Dropbox", + "master_protocol": "Unknown", + "sport": 17500, + "dport": 17500, + "src": "192.168.1.190", + "dst": "255.255.255.255", + "proto": "17", + "alive": "1", + "active": false + }, + { + "flow_id": "26", + "service_name": "ICMPV6", + "master_protocol": "Unknown", + "sport": 0, + "dport": 0, + "src": "35.97.167.44", + "dst": "0.0.0.1", + "proto": "58", + "alive": "1", + "active": false + }, + { + "flow_id": "47", + "service_name": "Unknown", + "master_protocol": "Unknown", + "sport": 443, + "dport": 58625, + "src": "172.217.21.174", + "dst": "192.168.1.190", + "proto": "17", + "alive": "1", + "active": false + }, + { + "flow_id": "41", + "service_name": "Unknown", + "master_protocol": "Unknown", + "sport": 54989, + "dport": 80, + "src": "192.168.1.169", + "dst": "216.58.211.3", + "proto": "6", + "alive": "1", + "active": false + }, + { + "flow_id": "48", + "service_name": "Unknown", + "master_protocol": "Unknown", + "sport": 54987, + "dport": 80, + "src": "192.168.1.169", + "dst": "216.58.211.3", + "proto": "6", + "alive": "1", + "active": false + }, + { + "flow_id": "104", + "service_name": "Skype", + "master_protocol": "DNS", + "sport": 58856, + "dport": 53, + "src": "192.168.1.223", + "dst": "192.168.1.1", + "proto": "17", + "alive": "1", + "active": false + }, + { + "flow_id": "113", + "service_name": "Skype", + "master_protocol": "DNS", + "sport": 50150, + "dport": 53, + "src": "189.93.205.231", + "dst": "0.0.0.1", + "proto": "17", + "alive": "1", + "active": false + }, + { + "flow_id": "108", + "service_name": "Microsoft", + "master_protocol": "DNS", + "sport": 54812, + "dport": 53, + "src": "189.93.205.231", + "dst": "0.0.0.1", + "proto": "17", + "alive": "1", + "active": false + }, + { + "flow_id": "133", + "service_name": "ICMPV6", + "master_protocol": "Unknown", + "sport": 0, + "dport": 0, + "src": "140.242.122.109", + "dst": "226.158.40.91", + "proto": "58", + "alive": "1", + "active": false + }, + { + "flow_id": "22", + "service_name": "TLS", + "master_protocol": "Unknown", + "sport": 443, + "dport": 58628, + "src": "3.9.202.151", + "dst": "192.168.1.190", + "proto": "6", + "alive": "1", + "active": false + }, + { + "flow_id": "102", + "service_name": "Skype", + "master_protocol": "DNS", + "sport": 58856, + "dport": 53, + "src": "189.93.205.231", + "dst": "0.0.0.1", + "proto": "17", + "alive": "1", + "active": false + }, + { + "flow_id": "14", + "service_name": "TLS", + "master_protocol": "Unknown", + "sport": 59998, + "dport": 443, + "src": "192.168.1.223", + "dst": "3.9.202.151", + "proto": "6", + "alive": "1", + "active": false + }, + { + "flow_id": "15", + "service_name": "TLS", + "master_protocol": "Unknown", + "sport": 60000, + "dport": 443, + "src": "192.168.1.223", + "dst": "3.9.202.151", + "proto": "6", + "alive": "1", + "active": false + }, + { + "flow_id": "118", + "service_name": "Google", + "master_protocol": "DNS", + "sport": 49929, + "dport": 53, + "src": "226.158.40.91", + "dst": "0.0.0.1", + "proto": "17", + "alive": "1", + "active": false + }, + { + "flow_id": "128", + "service_name": "Google", + "master_protocol": "DNS", + "sport": 52223, + "dport": 53, + "src": "226.158.40.91", + "dst": "0.0.0.1", + "proto": "17", + "alive": "1", + "active": false + }, + { + "flow_id": "90", + "service_name": "Unknown", + "master_protocol": "Unknown", + "sport": 443, + "dport": 58683, + "src": "34.204.212.142", + "dst": "192.168.1.190", + "proto": "6", + "alive": "1", + "active": false + }, + { + "flow_id": "127", + "service_name": "Google", + "master_protocol": "DNS", + "sport": 56945, + "dport": 53, + "src": "226.158.40.91", + "dst": "0.0.0.1", + "proto": "17", + "alive": "1", + "active": false + }, + { + "flow_id": "6", + "service_name": "CiscoVPN", + "master_protocol": "Unknown", + "sport": 58165, + "dport": 8009, + "src": "192.168.1.190", + "dst": "10.20.32.42", + "proto": "6", + "alive": "1", + "active": false + }, + { + "flow_id": "111", + "service_name": "Skype", + "master_protocol": "TLS", + "sport": 65174, + "dport": 443, + "src": "192.168.1.223", + "dst": "13.69.158.96", + "proto": "6", + "alive": "1", + "active": false + }, + { + "flow_id": "107", + "service_name": "DNS", + "master_protocol": "Unknown", + "sport": 57379, + "dport": 53, + "src": "226.158.40.91", + "dst": "0.0.0.1", + "proto": "17", + "alive": "1", + "active": false + }, + { + "flow_id": "85", + "service_name": "Unknown", + "master_protocol": "Unknown", + "sport": 443, + "dport": 60033, + "src": "35.186.224.47", + "dst": "192.168.1.223", + "proto": "6", + "alive": "1", + "active": false + }, + { + "flow_id": "64", + "service_name": "Skype", + "master_protocol": "TLS", + "sport": 65196, + "dport": 443, + "src": "192.168.1.223", + "dst": "20.185.212.106", + "proto": "6", + "alive": "1", + "active": false + }, + { + "flow_id": "134", + "service_name": "MDNS", + "master_protocol": "Unknown", + "sport": 5353, + "dport": 5353, + "src": "192.168.1.223", + "dst": "224.0.0.251", + "proto": "17", + "alive": "1", + "active": false + }, + { + "flow_id": "106", + "service_name": "DNS", + "master_protocol": "Unknown", + "sport": 60565, + "dport": 53, + "src": "226.158.40.91", + "dst": "0.0.0.1", + "proto": "17", + "alive": "1", + "active": false + }, + { + "flow_id": "121", + "service_name": "Google", + "master_protocol": "DNS", + "sport": 61629, + "dport": 53, + "src": "226.158.40.91", + "dst": "0.0.0.1", + "proto": "17", + "alive": "1", + "active": false + }, + { + "flow_id": "112", + "service_name": "TLS", + "master_protocol": "Unknown", + "sport": 443, + "dport": 59948, + "src": "51.105.249.239", + "dst": "192.168.1.223", + "proto": "6", + "alive": "1", + "active": false + }, + { + "flow_id": "49", + "service_name": "ICMPV6", + "master_protocol": "Unknown", + "sport": 0, + "dport": 0, + "src": "140.242.122.109", + "dst": "35.97.167.44", + "proto": "58", + "alive": "1", + "active": false + }, + { + "flow_id": "125", + "service_name": "Google", + "master_protocol": "DNS", + "sport": 64839, + "dport": 53, + "src": "226.158.40.91", + "dst": "0.0.0.1", + "proto": "17", + "alive": "1", + "active": false + }, + { + "flow_id": "123", + "service_name": "Google", + "master_protocol": "DNS", + "sport": 65091, + "dport": 53, + "src": "226.158.40.91", + "dst": "0.0.0.1", + "proto": "17", + "alive": "1", + "active": false + }, + { + "flow_id": "17", + "service_name": "ICMPV6", + "master_protocol": "Unknown", + "sport": 0, + "dport": 0, + "src": "189.93.205.231", + "dst": "0.0.0.1", + "proto": "58", + "alive": "1", + "active": false + }, + { + "flow_id": "25", + "service_name": "Unknown", + "master_protocol": "Unknown", + "sport": 55021, + "dport": 80, + "src": "192.168.1.169", + "dst": "91.135.34.42", + "proto": "6", + "alive": "1", + "active": false + }, + { + "flow_id": "68", + "service_name": "Unknown", + "master_protocol": "Unknown", + "sport": 59198, + "dport": 3478, + "src": "192.168.1.223", + "dst": "52.114.158.14", + "proto": "17", + "alive": "1", + "active": false + }, + { + "flow_id": "78", + "service_name": "Skype", + "master_protocol": "TLS", + "sport": 65197, + "dport": 443, + "src": "192.168.1.223", + "dst": "52.114.158.14", + "proto": "6", + "alive": "1", + "active": false + }, + { + "flow_id": "92", + "service_name": "Microsoft", + "master_protocol": "TLS", + "sport": 65199, + "dport": 443, + "src": "192.168.1.223", + "dst": "52.114.88.29", + "proto": "6", + "alive": "1", + "active": false + }, + { + "flow_id": "105", + "service_name": "Skype", + "master_protocol": "TLS", + "sport": 65203, + "dport": 443, + "src": "192.168.1.223", + "dst": "52.114.158.16", + "proto": "6", + "alive": "1", + "active": false + } + ] +} \ No newline at end of file diff --git a/srcs/libs/docs/json_schema/json/side-test-pattern.json b/srcs/libs/docs/json_schema/json/side-test-pattern.json new file mode 100644 index 0000000..0424234 --- /dev/null +++ b/srcs/libs/docs/json_schema/json/side-test-pattern.json @@ -0,0 +1,6 @@ +{ + "botenanna": "botenannna", + "boten": "boten", + "wuhu": "wublah", + "nullobject": null +} diff --git a/srcs/libs/docs/json_schema/json/side-test-patternProperties.json b/srcs/libs/docs/json_schema/json/side-test-patternProperties.json new file mode 100644 index 0000000..b7fbc0e --- /dev/null +++ b/srcs/libs/docs/json_schema/json/side-test-patternProperties.json @@ -0,0 +1,7 @@ +{ + "empty": {}, + "botenanna": { + "annaaaa": "boten" + }, + "nonobject": null +} diff --git a/srcs/libs/docs/json_schema/json/test-anyof.json b/srcs/libs/docs/json_schema/json/test-anyof.json new file mode 100644 index 0000000..8537a40 --- /dev/null +++ b/srcs/libs/docs/json_schema/json/test-anyof.json @@ -0,0 +1,35 @@ +{ + "test-int": { + "id": 123 + }, + "test-double": { + "id": 1.23 + }, + "test-string": { + "id": "1234" + }, + "test-boolean": { + "id": true + }, + "test-array": [ + 123, + "1234" + ], + "test-array2": [ + 123, + {} + ], + "test-array": { + "anna": [ + { + "id": 1 + }, + { + "id": 2 + }, + { + "id": 3 + } + ] + } +} \ No newline at end of file diff --git a/srcs/libs/docs/json_schema/json/test-enum.json b/srcs/libs/docs/json_schema/json/test-enum.json new file mode 100644 index 0000000..4376b17 --- /dev/null +++ b/srcs/libs/docs/json_schema/json/test-enum.json @@ -0,0 +1,10 @@ +{ + "teststring1": "one", + "teststring2": "two", + "teststring3": "three", + "teststring4": "four", + "testinteger1": 1, + "testinteger2": 2, + "testinteger3": 3, + "testinteger4": 4 +} diff --git a/srcs/libs/docs/json_schema/json/test-items.json b/srcs/libs/docs/json_schema/json/test-items.json new file mode 100644 index 0000000..eb292e0 --- /dev/null +++ b/srcs/libs/docs/json_schema/json/test-items.json @@ -0,0 +1,53 @@ +{ + "test1": { + "flowarray": [ + { + "key1": 5, + "key2": "hanz" + } + ] + }, + "wrongtype": { + "flowarray": [ + { + "key1": "5", + "key2": "hanz" + } + ] + }, + "missingkey": { + "flowarray": [ + { + "key2": "hanz" + } + ] + }, + "nestedarray": { + "flowarray": [ + { + "key1": "hanz", + "key2": [ + 1, + 2, + 3, + 4, + 5 + ] + } + ] + }, + "nestedarrayofstrings": { + "flowarray": [ + { + "key1": "hanz", + "key2": [ + "1", + "2", + "3", + "4", + "5" + ] + } + ] + } +} \ No newline at end of file diff --git a/srcs/libs/docs/json_schema/json/test-maxmin-items.json b/srcs/libs/docs/json_schema/json/test-maxmin-items.json new file mode 100644 index 0000000..82bfa48 --- /dev/null +++ b/srcs/libs/docs/json_schema/json/test-maxmin-items.json @@ -0,0 +1,7 @@ +{ + "array-1": [1], + "array-2": [1,2], + "array-3": [1,2,3], + "array-4": [1,2,3,4], + "array-5": [1,2,3,4,5] +} \ No newline at end of file diff --git a/srcs/libs/docs/json_schema/json/test-properties.json b/srcs/libs/docs/json_schema/json/test-properties.json new file mode 100644 index 0000000..3c8dfbb --- /dev/null +++ b/srcs/libs/docs/json_schema/json/test-properties.json @@ -0,0 +1,57 @@ +{ + "emptyobject": {}, + + "testjson": { + "computer": "banana", + "phone": 12345678, + "switch": true + }, + + "testjsontrue": { + "computer": "banana", + "phone": 12345678, + "switch": true, + "object": {"object":123}, + "list": [1,2,3] + }, + + "testjsonfalse": { + "computer": 12345678, + "phone": "banana", + "switch": [1,2,3], + "object": true, + "list": {"object":123} + }, + + "testjsonstring-all": { + "computer1": "hansihansi", + "computer2": "hans", + "computer3": "ha", + "computer4": "han" + }, + + "testjsonstring-all-fail": { + "computer1": "hansihansi", + "computer2": "hansi", + "computer3": "han", + "computer4": "hans" + }, + + "testjsonstring2": { + "computer4": "ha" + }, + + "testjsonstring3": { + "computer4": "han" + }, + + "testjsonstring4": { + "computer4": "hans" + }, + + "testjsonstring5": { + "computer4": "hansp" + } + + +} \ No newline at end of file diff --git a/srcs/libs/docs/json_schema/json/test-reqs-recursively.json b/srcs/libs/docs/json_schema/json/test-reqs-recursively.json new file mode 100644 index 0000000..07c1a68 --- /dev/null +++ b/srcs/libs/docs/json_schema/json/test-reqs-recursively.json @@ -0,0 +1,36 @@ +{ + "alltrue": { + "person": { + "name": "Anna", + "husbands": 1 + } + }, + + "nametooshort": { + "person": { + "name": "Ann", + "husbands": 1 + } + }, + + "wrongtype": { + "person": { + "name": "Anna", + "husbands": "1" + } + }, + + "husbandmissing": { + "person": { + "name": "Anna" + } + }, + + "namemissing": { + "person": { + } + }, + + "empty": { + } +} \ No newline at end of file diff --git a/srcs/libs/docs/json_schema/json/test-required.json b/srcs/libs/docs/json_schema/json/test-required.json new file mode 100644 index 0000000..b94951f --- /dev/null +++ b/srcs/libs/docs/json_schema/json/test-required.json @@ -0,0 +1,11 @@ +{ + "test-emptyjson": {}, + "test-keywords-abc": {"a":"a", "b":"b", "c":"c"}, + "test-keywords-bc": {"b":"b", "c":"c"}, + "test-keywords-ac": {"a":"a", "c":"c"}, + "test-keywords-ab": {"a":"a", "b":"b"}, + "test-keywords-a": {"a":"a"}, + "test-keywords-b": {"b":"b"}, + "test-keywords-c": {"c":"c"}, + "test-wrongtype": 123 +} diff --git a/srcs/libs/docs/json_schema/json/test-type.json b/srcs/libs/docs/json_schema/json/test-type.json new file mode 100644 index 0000000..46553e3 --- /dev/null +++ b/srcs/libs/docs/json_schema/json/test-type.json @@ -0,0 +1,8 @@ +{ + "test-object" : {"1":"2"}, + "test-array" : [1,2,3], + "test-string" : "101", + "test-integer" : 101, + "test-double" : 101.1, + "test-boolean" : true +} diff --git a/srcs/libs/docs/json_schema/json/test-uniqueitems.json b/srcs/libs/docs/json_schema/json/test-uniqueitems.json new file mode 100644 index 0000000..919d1d1 --- /dev/null +++ b/srcs/libs/docs/json_schema/json/test-uniqueitems.json @@ -0,0 +1,10 @@ +{ + "array-empty": [], + "array-unique1": [1,2,3,4,5,6], + "array-unique2": ["one","two","three"], + "array-notunique1": [1,2,3,3,5,6], + "array-notunique2": [1,2,3,4,5,5], + "array-notunique3": [1,1,3,4,5,6], + "array-notunique4": [1,1,1,4,5,6], + "array-notunique5": ["one", "two", "two"] +} diff --git a/srcs/libs/docs/json_schema/json/vectors-variants.json b/srcs/libs/docs/json_schema/json/vectors-variants.json new file mode 100644 index 0000000..01883e6 --- /dev/null +++ b/srcs/libs/docs/json_schema/json/vectors-variants.json @@ -0,0 +1,106 @@ +{ +"truejson": + { + "ID": "lcss_vectors", + "Version": "v01", + "Services": [ + { + "Service": "slitherio", + "NetifyName": "gaming.slitherio", + "max_diff": 1, + "max_length": 10, + "ValidPorts": [ [444,445] ], + "vectors": [ + { + "ID": "v0", + "CT": 0, + "TT": 0, + "ignore_indices": [0,1], + "proto": "tcp", + "confidence": 10, + "vector": [1,2,3,4,5,6,7,8,9] + } + ] + } + ] + }, + + "validport_toohigh": + { + "ID": "lcss_vectors", + "Version": "v01", + "Services": [ + { + "Service": "slitherio", + "NetifyName": "gaming.slitherio", + "max_diff": 1, + "max_length": 10, + "ValidPorts": [ [444,445], [50000,100000] ], + "vectors": [ + { + "ID": "v0", + "CT": 0, + "TT": 0, + "ignore_indices": [0,1], + "proto": "tcp", + "confidence": 10, + "vector": [1,2,3,4,5,6,7,8,9] + } + ] + } + ] + }, + + "ignore_indices_index_too_high": + { + "ID": "lcss_vectors", + "Version": "v01", + "Services": [ + { + "Service": "slitherio", + "NetifyName": "gaming.slitherio", + "max_diff": 1, + "max_length": 10, + "ValidPorts": [ [444,445] ], + "vectors": [ + { + "ID": "v0", + "CT": 0, + "TT": 0, + "ignore_indices": [0,20], + "proto": "tcp", + "confidence": 10, + "vector": [1,2,3,4,5,6,7,8,9] + } + ] + } + ] + }, + + "too_many_vector_elements": + { + "ID": "lcss_vectors", + "Version": "v01", + "Services": [ + { + "Service": "slitherio", + "NetifyName": "gaming.slitherio", + "max_diff": 1, + "max_length": 10, + "ValidPorts": [ [444,445] ], + "vectors": [ + { + "ID": "v0", + "CT": 0, + "TT": 0, + "ignore_indices": [0,1], + "proto": "tcp", + "confidence": 10, + "vector": [0,1,2,3,4,5,6,7,8,9,10] + } + ] + } + ] + } + + } diff --git a/srcs/libs/docs/json_schema/json/vectors.json b/srcs/libs/docs/json_schema/json/vectors.json new file mode 100644 index 0000000..64f1f98 --- /dev/null +++ b/srcs/libs/docs/json_schema/json/vectors.json @@ -0,0 +1,760 @@ +{ + "ID": "lcss_vectors", + "Version": "v01", + "Services": [ + { + "Service": "slitherio", + "NetifyName": "gaming.slitherio", + "ValidPorts": [ + [ + 444, + 444 + ] + ], + "vectors": [ + { + "ID": "v0", + "CT": 0, + "TT": 0, + "ignore_indices": [ + 0, + 1 + ], + "proto": "tcp", + "confidence": 10, + "vector": [ + 0, + 0 + ] + } + ] + }, + { + "Service": "steamworks", + "NetifyName": "gaming.steamworks", + "ValidPorts": [ + [ + 4380, + 4380 + ], + [ + 27000, + 27100 + ] + ], + "vectors": [ + { + "ID": "v0", + "CT": 2, + "TT": 20, + "ignore_indices": [], + "proto": "udp", + "confidence": 10, + "vector": [ + 1300, + -214 + ] + }, + { + "ID": "v1", + "CT": 2, + "TT": 0, + "ignore_indices": [], + "proto": "udp", + "confidence": 10, + "vector": [ + 1300, + 1300 + ] + } + ] + }, + { + "Service": "leagueoflegends", + "NetifyName": "gaming.leagueoflegends", + "ValidPorts": [ + [ + 5000, + 5500 + ] + ], + "vectors": [ + { + "ID": "v0", + "CT": 2, + "TT": 0, + "ignore_indices": [], + "proto": "udp", + "confidence": 10, + "vector": [ + 515, + -107 + ] + }, + { + "ID": "v1", + "CT": 2, + "TT": 0, + "ignore_indices": [], + "proto": "udp", + "confidence": 10, + "vector": [ + 515, + 515 + ] + } + ] + }, + { + "Service": "apex", + "NetifyName": "gaming.apex", + "ValidPorts": [ + [ + 1024, + 1124 + ], + [ + 18000, + 18000 + ], + [ + 29900, + 29900 + ], + [ + 37000, + 40000 + ] + ], + "vectors": [ + { + "ID": "v0", + "CT": 2, + "TT": 0, + "ignore_indices": [], + "proto": "udp", + "confidence": 10, + "vector": [ + 50, + -58 + ] + }, + { + "ID": "v1", + "CT": 2, + "TT": 0, + "ignore_indices": [], + "proto": "udp", + "confidence": 10, + "vector": [ + 50, + 50 + ] + } + ] + }, + { + "Service": "rainbow6", + "NetifyName": "gaming.rainbow6", + "ValidPorts": [ + [ + 3074, + 3074 + ], + [ + 4380, + 4380 + ], + [ + 6015, + 6015 + ], + [ + 10000, + 10099 + ] + ], + "vectors": [ + { + "ID": "v0", + "CT": 2, + "TT": 0, + "ignore_indices": [], + "proto": "udp", + "confidence": 10, + "vector": [ + 139, + 139 + ] + }, + { + "ID": "v1", + "CT": 2, + "TT": 0, + "ignore_indices": [], + "proto": "udp", + "confidence": 10, + "vector": [ + 139, + -131 + ] + } + ] + }, + { + "Service": "iperf3", + "NetifyName": "domos.iperf3", + "ValidPorts": [ + [ + 5201, + 5201 + ] + ], + "vectors": [ + { + "ID": "v2", + "ignore_indices": [], + "CT": 2, + "TT": 0, + "proto": "udp", + "confidence": 10, + "vector": [ + 4, + -4 + ] + } + ] + }, + { + "Service": "flent", + "NetifyName": "domos.flent", + "ValidPorts": [ + [ + 2116, + 2116 + ] + ], + "vectors": [ + { + "ID": "v2", + "ignore_indices": [], + "CT": 0, + "TT": 0, + "proto": "udp", + "confidence": 10, + "vector": [ + 4, + -4 + ] + } + ] + }, + { + "Service": "unrealengine4", + "NetifyName": "gaming.unrealengine4", + "ValidPorts": [ + [ + 12000, + 65535 + ] + ], + "vectors": [ + { + "ID": "v0", + "ignore_indices": [], + "CT": 2, + "TT": 0, + "proto": "udp", + "confidence": 10, + "vector": [ + 29, + -29 + ] + }, + { + "ID": "v1", + "ignore_indices": [], + "CT": 2, + "TT": 0, + "proto": "udp", + "confidence": 10, + "vector": [ + 29, + 29 + ] + } + ] + }, + { + "Service": "overwatch", + "NetifyName": "gaming.overwatch", + "ValidPorts": [ + [ + 26400, + 27000 + ] + ], + "vectors": [ + { + "ID": "v0", + "ignore_indices": [], + "CT": 2, + "TT": 0, + "proto": "udp", + "confidence": 10, + "vector": [ + 34, + -34 + ] + }, + { + "ID": "v1", + "ignore_indices": [], + "CT": 2, + "TT": 0, + "proto": "udp", + "confidence": 10, + "vector": [ + 34, + 34 + ] + } + ] + }, + { + "Service": "valorant", + "NetifyName": "gaming.valorant", + "ValidPorts": [ + [ + 7000, + 7500 + ] + ], + "vectors": [ + { + "ID": "v0", + "ignore_indices": [], + "CT": 2, + "TT": 0, + "proto": "udp", + "confidence": 10, + "vector": [ + 53, + -33 + ] + }, + { + "ID": "v1", + "ignore_indices": [], + "CT": 2, + "TT": 0, + "proto": "udp", + "confidence": 10, + "vector": [ + 53, + 53 + ] + } + ] + }, + { + "Service": "seaofthieves", + "NetifyName": "gaming.seaofthieves", + "ValidPorts": [ + [ + 30000, + 32000 + ] + ], + "vectors": [ + { + "ID": "v0", + "ignore_indices": [], + "CT": 2, + "TT": 0, + "proto": "udp", + "confidence": 10, + "vector": [ + 51, + -88 + ] + }, + { + "ID": "v1", + "ignore_indices": [], + "CT": 2, + "TT": 0, + "proto": "udp", + "confidence": 10, + "vector": [ + 51, + 51 + ] + }, + { + "ID": "v1", + "ignore_indices": [], + "CT": 2, + "TT": 0, + "proto": "udp", + "confidence": 10, + "vector": [ + 272, + 60 + ] + } + ] + }, + { + "Service": "rust", + "NetifyName": "gaming.rust", + "ValidPorts": [ + [ + 28015, + 28015 + ], + [ + 28225, + 28225 + ] + ], + "vectors": [ + { + "ID": "v0", + "ignore_indices": [], + "CT": 2, + "TT": 0, + "proto": "udp", + "confidence": 10, + "vector": [ + 1372, + -28 + ] + }, + { + "ID": "v1", + "ignore_indices": [], + "CT": 2, + "TT": 0, + "proto": "udp", + "confidence": 10, + "vector": [ + 1372, + 1372 + ] + } + ] + }, + { + "Service": "minecraft", + "NetifyName": "gaming.minecraft", + "ValidPorts": [ + [ + 19132, + 19133 + ], + [ + 19000, + 20000 + ] + ], + "vectors": [ + { + "ID": "v0", + "ignore_indices": [], + "CT": 2, + "TT": 0, + "proto": "udp", + "confidence": 10, + "vector": [ + 33, + 1464 + ] + }, + { + "ID": "v1", + "ignore_indices": [], + "CT": 2, + "TT": 0, + "proto": "udp", + "confidence": 10, + "vector": [ + 33, + 33 + ] + } + ] + }, + { + "Service": "gta5", + "NetifyName": "gaming.gta5", + "ValidPorts": [ + [ + 6672, + 6672 + ], + [ + 61455, + 61458 + ] + ], + "vectors": [ + { + "ID": "v0", + "ignore_indices": [ + 1 + ], + "CT": 2, + "TT": 0, + "proto": "udp", + "confidence": 10, + "vector": [ + 221, + 0 + ] + }, + { + "ID": "v1", + "ignore_indices": [], + "CT": 2, + "TT": 0, + "proto": "udp", + "confidence": 10, + "vector": [ + 221, + -221 + ] + }, + { + "ID": "v3", + "ignore_indices": [ + 1 + ], + "CT": 2, + "TT": 0, + "proto": "udp", + "confidence": 10, + "vector": [ + 237, + 0 + ] + }, + { + "ID": "v3", + "ignore_indices": [ + 1 + ], + "CT": 2, + "TT": 0, + "proto": "udp", + "confidence": 10, + "vector": [ + 205, + 0 + ] + }, + { + "ID": "v3", + "ignore_indices": [ + 1 + ], + "CT": 2, + "TT": 0, + "proto": "udp", + "confidence": 10, + "vector": [ + 141, + 205 + ] + } + ] + }, + { + "Service": "starcraft2", + "NetifyName": "gaming.starcraft2", + "ValidPorts": [ + [ + 3724, + 3724 + ] + ], + "vectors": [ + { + "ID": "v0", + "CT": 2, + "TT": 0, + "ignore_indices": [], + "proto": "udp", + "confidence": 10, + "vector": [ + 20, + -20 + ] + }, + { + "ID": "v0", + "CT": 2, + "TT": 0, + "ignore_indices": [], + "proto": "udp", + "confidence": 10, + "vector": [ + 20, + 20 + ] + } + ] + }, + { + "Service": "video-conferencing", + "NetifyName": "video-conferencing.video-conf", + "ValidPorts": [ + [ + 3478, + 3478 + ] + ], + "vectors": [ + { + "ID": "v0", + "CT": 0, + "TT": 0, + "ignore_indices": [], + "proto": "udp", + "confidence": 4, + "vector": [] + } + ] + }, + { + "Service": "slack", + "NetifyName": "video-conferencing.slack", + "ValidPorts": [ + [ + 22466, + 22466 + ] + ], + "vectors": [ + { + "ID": "v0", + "CT": 0, + "TT": 0, + "ignore_indices": [], + "proto": "udp", + "confidence": 6, + "vector": [] + } + ] + }, + { + "Service": "webex", + "NetifyName": "video-conferencing.webex", + "ValidPorts": [ + [ + 9000, + 9000 + ] + ], + "vectors": [ + { + "ID": "v0", + "CT": 0, + "TT": 0, + "ignore_indices": [], + "proto": "udp", + "confidence": 6, + "vector": [] + } + ] + }, + { + "Service": "meet", + "NetifyName": "video-conferencing.meet", + "ValidPorts": [ + [ + 19302, + 19309 + ] + ], + "vectors": [ + { + "ID": "v0", + "CT": 0, + "TT": 0, + "ignore_indices": [], + "proto": "udp", + "confidence": 6, + "vector": [] + } + ] + }, + { + "Service": "zoom", + "NetifyName": "video-conferencing.zoom", + "ValidPorts": [ + [ + 3478, + 3479 + ], + [ + 8801, + 8802 + ] + ], + "vectors": [ + { + "ID": "v0", + "CT": 0, + "TT": 0, + "ignore_indices": [], + "proto": "udp", + "confidence": 6, + "vector": [] + } + ] + }, + { + "Service": "bluejeans", + "NetifyName": "video-conferencing.bluejeans", + "ValidPorts": [ + [ + 5000, + 5000 + ] + ], + "vectors": [ + { + "ID": "v0", + "CT": 0, + "TT": 0, + "ignore_indices": [], + "proto": "udp", + "confidence": 6, + "vector": [] + } + ] + }, + { + "Service": "teams/skype", + "NetifyName": "video-conferencing.teams/skype", + "ValidPorts": [ + [ + 3478, + 3481 + ] + ], + "vectors": [ + { + "ID": "v0", + "CT": 0, + "TT": 0, + "ignore_indices": [], + "proto": "udp", + "confidence": 6, + "vector": [] + } + ] + } + ] +} \ No newline at end of file diff --git a/srcs/libs/docs/json_schema/json/vectors2.json b/srcs/libs/docs/json_schema/json/vectors2.json new file mode 100644 index 0000000..6560cfa --- /dev/null +++ b/srcs/libs/docs/json_schema/json/vectors2.json @@ -0,0 +1,769 @@ +{ + "ID": "lcss_vectors", + "Version": "v01", + "Services": [ + { + "Service": "slitherio", + "NetifyName": "gaming.slitherio", + "max_diff": 1, + "max_length": 10, + "ValidPorts": [ + [ + 444, + 444 + ] + ], + "vectors": [ + { + "ID": "v0", + "TT": 0, + "ignore_indices": [ + 0, + 1 + ], + "proto": "tcp", + "confidence": 10, + "vector": [ + 0, + 0 + ] + } + ] + }, + { + "Service": "steamworks", + "NetifyName": "gaming.steamworks", + "max_diff": 1, + "max_length": 10, + "ValidPorts": [ + [ + 4380, + 4380 + ], + [ + 27000, + 27100 + ] + ], + "vectors": [ + { + "ID": "v0", + "CT": 2, + "TT": 20, + "ignore_indices": [], + "proto": "udp", + "confidence": 10, + "vector": [ + 1300, + -214 + ] + }, + { + "ID": "v1", + "CT": 2, + "TT": 0, + "ignore_indices": [], + "proto": "udp", + "confidence": 10, + "vector": [ + 1300, + 1300 + ] + } + ] + }, + { + "Service": "leagueoflegends", + "NetifyName": "gaming.leagueoflegends", + "max_diff": 1, + "max_length": 10, + "ValidPorts": [ + [ + 5000, + 5500 + ] + ], + "vectors": [ + { + "ID": "v0", + "CT": 2, + "TT": 0, + "ignore_indices": [], + "proto": "udp", + "confidence": 10, + "vector": [ + 515, + -107 + ] + }, + { + "ID": "v1", + "CT": 2, + "TT": 0, + "ignore_indices": [], + "proto": "udp", + "confidence": 10, + "vector": [ + 515, + 515 + ] + } + ] + }, + { + "Service": "apex", + "NetifyName": "gaming.apex", + "max_diff": 1, + "max_length": 10, + "ValidPorts": [ + [ + 1024, + 1124 + ], + [ + 18000, + 18000 + ], + [ + 29900, + 29900 + ], + [ + 37000, + 40000 + ] + ], + "vectors": [ + { + "ID": "v0", + "CT": 2, + "TT": 0, + "ignore_indices": [], + "proto": "udp", + "confidence": 10, + "vector": [ + 50, + -58 + ] + }, + { + "ID": "v1", + "CT": 2, + "TT": 0, + "ignore_indices": [], + "proto": "udp", + "confidence": 10, + "vector": [ + 50, + 50 + ] + } + ] + }, + { + "Service": "rainbow6", + "NetifyName": "gaming.rainbow6", + "max_diff": 1, + "max_length": 10, + "ValidPorts": [ + [ + 3074, + 3074 + ], + [ + 4380, + 4380 + ], + [ + 6015, + 6015 + ], + [ + 10000, + 10099 + ] + ], + "vectors": [ + { + "ID": "v0", + "CT": 2, + "TT": 0, + "ignore_indices": [], + "proto": "udp", + "confidence": 10, + "vector": [ + 139, + 139 + ] + }, + { + "ID": "v1", + "CT": 2, + "TT": 0, + "ignore_indices": [], + "proto": "udp", + "confidence": 10, + "vector": [ + 139, + -131 + ] + } + ] + }, + { + "Service": "iperf3", + "NetifyName": "domos.iperf3", + "ValidPorts": [ + [ + 5201, + 5201 + ] + ], + "vectors": [ + { + "ID": "v2", + "ignore_indices": [], + "CT": 2, + "TT": 0, + "proto": "udp", + "confidence": 10, + "vector": [ + 4, + -4 + ] + } + ] + }, + { + "Service": "flent", + "NetifyName": "domos.flent", + "ValidPorts": [ + [ + 2116, + 2116 + ] + ], + "vectors": [ + { + "ID": "v2", + "ignore_indices": [], + "CT": 0, + "TT": 0, + "proto": "udp", + "confidence": 10, + "vector": [ + 4, + -4 + ] + } + ] + }, + { + "Service": "unrealengine4", + "NetifyName": "gaming.unrealengine4", + "ValidPorts": [ + [ + 12000, + 65535 + ] + ], + "vectors": [ + { + "ID": "v0", + "ignore_indices": [], + "CT": 2, + "TT": 0, + "proto": "udp", + "confidence": 10, + "vector": [ + 29, + -29 + ] + }, + { + "ID": "v1", + "ignore_indices": [], + "CT": 2, + "TT": 0, + "proto": "udp", + "confidence": 10, + "vector": [ + 29, + 29 + ] + } + ] + }, + { + "Service": "overwatch", + "NetifyName": "gaming.overwatch", + "ValidPorts": [ + [ + 26400, + 27000 + ] + ], + "vectors": [ + { + "ID": "v0", + "ignore_indices": [], + "CT": 2, + "TT": 0, + "proto": "udp", + "confidence": 10, + "vector": [ + 34, + -34 + ] + }, + { + "ID": "v1", + "ignore_indices": [], + "CT": 2, + "TT": 0, + "proto": "udp", + "confidence": 10, + "vector": [ + 34, + 34 + ] + } + ] + }, + { + "Service": "valorant", + "NetifyName": "gaming.valorant", + "ValidPorts": [ + [ + 7000, + 7500 + ] + ], + "vectors": [ + { + "ID": "v0", + "ignore_indices": [], + "CT": 2, + "TT": 0, + "proto": "udp", + "confidence": 10, + "vector": [ + 53, + -33 + ] + }, + { + "ID": "v1", + "ignore_indices": [], + "CT": 2, + "TT": 0, + "proto": "udp", + "confidence": 10, + "vector": [ + 53, + 53 + ] + } + ] + }, + { + "Service": "seaofthieves", + "NetifyName": "gaming.seaofthieves", + "ValidPorts": [ + [ + 30000, + 32000 + ] + ], + "vectors": [ + { + "ID": "v0", + "ignore_indices": [], + "CT": 2, + "TT": 0, + "proto": "udp", + "confidence": 10, + "vector": [ + 51, + -88 + ] + }, + { + "ID": "v1", + "ignore_indices": [], + "CT": 2, + "TT": 0, + "proto": "udp", + "confidence": 10, + "vector": [ + 51, + 51 + ] + }, + { + "ID": "v1", + "ignore_indices": [], + "CT": 2, + "TT": 0, + "proto": "udp", + "confidence": 10, + "vector": [ + 272, + 60 + ] + } + ] + }, + { + "Service": "rust", + "NetifyName": "gaming.rust", + "ValidPorts": [ + [ + 28015, + 28015 + ], + [ + 28225, + 28225 + ] + ], + "vectors": [ + { + "ID": "v0", + "ignore_indices": [], + "CT": 2, + "TT": 0, + "proto": "udp", + "confidence": 10, + "vector": [ + 1372, + -28 + ] + }, + { + "ID": "v1", + "ignore_indices": [], + "CT": 2, + "TT": 0, + "proto": "udp", + "confidence": 10, + "vector": [ + 1372, + 1372 + ] + } + ] + }, + { + "Service": "minecraft", + "NetifyName": "gaming.minecraft", + "ValidPorts": [ + [ + 19132, + 19133 + ], + [ + 19000, + 20000 + ] + ], + "vectors": [ + { + "ID": "v0", + "ignore_indices": [], + "CT": 2, + "TT": 0, + "proto": "udp", + "confidence": 10, + "vector": [ + 33, + 1464 + ] + }, + { + "ID": "v1", + "ignore_indices": [], + "CT": 2, + "TT": 0, + "proto": "udp", + "confidence": 10, + "vector": [ + 33, + 33 + ] + } + ] + }, + { + "Service": "gta5", + "NetifyName": "gaming.gta5", + "ValidPorts": [ + [ + 6672, + 6672 + ], + [ + 61455, + 61458 + ] + ], + "vectors": [ + { + "ID": "v0", + "ignore_indices": [ + 1 + ], + "CT": 2, + "TT": 0, + "proto": "udp", + "confidence": 10, + "vector": [ + 221, + 0 + ] + }, + { + "ID": "v1", + "ignore_indices": [], + "CT": 2, + "TT": 0, + "proto": "udp", + "confidence": 10, + "vector": [ + 221, + -221 + ] + }, + { + "ID": "v3", + "ignore_indices": [ + 1 + ], + "CT": 2, + "TT": 0, + "proto": "udp", + "confidence": 10, + "vector": [ + 237, + 0 + ] + }, + { + "ID": "v3", + "ignore_indices": [ + 1 + ], + "CT": 2, + "TT": 0, + "proto": "udp", + "confidence": 10, + "vector": [ + 205, + 0 + ] + }, + { + "ID": "v3", + "ignore_indices": [ + 1 + ], + "CT": 2, + "TT": 0, + "proto": "udp", + "confidence": 10, + "vector": [ + 141, + 205 + ] + } + ] + }, + { + "Service": "starcraft2", + "NetifyName": "gaming.starcraft2", + "ValidPorts": [ + [ + 3724, + 3724 + ] + ], + "vectors": [ + { + "ID": "v0", + "CT": 2, + "TT": 0, + "ignore_indices": [], + "proto": "udp", + "confidence": 10, + "vector": [ + 20, + -20 + ] + }, + { + "ID": "v0", + "CT": 2, + "TT": 0, + "ignore_indices": [], + "proto": "udp", + "confidence": 10, + "vector": [ + 20, + 20 + ] + } + ] + }, + { + "Service": "video-conferencing", + "NetifyName": "video-conferencing.video-conf", + "ValidPorts": [ + [ + 3478, + 3478 + ] + ], + "vectors": [ + { + "ID": "v0", + "CT": 0, + "TT": 0, + "ignore_indices": [], + "proto": "udp", + "confidence": 4, + "vector": [] + } + ] + }, + { + "Service": "slack", + "NetifyName": "video-conferencing.slack", + "ValidPorts": [ + [ + 22466, + 22466 + ] + ], + "vectors": [ + { + "ID": "v0", + "CT": 0, + "TT": 0, + "ignore_indices": [], + "proto": "udp", + "confidence": 6, + "vector": [] + } + ] + }, + { + "Service": "webex", + "NetifyName": "video-conferencing.webex", + "ValidPorts": [ + [ + 9000, + 9000 + ] + ], + "vectors": [ + { + "ID": "v0", + "CT": 0, + "TT": 0, + "ignore_indices": [], + "proto": "udp", + "confidence": 6, + "vector": [] + } + ] + }, + { + "Service": "meet", + "NetifyName": "video-conferencing.meet", + "ValidPorts": [ + [ + 19302, + 19309 + ] + ], + "vectors": [ + { + "ID": "v0", + "CT": 0, + "TT": 0, + "ignore_indices": [], + "proto": "udp", + "confidence": 6, + "vector": [] + } + ] + }, + { + "Service": "zoom", + "NetifyName": "video-conferencing.zoom", + "ValidPorts": [ + [ + 3478, + 3479 + ], + [ + 8801, + 8802 + ] + ], + "vectors": [ + { + "ID": "v0", + "CT": 0, + "TT": 0, + "ignore_indices": [], + "proto": "udp", + "confidence": 6, + "vector": [] + } + ] + }, + { + "Service": "bluejeans", + "NetifyName": "video-conferencing.bluejeans", + "ValidPorts": [ + [ + 5000, + 5000 + ] + ], + "vectors": [ + { + "ID": "v0", + "CT": 0, + "TT": 0, + "ignore_indices": [], + "proto": "udp", + "confidence": 6, + "vector": [] + } + ] + }, + { + "Service": "teams/skype", + "NetifyName": "video-conferencing.teams/skype", + "ValidPorts": [ + [ + 3478, + 3481 + ] + ], + "vectors": [ + { + "ID": "v0", + "CT": 0, + "TT": 0, + "ignore_indices": [], + "proto": "udp", + "confidence": 6, + "vector": [] + } + ] + } + ] +} \ No newline at end of file diff --git a/srcs/libs/docs/json_schema/schema/empty.json b/srcs/libs/docs/json_schema/schema/empty.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/srcs/libs/docs/json_schema/schema/empty.json @@ -0,0 +1 @@ +{} diff --git a/srcs/libs/docs/json_schema/schema/flows.json b/srcs/libs/docs/json_schema/schema/flows.json new file mode 100644 index 0000000..98a0796 --- /dev/null +++ b/srcs/libs/docs/json_schema/schema/flows.json @@ -0,0 +1,68 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "Represents a collection of flows. For testing ref defs", + "type": "object", + "properties": { + "flows": { + "type": "array", + "items": { + "$ref": "#/$defs/flow" + } + } + }, + "required": [ + "flows" + ], + "$defs": { + "flow": { + "type": "object", + "properties": { + "flow_id": { + "type": "string" + }, + "service_name": { + "type": "string" + }, + "master_protocol": { + "type": "string" + }, + "sport": { + "type": "integer" + }, + "dport": { + "type": "integer" + }, + "src": { + "type": "string" + }, + "dst": { + "type": "string" + }, + "proto": { + "type": "string" + }, + "alive": { + "type": "string" + }, + "active": { + "type": "boolean" + }, + "extrakeythatdoesnotexist": { + "type": "string" + } + }, + "required": [ + "flow_id", + "service_name", + "master_protocol", + "sport", + "dport", + "src", + "dst", + "proto", + "alive", + "active" + ] + } + } +} \ No newline at end of file diff --git a/srcs/libs/docs/json_schema/schema/side-test-pattern.json b/srcs/libs/docs/json_schema/schema/side-test-pattern.json new file mode 100644 index 0000000..a007ed4 --- /dev/null +++ b/srcs/libs/docs/json_schema/schema/side-test-pattern.json @@ -0,0 +1,12 @@ +{ + "description": "string pattern side tests", + "schema-non-object": { + "pattern": true + }, + "schema-invalid-regex": { + "pattern": "^[a-zA-Z0-9]*@[a-zA-Z0-9-_.]*[.]*?" + }, + "schema-valid": { + "pattern": "boten*" + } +} \ No newline at end of file diff --git a/srcs/libs/docs/json_schema/schema/side-test-patternProperties.json b/srcs/libs/docs/json_schema/schema/side-test-patternProperties.json new file mode 100644 index 0000000..5a28e5b --- /dev/null +++ b/srcs/libs/docs/json_schema/schema/side-test-patternProperties.json @@ -0,0 +1,23 @@ +{ + "description": "patternProperties side tests", + "schema-non-object": { + "patternProperties": true + }, + "schema-invalid-regex": { + "patternProperties": { + "^[a-zA-Z0-9]*@[a-zA-Z0-9-_.]*[.]*?": { + "type": "object" + } + } + }, + "schema-valid": { + "patternProperties": { + "boten*": { + "type": "string" + }, + "anna.*": { + "type": "string" + } + } + } +} diff --git a/srcs/libs/docs/json_schema/schema/test-anyof.json b/srcs/libs/docs/json_schema/schema/test-anyof.json new file mode 100644 index 0000000..303fb74 --- /dev/null +++ b/srcs/libs/docs/json_schema/schema/test-anyof.json @@ -0,0 +1,51 @@ +{ + "acceptstringandinteger": { + "$schema": "https://json-schema.org/draft/2019-09/schema", + "type": "object", + "anyOf": [ + { + "properties": { + "id": { + "type": "integer" + } + } + }, + { + "properties": { + "id": { + "type": "string" + } + } + } + ], + "required": [ + "id" + ] + }, + "inarray": { + "$schema": "https://json-schema.org/draft/2019-09/schema", + "type": "object", + "properties": { + "anna": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "required": [ + "id" + ] + } + ] + } + } + } + }, + "anyOfSchemaError": { + "$schema": "https://json-schema.org/draft/2019-09/schema", + "type": "object", + "anyOf": [ + "type" + ] + } +} \ No newline at end of file diff --git a/srcs/libs/docs/json_schema/schema/test-enum.json b/srcs/libs/docs/json_schema/schema/test-enum.json new file mode 100644 index 0000000..5ef25f7 --- /dev/null +++ b/srcs/libs/docs/json_schema/schema/test-enum.json @@ -0,0 +1,22 @@ +{ + "enum-string": { + "type": "string", + "enum": [ + "one", + "two", + "three" + ] + }, + "enum-integer": { + "type": "integer", + "enum": [ + 1, + 2, + 3 + ] + }, + "enum-schemaerror": { + "type": "integer", + "enum": "1,2,3" + } +} \ No newline at end of file diff --git a/srcs/libs/docs/json_schema/schema/test-items.json b/srcs/libs/docs/json_schema/schema/test-items.json new file mode 100644 index 0000000..67ea610 --- /dev/null +++ b/srcs/libs/docs/json_schema/schema/test-items.json @@ -0,0 +1,51 @@ +{ + "test1": { + "type": "object", + "properties": { + "flowarray": { + "type": "array", + "items": { + "type": "object", + "required": [ + "key1", + "key2" + ], + "properties": { + "key1": { + "type": "integer" + }, + "key2": { + "type": "string" + }, + } + } + } + } + }, + "nested": { + "type": "object", + "properties": { + "flowarray": { + "type": "array", + "items": { + "type": "object", + "required": [ + "key1", + "key2" + ], + "properties": { + "key1": { + "type": "string" + }, + "key2": { + "type": "array", + "items": { + "type": "integer" + } + }, + } + } + } + } + } +} \ No newline at end of file diff --git a/srcs/libs/docs/json_schema/schema/test-maxmin-items.json b/srcs/libs/docs/json_schema/schema/test-maxmin-items.json new file mode 100644 index 0000000..88d99d1 --- /dev/null +++ b/srcs/libs/docs/json_schema/schema/test-maxmin-items.json @@ -0,0 +1,7 @@ +{ + "schema-2-3": { + "type": "array", + "maxItems": 3, + "minItems": 2 + } +} diff --git a/srcs/libs/docs/json_schema/schema/test-properties.json b/srcs/libs/docs/json_schema/schema/test-properties.json new file mode 100644 index 0000000..e1567cd --- /dev/null +++ b/srcs/libs/docs/json_schema/schema/test-properties.json @@ -0,0 +1,80 @@ +{ + "noproperties": + { + "type": "object" + }, + + "emptyproperties": + { + "type": "object", + "properties": {} + }, + + "typetests": + { + "type": "object", + "properties": { + "computer": { + "type": "string" + }, + "phone": { + "type": "integer" + }, + "switch": { + "type": "boolean" + }, + "object": { + "type": "object" + }, + "list": { + "type": "array" + } + } + }, + + "typetests2": + { + "type": "object", + "properties": { + "computer": { + "type": "string" + }, + "phone": { + "type": "integer" + }, + "switch": { + "type": "boolean" + }, + "object": { + "type": "object" + }, + "list": { + "type": "array" + } + } + }, + + "string-properties": + { + "type": "object", + "properties": { + "computer1": { + "type": "string" + }, + "computer2": { + "type": "string", + "maxLength": 4 + }, + "computer3": { + "type": "string", + "minLength": 2, + "maxLength": 2 + }, + "computer4": { + "type": "string", + "minLength": 3, + "maxLength": 4 + } + } + } +} diff --git a/srcs/libs/docs/json_schema/schema/test-reqs-recursively.json b/srcs/libs/docs/json_schema/schema/test-reqs-recursively.json new file mode 100644 index 0000000..ab69c08 --- /dev/null +++ b/srcs/libs/docs/json_schema/schema/test-reqs-recursively.json @@ -0,0 +1,14 @@ +{ + "type": "object", + "properties": { + "person": { + "type": "object", + "properties": { + "name": {"type": "string", "minLength": 4}, + "husbands": {"type": "integer"} + }, + "required": ["name"] + } + }, + "required": ["person"] +} diff --git a/srcs/libs/docs/json_schema/schema/test-required.json b/srcs/libs/docs/json_schema/schema/test-required.json new file mode 100644 index 0000000..2be23af --- /dev/null +++ b/srcs/libs/docs/json_schema/schema/test-required.json @@ -0,0 +1,12 @@ +{ + "test-emptyschema": {}, + "test-norequired": {"type": "object"}, + "test-required-empty": {"type": "object", "required": []}, + "test-require-abc": {"type": "object", "required": ["a","b","c"]}, + "test-require-bc": {"type": "object", "required": ["b","c"]}, + "test-require-ac": {"type": "object", "required": ["a","c"]}, + "test-require-ab": {"type": "object", "required": ["a","b"]}, + "test-require-a": {"type": "object", "required": ["a"]}, + "test-require-b": {"type": "object", "required": ["b"]}, + "test-require-c": {"type": "object", "required": ["c"]} +} diff --git a/srcs/libs/docs/json_schema/schema/test-type.json b/srcs/libs/docs/json_schema/schema/test-type.json new file mode 100644 index 0000000..f7754df --- /dev/null +++ b/srcs/libs/docs/json_schema/schema/test-type.json @@ -0,0 +1,12 @@ +{ + "test-object" : {"type": "object"}, + "test-array" : {"type": "array"}, + "test-string" : {"type": "string"}, + "test-integer" : {"type": "integer"}, + "test-double" : {"type": "double"}, + "test-boolean" : {"type": "boolean"}, + "test-nonsense" : {"type": "nonsense"}, + "test-nonstring" : {"type": 1 }, + "test-nonstring2" : {"type": {"type": "yes" } } + } + \ No newline at end of file diff --git a/srcs/libs/docs/json_schema/schema/test-uniqueitems.json b/srcs/libs/docs/json_schema/schema/test-uniqueitems.json new file mode 100644 index 0000000..284c5e4 --- /dev/null +++ b/srcs/libs/docs/json_schema/schema/test-uniqueitems.json @@ -0,0 +1,14 @@ +{ + "unique": { + "type": "array", + "uniqueItems": true + }, + "notunique": { + "type": "array", + "uniqueItems": false + }, + "schemaerror": { + "type": "array", + "uniqueItems": 1 + } +} \ No newline at end of file diff --git a/srcs/libs/docs/json_schema/schema/vectors.json b/srcs/libs/docs/json_schema/schema/vectors.json new file mode 100644 index 0000000..6558acc --- /dev/null +++ b/srcs/libs/docs/json_schema/schema/vectors.json @@ -0,0 +1,127 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [ + "ID", + "Version", + "Services" + ], + "properties": { + "ID": { + "type": "string", + "enum": [ + "lcss_vectors" + ] + }, + "Version": { + "type": "string", + "minLength": 2 + }, + "Services": { + "type": "array", + "minItems": 1, + "items": { + "type": "object", + "required": [ + "Service", + "NetifyName", + "ValidPorts", + "vectors" + ], + "properties": { + "Service": { + "type": "string", + "minLength": 4 + }, + "NetifyName": { + "type": "string", + "minLength": 4 + }, + "max_diff": { + "type": "integer" + }, + "max_length": { + "type": "integer" + }, + "ValidPorts": { + "type": "array", + "minItems": 1, + "items": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "type": "integer", + "minimum": 1, + "maximum": 65535 + } + } + }, + "vectors": { + "type": "array", + "minItems": 1, + "items": { + "type": "object", + "required": [ + "ID", + "CT", + "TT", + "ignore_indices", + "proto", + "confidence", + "vector" + ], + "properties": { + "ID": { + "type": "string", + "enum": [ + "v0", + "v1", + "v2", + "v3" + ] + }, + "CT": { + "type": "integer", + "minimum": 0 + }, + "TT": { + "type": "integer", + "minimum": 0 + }, + "ignore_indices": { + "type": "array", + "maxItems": 10, + "items": { + "type": "integer", + "minimum": 0, + "maximum": 9 + } + }, + "proto": { + "type": "string", + "enum": [ + "tcp", + "udp" + ] + }, + "confidence": { + "type": "integer" + }, + "vector": { + "type": "array", + "maxItems": 10, + "items": { + "type": "integer", + "minimum": -1500, + "maximum": 1500 + } + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/srcs/libs/include/json_schema/internal.h b/srcs/libs/include/json_schema/internal.h new file mode 100644 index 0000000..9f92388 --- /dev/null +++ b/srcs/libs/include/json_schema/internal.h @@ -0,0 +1,26 @@ +#ifndef __INTERNAL_H +#define __INTERNAL_H + +#include + +int _jdac_load(const char *jsonfile, const char *jsonschema); +int __jdac_inspect_type(json_object *jobj, const char *type); +int _jdac_check_type (json_object *jobj, json_object *jschema); +int _jdac_check_required (json_object *jobj, json_object *jschema); +int _jdac_check_properties (json_object *jobj, json_object *jschema); +int _jdac_check_prefixItems_and_items (json_object *jobj, json_object *jschema); +int _jdac_value_is_equal (json_object *jobj1, json_object *jobj2); +int _jdac_check_const (json_object *jobj, json_object *jschema); +int _jdac_check_enums (json_object *jobj, json_object *jschema); +int _jdac_check_uniqueItems (json_object *jobj, json_object *jschema); +int _jdac_check_maxmin_items (json_object *jobj, json_object *jschema); +int _jdac_validate_array (json_object *jobj, json_object *jschema); +int _jdac_validate_object (json_object *jobj, json_object *jschema); +int _jdac_validate_string (json_object *jobj, json_object *jschema); +int _jdac_validate_integer (json_object *jobj, json_object *jschema); +int _jdac_validate_double (json_object *jobj, json_object *jschema); +int _jdac_validate_number (json_object *jobj, json_object *jschema, double value); +int _jdac_validate_boolean (json_object *jobj, json_object *jschema); +int _jdac_validate_instance (json_object *jobj, json_object *jschema); + +#endif // __INTERNAL_H diff --git a/srcs/libs/include/json_schema/jsoncdaccord.h b/srcs/libs/include/json_schema/jsoncdaccord.h new file mode 100644 index 0000000..8ef0700 --- /dev/null +++ b/srcs/libs/include/json_schema/jsoncdaccord.h @@ -0,0 +1,40 @@ +#ifndef __JSONCDACCORD_H +#define __JSONCDACCORD_H + +#include + +enum jdac_errors { + JDAC_ERR_VALID = 0, + JDAC_ERR_GENERAL_ERROR, + JDAC_ERR_JSON_NOT_FOUND, + JDAC_ERR_SCHEMA_NOT_FOUND, + JDAC_ERR_WRONG_ARGS, + JDAC_ERR_SCHEMA_ERROR, + JDAC_ERR_INVALID, + JDAC_ERR_INVALID_TYPE, + JDAC_ERR_INVALID_REQUIRED, + JDAC_ERR_INVALID_SUBSCHEMALOGIC, + JDAC_ERR_INVALID_CONST, + JDAC_ERR_INVALID_ENUMS, + JDAC_ERR_INVALID_STRLEN, + JDAC_ERR_INVALID_UNIQUEITEMS, + JDAC_ERR_INVALID_PREFIXITEMS, + JDAC_ERR_INVALID_ITEMS, + JDAC_ERR_INVALID_CONTAINS, + JDAC_ERR_INVALID_ARRAYLEN, + JDAC_ERR_INVALID_NUMBER, + JDAC_ERR_INVALID_PATTERNMATCH, + JDAC_ERR_INVALID_REF, + JDAC_REGEX_MISMATCH, + JDAC_REGEX_MATCH, + JDAC_REGEX_COMPILE_FAILED, + JDAC_ERR_MAX +}; + +int jdac_validate_file(const char *jsonfile, const char *jsonschemafile); +int jdac_validate(json_object *jobj, json_object *jschema); +int jdac_ref_set_localpath(const char *_localpath); + +const char *jdac_errorstr(unsigned int jdac_errors); + +#endif //__JSONCDACCORD_H diff --git a/srcs/libs/include/json_schema/optional.h b/srcs/libs/include/json_schema/optional.h new file mode 100644 index 0000000..5b2c968 --- /dev/null +++ b/srcs/libs/include/json_schema/optional.h @@ -0,0 +1,36 @@ +#ifndef __OPTIONAL_H +#define __OPTIONAL_H + +#include + +typedef struct storage_node { + char JSONPtrURI[128]; + char id[128]; + char anchor[32]; + char dynamicAnchor[32]; + json_object *json_instance_ptr; + json_object *json_schema_ptr; + int is_root; + struct storage_node *next; +} storage_node; + +int _jdac_match_string_with_regex(const char* regex_pattern, const char* value); +int _jdac_check_pattern(json_object *jobj, json_object *jschema); +int _jdac_check_patternproperties(json_object *jobj, json_object *jschema); +int _jdac_check_additionalproperties(json_object *jobj, json_object *jschema); +int _jdac_check_propertynames(json_object *jobj, json_object *jschema); +int _jdac_check_subschemalogic(json_object *jobj, json_object *jschema); +int _jdac_check_contains_and_minmaxcontains(json_object *jobj, json_object *jschema); + +char* _jdac_download_schema(const char *url); +const char* _jdac_download_resolve(const char *uri); + +int _jdac_store_traverse_json(storage_node **head, json_object *jschema, char *pathbuffer); +void _jdac_store_free(storage_node **head); +void _jdac_store_print(storage_node *head); +json_object* _jdac_store_resolve(storage_node *list, const char *uri); +storage_node* _jdac_store_get_root_node(storage_node *head); + +int _jdac_check_ref(json_object *jobj, json_object *jschema, storage_node *storage_list); + +#endif // __OPTIONAL_H diff --git a/srcs/libs/include/proto.h b/srcs/libs/include/proto.h index 89cf5e8..46a075e 100644 --- a/srcs/libs/include/proto.h +++ b/srcs/libs/include/proto.h @@ -10,6 +10,8 @@ extern "C" { #endif #include +#define JSON_SCHEMA_ON (1) + typedef enum { CRYPTO_NONE = 0, CRYPTO_BASE64 = 1, @@ -19,7 +21,7 @@ typedef enum { } PROTO_CRYPTO_TYPE; const char *proto_create_new(cJSON *pMsgCtx, int httpCode); -const char *proto_decode_context(const char *pString, unsigned int *pVer, unsigned long long *pTm); +const char *proto_decode_context(const char *pString, unsigned int *pVer, unsigned long long *pTm, int *pErrCode); #ifdef __cplusplus } #endif diff --git a/srcs/libs/include/user_errno.h b/srcs/libs/include/user_errno.h index a76a3d7..9648ffd 100644 --- a/srcs/libs/include/user_errno.h +++ b/srcs/libs/include/user_errno.h @@ -51,17 +51,18 @@ extern "C" { ERR_CODE(ERR_MQ_SEND_MSG, 37, "消息队列发送消息失败") \ ERR_CODE(ERR_JSON_CREAT_OBJ, 38, "创建JSON对象失败") \ ERR_CODE(ERR_JSON_PRASE_OBJ, 39, "解析JSON对象失败") \ - ERR_CODE(ERR_CREATE_NETIF, 40, "创建网络接口失败") \ - ERR_CODE(ERR_CREATE_PPPOE_NETIF, 41, "创建PPPoE网络接口失败") \ - ERR_CODE(ERR_CREATE_PPP_SESSION, 42, "创建PPP连接失败") \ - ERR_CODE(ERR_MISC_GET_IPADDR, 43, "获取网卡IP地址失败") \ - ERR_CODE(ERR_MISC_GET_NETMASK, 44, "获取网卡子网掩码失败") \ - ERR_CODE(ERR_MISC_GET_GATEWAY, 45, "获取网卡网关地址失败") \ - ERR_CODE(ERR_MISC_GET_MACADDR, 46, "获取网卡MAC地址失败") \ - ERR_CODE(ERR_MENU_EXIT, 47, "菜单执行完后自动退出") \ - ERR_CODE(ERR_HTTP_UNSUP_METHOD, 48, "不支持的 HTTP 请求方法") \ - ERR_CODE(ERR_HTTP_UNSUP_PAGE, 49, "找不到 HTTP 服务") \ - ERR_CODE(ERR_PROTO_DECODE, 50, "HTTP 协议解析失败") + ERR_CODE(ERR_JSON_VALID_SCH, 40, "JSON数据验证失败") \ + ERR_CODE(ERR_CREATE_NETIF, 41, "创建网络接口失败") \ + ERR_CODE(ERR_CREATE_PPPOE_NETIF, 42, "创建PPPoE网络接口失败") \ + ERR_CODE(ERR_CREATE_PPP_SESSION, 43, "创建PPP连接失败") \ + ERR_CODE(ERR_MISC_GET_IPADDR, 44, "获取网卡IP地址失败") \ + ERR_CODE(ERR_MISC_GET_NETMASK, 45, "获取网卡子网掩码失败") \ + ERR_CODE(ERR_MISC_GET_GATEWAY, 46, "获取网卡网关地址失败") \ + ERR_CODE(ERR_MISC_GET_MACADDR, 47, "获取网卡MAC地址失败") \ + ERR_CODE(ERR_MENU_EXIT, 48, "菜单执行完后自动退出") \ + ERR_CODE(ERR_HTTP_UNSUP_METHOD, 49, "不支持的 HTTP 请求方法") \ + ERR_CODE(ERR_HTTP_UNSUP_PAGE, 50, "找不到 HTTP 服务") \ + ERR_CODE(ERR_PROTO_DECODE, 51, "HTTP 协议解析失败") #define GENERATE_ENUM(ENUM, no, x) ENUM, diff --git a/srcs/libs/json/json_schema/CMakeLists.txt b/srcs/libs/json/json_schema/CMakeLists.txt new file mode 100644 index 0000000..cf6258f --- /dev/null +++ b/srcs/libs/json/json_schema/CMakeLists.txt @@ -0,0 +1,42 @@ +INCLUDE_DIRECTORIES(../../include/json_schema) + +SET(jsoncdac_SOURCES validate.c) + + +LIST(APPEND OPTIONAL + pattern patternproperties additionalproperties propertynames subschemalogic + contains store ref) + +FOREACH (src ${OPTIONAL}) + STRING(TOUPPER ${src} srcupper) + OPTION(BUILD_${srcupper} "${src} option" ON) + IF (BUILD_${srcupper}) + LIST(APPEND SUPPORTED_OPTIONAL ${src}) + SET(jsoncdac_SOURCES ${jsoncdac_SOURCES} ${src}.c) + ADD_DEFINITIONS(-DJDAC_${srcupper}) + ENDIF () +ENDFOREACH () + +IF (BUILD_PATTERNPROPERTIES OR BUILD_PATTERN) + SET(jsoncdac_SOURCES ${jsoncdac_SOURCES} regex_match.c) + #set(EXTRA_LIBS regex) +ENDIF () + +# always supported +LIST(APPEND SUPPORTED_BASE type enum required properties anyOf minLength maxLength minimum maximum const) +LIST(APPEND SUPPORTED_BASE minItems maxItems uniqueItems items) +STRING(REPLACE ";" "," SUPPORTED_BASE_STR "${SUPPORTED_BASE}") +STRING(REPLACE ";" "," SUPPORTED_OPTIONAL_STR "${SUPPORTED_OPTIONAL}") + +ADD_LIBRARY(jsoncdac STATIC ${jsoncdac_SOURCES}) +TARGET_COMPILE_DEFINITIONS(jsoncdac PRIVATE "SUPPORTED_KEYWORDS_BASE=\"${SUPPORTED_BASE_STR}\"" "SUPPORTED_KEYWORDS_OPTIONAL=\"${SUPPORTED_OPTIONAL_STR}\"") +TARGET_LINK_LIBRARIES(jsoncdac ${EXTRA_LIBS}) + +SET_TARGET_PROPERTIES( + jsoncdac + PROPERTIES + OUTPUT_NAME jsoncdac + PUBLIC_HEADER "${HEADER_FILES}" + VERSION 0.0.2 + SOVERSION 1 +) \ No newline at end of file diff --git a/srcs/libs/json/json_schema/additionalproperties.c b/srcs/libs/json/json_schema/additionalproperties.c new file mode 100644 index 0000000..b7db9eb --- /dev/null +++ b/srcs/libs/json/json_schema/additionalproperties.c @@ -0,0 +1,58 @@ +#include "jsoncdaccord.h" +#include "internal.h" +#include "optional.h" + +int _jdac_check_additionalproperties(json_object *jobj, json_object *jschema) { + json_object *jaddprops = json_object_object_get(jschema, "additionalProperties"); + + if (!jaddprops) { + return JDAC_ERR_VALID; + } + + if (!json_object_is_type(jaddprops, json_type_object) && !json_object_is_type(jaddprops, json_type_boolean)) { + return JDAC_ERR_SCHEMA_ERROR; + } + + json_object *jprops = json_object_object_get(jschema, "properties"); +#ifdef JDAC_PATTERNPROPERTIES + json_object *jpatprops = json_object_object_get(jschema, "patternProperties"); +#endif + + json_object_object_foreach(jobj, jobj_key, jobj_val) { + + // if an instance key is found in properties, it is not an additional property + if (jprops) { + json_object *jprop = json_object_object_get(jprops, jobj_key); + if (jprop) { + continue; // ignore properties + } + } + + // if an instance key has a match in patternProperties, it is not an additional property +#ifdef JDAC_PATTERNPROPERTIES + int foundpatternproperty = 0; + if (jpatprops) { + if (json_object_is_type(jpatprops, json_type_object)) { + json_object_object_foreach(jpatprops, jpat_key, jpat_val) { + int ret = _jdac_match_string_with_regex(jpat_key, jobj_key); + if (ret == JDAC_REGEX_MATCH) { + foundpatternproperty = 1; + break; + } + } + } + if (foundpatternproperty) { + continue; + } + } +#endif + + // by this point we consider the instance to be an additional property + int err = _jdac_validate_instance(jobj_val, jaddprops); + if (err) { + return err; + } + } + + return JDAC_ERR_VALID; +} diff --git a/srcs/libs/json/json_schema/contains.c b/srcs/libs/json/json_schema/contains.c new file mode 100644 index 0000000..e943c29 --- /dev/null +++ b/srcs/libs/json/json_schema/contains.c @@ -0,0 +1,53 @@ +#include "jsoncdaccord.h" +#include "internal.h" +#include "optional.h" + +int _jdac_check_contains_and_minmaxcontains(json_object *jobj, json_object *jschema) { + int err; + + json_object *jcontains = json_object_object_get(jschema, "contains"); + if (!jcontains) { + return JDAC_ERR_VALID; + } + + if (!json_object_is_type(jcontains, json_type_object) && !json_object_is_type(jcontains, json_type_boolean)) { + return JDAC_ERR_SCHEMA_ERROR; + } + + int arraylen = json_object_array_length(jobj); + + int match_count = 0; + for (int i = 0; i < arraylen; i++) { + json_object *iobj = json_object_array_get_idx(jobj, i); + err = _jdac_validate_instance(iobj, jcontains); + if (err == JDAC_ERR_VALID) { + match_count++; + } else if (err == JDAC_ERR_SCHEMA_ERROR) { + return JDAC_ERR_SCHEMA_ERROR; + } + } + + json_object *jmaxcontains = json_object_object_get(jschema, "maxContains"); + if (jmaxcontains) { + int max = json_object_get_int(jmaxcontains); + if (match_count > max) { + return JDAC_ERR_INVALID_CONTAINS; + } + } + + json_object *jmincontains = json_object_object_get(jschema, "minContains"); + if (jmincontains) { + int min = json_object_get_int(jmincontains); + if (min == 0) { + return JDAC_ERR_VALID; + } else if (match_count < min) { + return JDAC_ERR_INVALID_CONTAINS; + } + } + + if (match_count == 0) { + return JDAC_ERR_INVALID_CONTAINS; + } + + return JDAC_ERR_VALID; +} diff --git a/srcs/libs/json/json_schema/download.c b/srcs/libs/json/json_schema/download.c new file mode 100644 index 0000000..6a16ca9 --- /dev/null +++ b/srcs/libs/json/json_schema/download.c @@ -0,0 +1,55 @@ +#include +#include +#include +#include +#include "jsoncdaccord.h" +#include "internal.h" +#include "optional.h" + +struct curlmemory { + char *response; + size_t size; +}; + +static size_t cb(void *data, size_t size, size_t nmemb, void *userp) { + size_t realsize = size * nmemb; + struct curlmemory *mem = (struct curlmemory *)userp; + + char *ptr = realloc(mem->response, mem->size + realsize + 1); + if (ptr == NULL) { + return 0; /* out of memory! */ + } + + mem->response = ptr; + memcpy(&(mem->response[mem->size]), data, realsize); + mem->size += realsize; + mem->response[mem->size] = 0; + + return realsize; +} + +char *_jdac_download_schema(const char *url) { + struct curlmemory chunk = {0}; + + CURL *curl = curl_easy_init(); + if (curl) { + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, url); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, cb); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk); + res = curl_easy_perform(curl); + if (res != CURLE_OK) { + fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); + } + curl_easy_cleanup(curl); + } + return chunk.response; +} + +const char *_jdac_download_resolve(const char *uri) { + int len = strlen(uri); + if (len > 8 && strncmp(uri, "http", 4) == 0) { + return uri; + } + return NULL; +} diff --git a/srcs/libs/json/json_schema/jdac-cli.c b/srcs/libs/json/json_schema/jdac-cli.c new file mode 100644 index 0000000..d6745fd --- /dev/null +++ b/srcs/libs/json/json_schema/jdac-cli.c @@ -0,0 +1,103 @@ +#include +#include +#include +#include "../include/jsoncdaccord.h" +#include "version_config.h" + +char *json_file=NULL, *schema_file=NULL; + +void usage() +{ + printf("\nUsage:\n"); + printf(" jdac-cli -j jsonfile -s schemafile [options]\n\n"); + printf(" options:\n"); +#ifdef JDAC_REF + printf(" --loadlocal, -l referenced schemas at local filelocationl\n"); +#endif + printf(" --version, -v Show version and supported JSON Schema keywords\n"); + printf(" --help, -h Show this\n\n"); +} + +void freeall() +{ + if (json_file) free(json_file); + if (schema_file) free(schema_file); +} + +int main(int argc, char *argv[]) +{ + + int c; + + while (1) { + int option_index = 0; + static struct option long_options[] = { + {"json", required_argument, 0, 'j'}, + {"schema", required_argument, 0, 's'}, +#ifdef JDAC_REF + {"loadlocal", required_argument, 0, 'l'}, +#endif + {"version", no_argument, 0, 'v'}, + {"help", no_argument, 0, 'h'}, + {0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "j:s:l:v", + long_options, &option_index); + if (c == -1) + break; + + switch (c) { + case 'j': + json_file = strdup(optarg); + break; + case 's': + schema_file = strdup(optarg); + break; +#ifdef JDAC_REF + case 'l': + jdac_ref_set_localpath(optarg); + break; +#endif + case 'v': + printf("jdac-cli (%s version %s)\n", PROJECT_NAME, PROJECT_VER); + printf("supported keywords:\n"); + printf(" - base: %s\n", SUPPORTED_KEYWORDS_BASE); + printf(" - selected: %s\n\n", SUPPORTED_KEYWORDS_OPTIONAL); + freeall(); + return 0; + break; + case 'h': + usage(); + freeall(); + return JDAC_ERR_VALID; + default: + printf("?? getopt returned character code 0%o ??\n", c); + } + } + + if (!json_file) { + printf("No json file given\n"); + } + + if (!schema_file) { + printf("No schema file given"); + } + + if (!json_file || !schema_file) { + usage(); + freeall(); + return JDAC_ERR_WRONG_ARGS; + } + + + printf("validating %s with %s\n", json_file, schema_file); + int err = jdac_validate_file(json_file, schema_file); + if (err==JDAC_ERR_VALID) { + printf("validation ok\n"); + } else { + printf("validate failed. err %d: %s\n", err, jdac_errorstr(err)); + } + freeall(); + return err; +} diff --git a/srcs/libs/json/json_schema/pattern.c b/srcs/libs/json/json_schema/pattern.c new file mode 100644 index 0000000..aee1d39 --- /dev/null +++ b/srcs/libs/json/json_schema/pattern.c @@ -0,0 +1,27 @@ +#include "jsoncdaccord.h" +#include "internal.h" +#include "optional.h" + +int _jdac_check_pattern(json_object *jobj, json_object *jschema) { + json_object *jpat = json_object_object_get(jschema, "pattern"); + if (jpat) { + if (!json_object_is_type(jpat, json_type_string)) { + return JDAC_ERR_SCHEMA_ERROR; + } + + const char *pattern = json_object_get_string(jpat); + const char *istr = json_object_get_string(jobj); + + if (istr) { + int ret = _jdac_match_string_with_regex(pattern, istr); + if (ret == JDAC_REGEX_COMPILE_FAILED) { + return JDAC_ERR_SCHEMA_ERROR; + } else if (ret == JDAC_REGEX_MATCH) { + return JDAC_ERR_VALID; + } else if (ret == JDAC_REGEX_MISMATCH) { + return JDAC_ERR_INVALID_PATTERNMATCH; + } + } + } + return JDAC_ERR_VALID; +} diff --git a/srcs/libs/json/json_schema/patternproperties.c b/srcs/libs/json/json_schema/patternproperties.c new file mode 100644 index 0000000..352331d --- /dev/null +++ b/srcs/libs/json/json_schema/patternproperties.c @@ -0,0 +1,32 @@ +#include "jsoncdaccord.h" +#include "internal.h" +#include "optional.h" + +int _jdac_check_patternproperties(json_object *jobj, json_object *jschema) { + //printf("%s\n", __func__); + // check jobj non object is already checked + + json_object *jpatprops = json_object_object_get(jschema, "patternProperties"); + if (jpatprops) { + if (!json_object_is_type(jpatprops, json_type_object)) { + return JDAC_ERR_SCHEMA_ERROR; + } + + json_object_object_foreach(jpatprops, jprop_key, jprop_val) { + json_object_object_foreach(jobj, jobj_key, jobj_val) { + int ret = _jdac_match_string_with_regex(jprop_key, jobj_key); + if (ret == JDAC_REGEX_COMPILE_FAILED) { + return JDAC_ERR_SCHEMA_ERROR; + } else if (ret == JDAC_REGEX_MATCH) { + int err = _jdac_validate_instance(jobj_val, jprop_val); + if (err) { + return err; + } + } else if (ret == JDAC_REGEX_MISMATCH) { + // + } + } + } + } + return JDAC_ERR_VALID; +} diff --git a/srcs/libs/json/json_schema/propertynames.c b/srcs/libs/json/json_schema/propertynames.c new file mode 100644 index 0000000..13e1182 --- /dev/null +++ b/srcs/libs/json/json_schema/propertynames.c @@ -0,0 +1,26 @@ +#include "jsoncdaccord.h" +#include "internal.h" +#include "optional.h" + +int _jdac_check_propertynames(json_object *jobj, json_object *jschema) { + json_object *jpropnames_schema = json_object_object_get(jschema, "propertyNames"); + if (!jpropnames_schema) { + return JDAC_ERR_VALID; + } + + if (!json_object_is_type(jpropnames_schema, json_type_object) && + !json_object_is_type(jpropnames_schema, json_type_boolean)) { + return JDAC_ERR_SCHEMA_ERROR; + } + + json_object_object_foreach(jobj, jprop_key, jprop_val) { + json_object *jprop = json_object_new_string(jprop_key); + int err = _jdac_validate_instance(jprop, jpropnames_schema); + json_object_put(jprop); + if (err) { + return err; + } + } + + return JDAC_ERR_VALID; +} diff --git a/srcs/libs/json/json_schema/ref.c b/srcs/libs/json/json_schema/ref.c new file mode 100644 index 0000000..370b40e --- /dev/null +++ b/srcs/libs/json/json_schema/ref.c @@ -0,0 +1,88 @@ +#include +#include +#include "jsoncdaccord.h" +#include "internal.h" +#include "optional.h" + +char localpath[256] = {0}; + +int jdac_ref_set_localpath(const char *_localpath) { + strcpy(localpath, _localpath); + return JDAC_ERR_VALID; +} + +const char *_jdac_uri_get_path(const char *uri) { + const char *ptr = uri; + char *schemaseparator = strstr(uri, "://"); + if (!schemaseparator) { + //no schema + + } else { + //has schema + ptr = schemaseparator + 3; + char *path = strstr(ptr, "/"); + if (path) { + return path; + } + } + return NULL; +} + +int _jdac_check_ref(json_object *jobj, json_object *jschema, storage_node *storage_list) { + // #ifdef JDAC_STORE + json_object *jref = json_object_object_get(jschema, "$ref"); + if (jref) { + const char *refstr = json_object_get_string(jref); + printf("ref is %s\n", refstr); + storage_node *rootnode = _jdac_store_get_root_node(storage_list); + // if there is a rootnode id, compare it to the ref + if (rootnode) { + const char *path_ref = _jdac_uri_get_path(refstr); + const char *path_id = _jdac_uri_get_path(rootnode->id); + + if (!path_ref) { + return JDAC_ERR_VALID; + } + + if (!path_id) { + char filepath[512]; + snprintf(filepath, sizeof(filepath) - 1, "%s.%s", localpath, path_ref); + printf("filepath is %s\n", filepath); + json_object *jschemafromfile = json_object_from_file(filepath); + if (!jschemafromfile) { + return JDAC_ERR_JSON_NOT_FOUND; + } + int err = _jdac_validate_instance(jobj, jschemafromfile); + json_object_put(jschemafromfile); + return err; + } + + if (strcmp(path_ref, path_id) != 0 && strlen(localpath) > 0) { + printf("yep\n"); + char filepath[512]; + snprintf(filepath, sizeof(filepath) - 1, "%s.%s", localpath, path_ref); + printf("filepath is %s\n", filepath); + json_object *jschemafromfile = json_object_from_file(filepath); + if (!jschemafromfile) { + return JDAC_ERR_JSON_NOT_FOUND; + } + int err = _jdac_validate_instance(jobj, jschemafromfile); + json_object_put(jschemafromfile); + return err; + } else if (strcmp(path_ref, path_id) == 0) { + int err = _jdac_validate_instance(jobj, rootnode->json_schema_ptr); + return err; + } + } + + // if (refstr) { + // json_object *jschema_from_resolved_uri = _jdac_store_resolve(storage_list, refstr); + // if (!jschema_from_resolved_uri) + // return JDAC_ERR_INVALID_REF; + // int err = _jdac_validate_instance(jobj, jschema_from_resolved_uri); + // return err; + // } + } + // #endif + return JDAC_ERR_VALID; +} \ No newline at end of file diff --git a/srcs/libs/json/json_schema/regex_match.c b/srcs/libs/json/json_schema/regex_match.c new file mode 100644 index 0000000..c8e116b --- /dev/null +++ b/srcs/libs/json/json_schema/regex_match.c @@ -0,0 +1,19 @@ +#include +#include +#include +#include "jsoncdaccord.h" + +int _jdac_match_string_with_regex(const char *regex_pattern, const char *value) { + regex_t regex; + int reti = regcomp(®ex, regex_pattern, REG_EXTENDED); + if (reti) { + fprintf(stderr, "Could not compile regex\n"); + return JDAC_REGEX_COMPILE_FAILED; + } + reti = regexec(®ex, value, 0, NULL, 0); + regfree(®ex); + if (reti == 0) { + return JDAC_REGEX_MATCH; + } + return JDAC_REGEX_MISMATCH; +} diff --git a/srcs/libs/json/json_schema/store.c b/srcs/libs/json/json_schema/store.c new file mode 100644 index 0000000..5810f7a --- /dev/null +++ b/srcs/libs/json/json_schema/store.c @@ -0,0 +1,114 @@ +#include +#include +#include "jsoncdaccord.h" +#include "optional.h" + +void _jdac_store_append(storage_node **head, storage_node *ref) { + storage_node *new_node = malloc(sizeof(storage_node)); + memcpy(new_node, ref, sizeof(storage_node)); + new_node->next = *head; + *head = new_node; +} + +void _jdac_store_free(storage_node **head) { + while (*head) { + // if ((*head)->is_root==1 && (*head)->json_instance_ptr!=NULL) + // json_object_put((*head)->json_instance_ptr); + storage_node *el = *head; + *head = (*head)->next; + free(el); + } + *head = NULL; +} + +int _jdac_store_traverse_json(storage_node **head, json_object *jschema, char *pathbuffer) { + char pathbuf[256]; + pathbuf[0] = 0; + storage_node node = {0}; + + if (pathbuffer == NULL) { + strcpy(pathbuf, "#/"); + node.is_root = 1; + } else { + strcpy(pathbuf, pathbuffer); + node.is_root = 0; + } + node.json_instance_ptr = jschema; + + json_object *jid = json_object_object_get(jschema, "$id"); + json_object *janchor = json_object_object_get(jschema, "$anchor"); + json_object *jdynamicanchor = json_object_object_get(jschema, "$dynamicAnchor"); + + if (jid) { + strcpy(node.id, json_object_get_string(jid)); + } + + if (janchor) { + strcpy(node.anchor, json_object_get_string(janchor)); + } + + if (jdynamicanchor) { + strcpy(node.dynamicAnchor, json_object_get_string(jdynamicanchor)); + } + + // if (jid || janchor || jdynamicanchor || node.is_root==1) { + strcpy(node.JSONPtrURI, pathbuf); + node.json_schema_ptr = jschema; + _jdac_store_append(head, &node); + // } + + json_object_object_foreach(jschema, jkey, jval) { + if (json_object_is_type(jval, json_type_object)) { + + if (strcmp(jkey, "const") == 0) { + continue; + } + + if (pathbuffer == NULL) { + sprintf(pathbuf, "#/%s", jkey); + } else { + sprintf(pathbuf, "%s/%s", pathbuffer, jkey); + } + //printf("%s\n", pathbuf); + _jdac_store_traverse_json(head, jval, pathbuf); + } + } + return JDAC_ERR_VALID; +} + +void _jdac_store_print(storage_node *head) { + storage_node *list = head; + printf("%-*s %-*s %-*s %-*s\n", 32, "JSONPtr", 16, "anchor", 16, "dynamicAnchor", 32, "id"); + while (list) { + printf("%-*s %-*s %-*s %-*s\n", 32, list->JSONPtrURI, 16, list->anchor, 16, list->dynamicAnchor, 32, list->id); + list = list->next; + } +} + +storage_node *_jdac_store_get_root_node(storage_node *head) { + // spool to start of list + storage_node *list = head; + //printf("%-*s %-*s %-*s %-*s\n", 32, "JSONPtr", 16, "anchor", 16, "dynamicAnchor", 32, "id"); + while (list) { + //printf("%-*s %-*s %-*s %-*s\n", 32, list->JSONPtrURI, 16, list->anchor, 16, list->dynamicAnchor, 32, list->id); + if (list->next == NULL) { + return list; + } + list = list->next; + } + return NULL; +} + +json_object *_jdac_store_resolve(storage_node *list, const char *uri) { + while (list) { + if (strcmp(list->id, uri) == 0) { + return list->json_instance_ptr; + } + if (strcmp(list->JSONPtrURI, uri) == 0) { + return list->json_instance_ptr; + } + list = list->next; + } + + return NULL; +} \ No newline at end of file diff --git a/srcs/libs/json/json_schema/subschemalogic.c b/srcs/libs/json/json_schema/subschemalogic.c new file mode 100644 index 0000000..d0dbc67 --- /dev/null +++ b/srcs/libs/json/json_schema/subschemalogic.c @@ -0,0 +1,116 @@ +#include "jsoncdaccord.h" +#include "internal.h" +#include "optional.h" + +enum subschematype { + JDAC_ALLOF = 0, + JDAC_ANYOF, + JDAC_ONEOF +}; + +// shall return valid or invalid based on subschema type +int _jdac_test_subschema_array(json_object *jobj, json_object *jsubschema_array, enum subschematype type) { + if (jsubschema_array == NULL) { + return JDAC_ERR_VALID; + } + + // MUST be a non-empty array + if (!json_object_is_type(jsubschema_array, json_type_array)) { + return JDAC_ERR_SCHEMA_ERROR; + } + + int arraylen = (int)json_object_array_length(jsubschema_array); + if (arraylen == 0) { + return JDAC_ERR_SCHEMA_ERROR; + } + + int number_of_valid_schemas = 0; + + for (int i = 0; i < arraylen; i++) { + json_object *jsubschema = json_object_array_get_idx(jsubschema_array, i); + if (!json_object_is_type(jsubschema, json_type_object) && !json_object_is_type(jsubschema, json_type_boolean)) { + return JDAC_ERR_SCHEMA_ERROR; + } + + int err = _jdac_validate_instance(jobj, jsubschema); + + if (err == JDAC_ERR_VALID) { + number_of_valid_schemas++; + } else if (err == JDAC_ERR_SCHEMA_ERROR) { + return JDAC_ERR_SCHEMA_ERROR; + } else { + // continue + } + } + + if (type == JDAC_ALLOF) { + if (number_of_valid_schemas == arraylen) { + return JDAC_ERR_VALID; + } + } else if (type == JDAC_ANYOF) { + if (number_of_valid_schemas > 0) { + return JDAC_ERR_VALID; + } + } else if (type == JDAC_ONEOF) { + if (number_of_valid_schemas == 1) { + return JDAC_ERR_VALID; + } + } + return JDAC_ERR_INVALID_SUBSCHEMALOGIC; +} + +int _jdac_check_subschemalogic(json_object *jobj, json_object *jschema) { + int err; + json_object *jarray; + + jarray = json_object_object_get(jschema, "allOf"); + err = _jdac_test_subschema_array(jobj, jarray, JDAC_ALLOF); + if (err) { + return JDAC_ERR_INVALID_SUBSCHEMALOGIC; + } + + jarray = json_object_object_get(jschema, "anyOf"); + err = _jdac_test_subschema_array(jobj, jarray, JDAC_ANYOF); + if (err) { + return JDAC_ERR_INVALID_SUBSCHEMALOGIC; + } + + jarray = json_object_object_get(jschema, "oneOf"); + err = _jdac_test_subschema_array(jobj, jarray, JDAC_ONEOF); + if (err) { + return JDAC_ERR_INVALID_SUBSCHEMALOGIC; + } + + json_object *jnot = json_object_object_get(jschema, "not"); + if (jnot) { + // "not" is special, and MUST be a json object + if (json_object_is_type(jnot, json_type_object) || json_object_is_type(jnot, json_type_boolean)) { + err = _jdac_validate_instance(jobj, jnot); + if (err == JDAC_ERR_VALID) { + return JDAC_ERR_INVALID_SUBSCHEMALOGIC; + } else if (err == JDAC_ERR_SCHEMA_ERROR) { + return JDAC_ERR_SCHEMA_ERROR; + } else { + return JDAC_ERR_VALID; + } + } else { + return JDAC_ERR_SCHEMA_ERROR; + } + } + + json_object *if_schema = json_object_object_get(jschema, "if"); + json_object *then_schema = json_object_object_get(jschema, "then"); + json_object *else_schema = json_object_object_get(jschema, "else"); + if (if_schema) { + err = _jdac_validate_instance(jobj, if_schema); + if (err == JDAC_ERR_VALID && then_schema) { + err = _jdac_validate_instance(jobj, then_schema); + return err; + } else if (err != JDAC_ERR_VALID && else_schema) { + err = _jdac_validate_instance(jobj, else_schema); + return err; + } + } + + return JDAC_ERR_VALID; +} diff --git a/srcs/libs/json/json_schema/validate.c b/srcs/libs/json/json_schema/validate.c new file mode 100644 index 0000000..55f5d23 --- /dev/null +++ b/srcs/libs/json/json_schema/validate.c @@ -0,0 +1,648 @@ +#include +#include +#include +#include + +#include "jsoncdaccord.h" +#include "internal.h" +#include "optional.h" + +json_object *json = NULL; +json_object *schema = NULL; +json_object *defs = NULL; + +#ifdef JDAC_STORE +static storage_node *storagelist_head = NULL; +#endif + +static char *jdacerrstr[JDAC_ERR_MAX] = {"VALID", + "GENERAL ERROR", + "JSON FILE NOT FOUND", + "SCHEMA FILE NOT FOUND", + "WRONG ARGUEMNTS GIVEN", + "SCHEMA ERROR", + "INVALID", + "INVALID TYPE", + "INVALID REQUIRED", + "INVALID SUBSCHEMA LOGIC (allOf, anyOf, oneOf, or not)", + "INVALID CONST", + "INVALID ENUMS", + "INVALID STRING LENGTH", + "INVALID UNIQUE ITEMS", + "INVALID UNIQUE CONTAINS", + "INVALID PREFIXITEMS", + "INVALID ITEMS", + "INVALID ARRAY LENGTH", + "INVALID NUMBER", + "INVALID REFERENCE", + "PATTERN NO MATCH", + "REGEX MISMATCH", + "REGEX MATCH", + "REGEX COMPILE FAILED"}; + +const char *jdac_errorstr(unsigned int jdac_errors) { + if (jdac_errors < JDAC_ERR_MAX) { + return jdacerrstr[jdac_errors]; + } + return NULL; +} + +int _jdac_load(const char *jsonfile, const char *jsonschema) { + json = json_object_from_file(jsonfile); + if (json == NULL) { + return JDAC_ERR_JSON_NOT_FOUND; + } + + schema = json_object_from_file(jsonschema); + if (schema == NULL) { + json_object_put(json); + return JDAC_ERR_SCHEMA_NOT_FOUND; + } + + return JDAC_ERR_VALID; +} + +int __jdac_inspect_type(json_object *jobj, const char *type) { + if (strcmp(type, "object") == 0) { + if (json_object_is_type(jobj, json_type_object)) { + return JDAC_ERR_VALID; + } + } else if (strcmp(type, "array") == 0) { + if (json_object_is_type(jobj, json_type_array)) { + return JDAC_ERR_VALID; + } + } else if (strcmp(type, "string") == 0) { + if (json_object_is_type(jobj, json_type_string)) { + return JDAC_ERR_VALID; + } + } else if (strcmp(type, "integer") == 0) { + if (json_object_is_type(jobj, json_type_int)) { + return JDAC_ERR_VALID; + } + if (json_object_is_type(jobj, json_type_double)) { + double value = json_object_get_double(jobj); + if (value == round(value)) { // "zero fractional part is an integer" + return JDAC_ERR_VALID; + } + } + } else if (strcmp(type, "double") == 0) { + if (json_object_is_type(jobj, json_type_double)) { + return JDAC_ERR_VALID; + } + } else if (strcmp(type, "number") == 0) { + if (json_object_is_type(jobj, json_type_double) || json_object_is_type(jobj, json_type_int)) { + return JDAC_ERR_VALID; + } + } else if (strcmp(type, "boolean") == 0) { + if (json_object_is_type(jobj, json_type_boolean)) { + return JDAC_ERR_VALID; + } + } else if (strcmp(type, "null") == 0) { + if (json_object_is_type(jobj, json_type_null)) { + return JDAC_ERR_VALID; + } + } else { + printf("WARN unknown type in check type %s\n", type); + return JDAC_ERR_SCHEMA_ERROR; + } + return JDAC_ERR_INVALID_TYPE; +} + +int _jdac_check_type(json_object *jobj, json_object *jschema) { + json_object *jtype = json_object_object_get(jschema, "type"); + + if (jtype == NULL) { + return JDAC_ERR_VALID; + } else if (json_object_is_type(jtype, json_type_string)) { + const char *type = json_object_get_string(jtype); + return __jdac_inspect_type(jobj, type); + } else if (json_object_is_type(jtype, json_type_array)) { + int arraylen = json_object_array_length(jtype); + for (int i = 0; i < arraylen; i++) { + json_object *iobj = json_object_array_get_idx(jtype, i); + if (!json_object_is_type(iobj, json_type_string)) { + return JDAC_ERR_SCHEMA_ERROR; + } + const char *type = json_object_get_string(iobj); + int err = __jdac_inspect_type(jobj, type); + if (err == JDAC_ERR_VALID) { + return JDAC_ERR_VALID; + } + } + return JDAC_ERR_INVALID_TYPE; + } else { + return JDAC_ERR_SCHEMA_ERROR; + } +} + +int _jdac_check_required(json_object *jobj, json_object *jschema) { + //printf("%s\n%s\n", __func__, json_object_to_json_string(jobj)); + json_object *jarray = json_object_object_get(jschema, "required"); + int missing_required_key = 0; + if (jarray) { + int arraylen = json_object_array_length(jarray); + for (int i = 0; i < arraylen; i++) { + json_object *iobj = json_object_array_get_idx(jarray, i); + const char *key = json_object_get_string(iobj); + if (key) { + //printf("%s\n", key); + // use json_object_object_get_ex becuase of json_type_null types + json_object *required_object = NULL; + int err = json_object_object_get_ex(jobj, key, &required_object); + if (err == 0) { + printf("required key missing: %s\n", key); + missing_required_key = 1; + } + } + } + } + if (missing_required_key) { + return JDAC_ERR_INVALID_REQUIRED; + } else { + return JDAC_ERR_VALID; + } +} + +int _jdac_check_properties(json_object *jobj, json_object *jschema) { + // printf("%s\n", __func__); + + json_object *jprops = json_object_object_get(jschema, "properties"); + if (jprops) { + json_object_object_foreach(jprops, jprop_key, jprop_val) { + // printf("key of prop is %s\n", jprop_key); + json_object *iobj = json_object_object_get(jobj, jprop_key); + //printf("iobj %s type %d\nkey %s\nval %s\n", json_object_get_string(iobj), json_object_get_type(iobj), jprop_key, json_object_get_string(jprop_val)); + if (iobj) { + int err = _jdac_validate_instance(iobj, jprop_val); + if (err) { + return err; + } + } + } + } + return JDAC_ERR_VALID; +} + +int _jdac_check_prefixItems_and_items(json_object *jobj, json_object *jschema) { + json_object *jprefixitems = json_object_object_get(jschema, "prefixItems"); + json_object *jitems = json_object_object_get(jschema, "items"); + + int jobj_arraylen = json_object_array_length(jobj); + int prefixitems_arraylen = 0; + + if (jprefixitems) { + + if (!json_object_is_type(jprefixitems, json_type_array)) { + return JDAC_ERR_SCHEMA_ERROR; + } + + prefixitems_arraylen = json_object_array_length(jprefixitems); + + for (int i = 0; i < jobj_arraylen && i < prefixitems_arraylen; i++) { + //printf("i=%d prefixitems\n", i); + json_object *iobj = json_object_array_get_idx(jobj, i); + json_object *ischema = json_object_array_get_idx(jprefixitems, i); + int err = _jdac_validate_instance(iobj, ischema); + if (err) { + return JDAC_ERR_INVALID_PREFIXITEMS; + } + } + } + + if (jitems) { + if (!json_object_is_type(jitems, json_type_object) && !json_object_is_type(jitems, json_type_boolean)) { + return JDAC_ERR_SCHEMA_ERROR; + } + + for (int i = prefixitems_arraylen; i < jobj_arraylen; i++) { + //printf("i=%d items\n", i); + json_object *iobj = json_object_array_get_idx(jobj, i); + int err = _jdac_validate_instance(iobj, jitems); + if (err) { + return JDAC_ERR_INVALID_ITEMS; + } + } + } + return JDAC_ERR_VALID; +} + +json_object *_jdac_get_defs_from_ref(json_object *ref) { + char key[128]; + if (!json_object_is_type(ref, json_type_string)) { + return NULL; + } + + const char *refstr = json_object_get_string(ref); + if (sscanf(refstr, "#/$defs/%s", key) == 1) { + return json_object_object_get(defs, key); + } + return NULL; +} + +int _jdac_value_is_equal(json_object *jobj1, json_object *jobj2) { + if (json_object_equal(jobj1, jobj2)) { + return JDAC_ERR_VALID; + } + + if (json_object_is_type(jobj1, json_type_double) && json_object_is_type(jobj2, json_type_int)) { + double value = json_object_get_double(jobj1); + double value2 = json_object_get_int64(jobj2); + if (value == round(value) && value == value2) { + return JDAC_ERR_VALID; + } + } + + if (json_object_is_type(jobj1, json_type_int) && json_object_is_type(jobj2, json_type_double)) { + double value = json_object_get_double(jobj2); + double value2 = json_object_get_int64(jobj1); + if (value == round(value) && value == value2) { + return JDAC_ERR_VALID; + } + } + + return JDAC_ERR_INVALID; +} + +int _jdac_check_const(json_object *jobj, json_object *jschema) { + json_object *jconst; + int err = json_object_object_get_ex(jschema, "const", &jconst); + if (err == 0) { + return JDAC_ERR_VALID; + } + + err = _jdac_value_is_equal(jobj, jconst); + if (err == JDAC_ERR_VALID) { + return JDAC_ERR_VALID; + } + + return JDAC_ERR_INVALID_CONST; +} + +int _jdac_check_enums(json_object *jobj, json_object *jschema) { + json_object *jenum_array = json_object_object_get(jschema, "enum"); + + if (!jenum_array) { + return JDAC_ERR_VALID; + } + + if (!json_object_is_type(jenum_array, json_type_array)) { + return JDAC_ERR_SCHEMA_ERROR; + } + + int arraylen = json_object_array_length(jenum_array); + for (int i = 0; i < arraylen; i++) { + json_object *ienum = json_object_array_get_idx(jenum_array, i); + + int err = _jdac_value_is_equal(jobj, ienum); + if (err == JDAC_ERR_VALID) { + return JDAC_ERR_VALID; + } + } + printf("ERROR: enum check failed (%s not in enum)\n", json_object_to_json_string(jobj)); + + return JDAC_ERR_INVALID_ENUMS; +} + +int _jdac_check_uniqueItems(json_object *jobj, json_object *jschema) { + json_object *juniq = json_object_object_get(jschema, "uniqueItems"); + if (juniq) { + if (!json_object_is_type(juniq, json_type_boolean)) { + return JDAC_ERR_SCHEMA_ERROR; + } + + if (json_object_get_boolean(juniq) == 0) { + return JDAC_ERR_VALID; + } + + int arraylen = json_object_array_length(jobj); + for (int i = 0; i < arraylen - 1; i++) { + json_object *iobj = json_object_array_get_idx(jobj, i); + for (int j = i + 1; j < arraylen; j++) { + json_object *uobj = json_object_array_get_idx(jobj, j); + if (json_object_equal(iobj, uobj) == 1) { + return JDAC_ERR_INVALID_UNIQUEITEMS; + } + } + } + } + return JDAC_ERR_VALID; +} + +int _jdac_check_maxmin_items(json_object *jobj, json_object *jschema) { + int err = JDAC_ERR_VALID; + json_object *jmax = json_object_object_get(jschema, "maxItems"); + json_object *jmin = json_object_object_get(jschema, "minItems"); + int arraylen = json_object_array_length(jobj); + + if (jmax) { + if (json_object_is_type(jmax, json_type_int) || json_object_is_type(jmax, json_type_double)) { + int maxitems = json_object_get_double(jmax); + if (arraylen > maxitems) { + err = JDAC_ERR_INVALID_ARRAYLEN; + } + } + } + + if (jmin) { + if (json_object_is_type(jmin, json_type_int) || json_object_is_type(jmin, json_type_double)) { + int minitems = json_object_get_double(jmin); + if (arraylen < minitems) { + err = JDAC_ERR_INVALID_ARRAYLEN; + } + } + } + + if (err) { + printf("ERROR: failed at maxItems or minItems check\n"); + } + return err; +} + +int _jdac_validate_array(json_object *jobj, json_object *jschema) { + int err; + + err = _jdac_check_prefixItems_and_items(jobj, jschema); + if (err) { + return err; + } + + err = _jdac_check_uniqueItems(jobj, jschema); + if (err) { + return err; + } + + err = _jdac_check_maxmin_items(jobj, jschema); + if (err) { + return err; + } + +#ifdef JDAC_CONTAINS + err = _jdac_check_contains_and_minmaxcontains(jobj, jschema); + if (err) { + return err; + } +#endif + + return JDAC_ERR_VALID; +} + +int _jdac_validate_object(json_object *jobj, json_object *jschema) { + int err; + if (defs == NULL) { + defs = json_object_object_get(jschema, "$defs"); + } + + err = _jdac_check_required(jobj, jschema); + if (err) { + return err; + } + + err = _jdac_check_properties(jobj, jschema); + if (err) { + return err; + } + +#ifdef JDAC_PROPERTYNAMES + err = _jdac_check_propertynames(jobj, jschema); + if (err) { + return err; + } +#endif + +#ifdef JDAC_PATTERNPROPERTIES + err = _jdac_check_patternproperties(jobj, jschema); + if (err) { + return err; + } +#endif + +#ifdef JDAC_ADDITIONALPROPERTIES + err = _jdac_check_additionalproperties(jobj, jschema); + if (err) { + return err; + } +#endif + + return JDAC_ERR_VALID; +} + +int utf8_length(const char *str) { + const char *pointer = str; + int len = 0; + while (pointer[0]) { + if ((pointer[0] & 0xC0) != 0x80) { + len++; + } + pointer++; + } + return len; +} + +int _jdac_validate_string(json_object *jobj, json_object *jschema) { + const char *str = json_object_get_string(jobj); + //printf("strlen of %s %ld %d %d\n", str, strlen(str), json_object_get_string_len(jobj), utf8_length(str)); + json_object *jminlen = json_object_object_get(jschema, "minLength"); + if (jminlen) { + int minlen = json_object_get_int64(jminlen); + if (utf8_length(str) < minlen) { + return JDAC_ERR_INVALID_STRLEN; + } + } + json_object *jmaxlen = json_object_object_get(jschema, "maxLength"); + if (jmaxlen) { + int maxlen = json_object_get_int64(jmaxlen); + if (utf8_length(str) > maxlen) { + return JDAC_ERR_INVALID_STRLEN; + } + } + + int err = _jdac_check_enums(jobj, jschema); + if (err) { + return err; + } + +#ifdef JDAC_PATTERN + err = _jdac_check_pattern(jobj, jschema); + if (err) { + return err; + } +#endif + + return JDAC_ERR_VALID; +} + +int _jdac_validate_integer(json_object *jobj, json_object *jschema) { + double value = (double)json_object_get_int64(jobj); + int err = _jdac_validate_number(jobj, jschema, value); + return err; +} + +int _jdac_validate_double(json_object *jobj, json_object *jschema) { + double value = json_object_get_double(jobj); + int err = _jdac_validate_number(jobj, jschema, value); + return err; +} + +int _jdac_validate_number(json_object *jobj, json_object *jschema, double value) { + + json_object *jmult = json_object_object_get(jschema, "multipleOf"); + if (jmult) { + double multipland = (double)json_object_get_double(jmult); + if (multipland == 0.0) { + return JDAC_ERR_SCHEMA_ERROR; + } + double divided = value / multipland; + if (isinf(divided) != 0) { + return JDAC_ERR_INVALID_NUMBER; + } + if (divided != round(divided)) { + return JDAC_ERR_INVALID_NUMBER; + } + } + + json_object *jmin = json_object_object_get(jschema, "minimum"); + if (jmin) { + double min = (double)json_object_get_double(jmin); + if (value < min) { + return JDAC_ERR_INVALID_NUMBER; + } + } + + json_object *jexclmin = json_object_object_get(jschema, "exclusiveMinimum"); + if (jexclmin) { + double min = (double)json_object_get_double(jexclmin); + if (value <= min) { + return JDAC_ERR_INVALID_NUMBER; + } + } + + json_object *jmax = json_object_object_get(jschema, "maximum"); + if (jmax) { + double max = (double)json_object_get_double(jmax); + if (value > max) { + return JDAC_ERR_INVALID_NUMBER; + } + } + + json_object *jexclmax = json_object_object_get(jschema, "exclusiveMaximum"); + if (jexclmax) { + double max = (double)json_object_get_double(jexclmax); + if (value >= max) { + return JDAC_ERR_INVALID_NUMBER; + } + } + + return JDAC_ERR_VALID; +} + +int _jdac_validate_boolean(json_object *jobj, json_object *jschema) { + // printf("%s\n", __func__); + return JDAC_ERR_VALID; +} + +int _jdac_validate_instance(json_object *jobj, json_object *jschema) { + int err; + // printf("--validate instance--\n"); + // printf("%s\n", json_object_get_string(jobj)); + // printf("%s\n", json_object_get_string(jschema)); + +#ifdef JDAC_REF + err = _jdac_check_ref(jobj, jschema, storagelist_head); + if (err) { + return err; + } +#endif + + // check if jschema is a bool, true or false + if (json_object_is_type(jschema, json_type_boolean)) { + json_bool value = json_object_get_boolean(jschema); + if (value == 0) { + return JDAC_ERR_INVALID; + } + if (value == 1) { + return JDAC_ERR_VALID; + } + } + + err = _jdac_check_type(jobj, jschema); + if (err) { + return err; + } + + err = _jdac_check_const(jobj, jschema); + if (err) { + return err; + } + + err = _jdac_check_enums(jobj, jschema); + if (err) { + return err; + } + + // if (!json_object_is_type(jobj, json_type_null)) + // printf("%s\n", json_object_get_string(jobj)); + // else + // printf("jobj was null\n"); + // if (!json_object_is_type(jschema, json_type_null)) + // printf("%s\n", json_object_get_string(jschema)); + // else + // printf("jschema was null\n"); + +#ifdef JDAC_SUBSCHEMALOGIC + err = _jdac_check_subschemalogic(jobj, jschema); + if (err) { + return err; + } +#endif + + json_type type = json_object_get_type(jobj); + + if (type == json_type_object) { + return _jdac_validate_object(jobj, jschema); + } else if (type == json_type_array) { + return _jdac_validate_array(jobj, jschema); + } else if (type == json_type_string) { + return _jdac_validate_string(jobj, jschema); + } else if (type == json_type_boolean) { + return _jdac_validate_boolean(jobj, jschema); + } else if (type == json_type_int) { + return _jdac_validate_integer(jobj, jschema); + } else if (type == json_type_double) { + return _jdac_validate_double(jobj, jschema); + } else if (type == json_type_null) { + return JDAC_ERR_VALID; + } else { + printf("WARN: type %d not handled\n", type); + } + + return JDAC_ERR_VALID; +} + +int jdac_validate(json_object *jobj, json_object *jschema) { +#ifdef JDAC_STORE + _jdac_store_traverse_json(&storagelist_head, jschema, NULL); + _jdac_store_print(storagelist_head); +#endif + + int err = _jdac_validate_instance(jobj, jschema); +#ifdef JDAC_STORE + _jdac_store_free(&storagelist_head); +#endif + return err; +} + +int jdac_validate_file(const char *jsonfile, const char *jsonschemafile) { + int err = _jdac_load(jsonfile, jsonschemafile); + if (err) { + return err; + } + + err = jdac_validate(json, schema); + + json_object_put(json); + json_object_put(schema); + json = NULL; + schema = NULL; + defs = NULL; + return err; +} diff --git a/srcs/libs/protocol/protocol.c b/srcs/libs/protocol/protocol.c index f8a78c0..84ed316 100644 --- a/srcs/libs/protocol/protocol.c +++ b/srcs/libs/protocol/protocol.c @@ -10,6 +10,7 @@ #include "crypto.h" #include "user_errno.h" #include "zlog_module.h" +#include "json_schema/jsoncdaccord.h" #define CURRENT_PROTOCOL_VERSION (1) @@ -21,14 +22,85 @@ typedef struct { cJSON *msgContend; } PROTOCOL_WARP, *PPROTOCOL_WARP; -const char *proto_decode_context(const char *pString, unsigned int *pVer, unsigned long long *pTm) { +#ifdef JSON_SCHEMA_ON +typedef struct { + const char *pSchJson; + const char *pErrMsg; +} JSON_SCHEMA_CTX, *PJSON_SCHEMA_CTX; + +static JSON_SCHEMA_CTX g_json_sch[] = { + {"{\"type\":\"object\",\"required\":[\"ver\"]}", "Missing required field [ver]" }, + {"{\"type\":\"object\",\"required\":[\"cryptoType\"]}", "Missing required field [cryptoType]"}, + {"{\"type\":\"object\",\"required\":[\"timeStamp\"]}", "Missing required field [timeStamp]" }, + {"{\"type\":\"object\",\"required\":[\"msgContent\"]}", "Missing required field [msgContent]"}, +}; + +const char *proto_schema_validation(const char *pJsonStr) { + int i; + + json_object *pJs = json_tokener_parse(pJsonStr); + + if (!pJs) { + cJSON *pRspRoot = cJSON_CreateObject(); + cJSON_AddNumberToObject(pRspRoot, "status", ERR_JSON_PRASE_OBJ); + cJSON_AddStringToObject(pRspRoot, "message", getErrorEnumDesc(ERR_JSON_PRASE_OBJ)); + return proto_create_new(pRspRoot, 200); + } + + for (i = 0; i < ARRAY_SIZE(g_json_sch); i++) { + json_object *pSc = json_tokener_parse(g_json_sch[i].pSchJson); + + if (!pSc) { + LOG_MOD(error, ZLOG_MOD_PROTO, "Json schema format error: [%s]\n", g_json_sch[i].pSchJson); + continue; + } + + if (jdac_validate(pJs, pSc) != JDAC_ERR_VALID) { + cJSON *pRspRoot = cJSON_CreateObject(); + cJSON_AddNumberToObject(pRspRoot, "status", ERR_JSON_VALID_SCH); + cJSON_AddStringToObject(pRspRoot, "message", getErrorEnumDesc(ERR_JSON_VALID_SCH)); + cJSON_AddStringToObject(pRspRoot, "details", g_json_sch[i].pErrMsg); + json_object_put(pSc); + return proto_create_new(pRspRoot, 200); + } + + json_object_put(pSc); + } + + json_object_put(pJs); + return NULL; +} +#endif + +const char *proto_decode_context(const char *pString, unsigned int *pVer, unsigned long long *pTm, int *pErrCode) { cJSON *pMsgCtx; unsigned char *pBase64; int decodeSize; unsigned int outSize = 0; char *pMsgContent = NULL; - cJSON *pRoot = cJSON_Parse(pString); + cJSON *pRoot; +#ifdef JSON_SCHEMA_ON + const char *pSchJson; +#endif + if (pErrCode == NULL) { + return NULL; + } + + if (pString == NULL || strlen(pString) == 0) { + *pErrCode = ERR_INPUT_PARAMS; + return NULL; + } + +#ifdef JSON_SCHEMA_ON + pSchJson = proto_schema_validation(pString); + if (pSchJson != NULL && strlen(pSchJson) > 0) { + *pErrCode = ERR_JSON_VALID_SCH; + return pSchJson; + } +#endif + + pRoot = cJSON_Parse(pString); if (!pRoot) { return NULL; } diff --git a/srcs/opendhcp183/query.cpp b/srcs/opendhcp183/query.cpp index df6fe05..a947eba 100644 --- a/srcs/opendhcp183/query.cpp +++ b/srcs/opendhcp183/query.cpp @@ -47,6 +47,7 @@ static int dhcp_get_user_info(const char **pRsp, const char *pRequest) { char logBuff[512]; const char *pStrContent; int k; + int errCode; dhcpMap::iterator p; if (pRequest == nullptr || strlen(pRequest) == 0) { @@ -55,7 +56,7 @@ static int dhcp_get_user_info(const char **pRsp, const char *pRequest) { return ERR_INPUT_PARAMS; } - pStrContent = proto_decode_context(pRequest, nullptr, nullptr); + pStrContent = proto_decode_context(pRequest, nullptr, nullptr, &errCode); if (pStrContent == nullptr) { sprintf(logBuff, "Requeset Json error %s", pRequest); @@ -63,6 +64,13 @@ static int dhcp_get_user_info(const char **pRsp, const char *pRequest) { return ERR_PROTO_DECODE; } +#ifdef JSON_SCHEMA_ON + if (errCode == ERR_JSON_VALID_SCH) { + *pRsp = pStrContent; + return ERR_SUCCESS; + } +#endif + cJSON *pRoot = cJSON_Parse(pStrContent); free((void *)pStrContent); @@ -318,6 +326,7 @@ static int add_dhcpd_rangeset(const char **pRsp, const char *pRequest) { OBJ_DHCP_RNG range; cJSON *pRspRoot; cJSON *pExpandArray; + int errCode; if (pRequest == nullptr || strlen(pRequest) == 0) { sprintf(logBuff, "Requeset Json"); @@ -325,7 +334,7 @@ static int add_dhcpd_rangeset(const char **pRsp, const char *pRequest) { return ERR_INPUT_PARAMS; } - pStrContent = proto_decode_context(pRequest, nullptr, nullptr); + pStrContent = proto_decode_context(pRequest, nullptr, nullptr, &errCode); if (pStrContent == nullptr) { sprintf(logBuff, "Requeset Json error %s", pRequest); @@ -333,6 +342,13 @@ static int add_dhcpd_rangeset(const char **pRsp, const char *pRequest) { return ERR_PROTO_DECODE; } +#ifdef JSON_SCHEMA_ON + if (errCode == ERR_JSON_VALID_SCH) { + *pRsp = pStrContent; + return ERR_SUCCESS; + } +#endif + cJSON *pRoot = cJSON_Parse(pStrContent); free((void *)pStrContent); @@ -417,6 +433,7 @@ static int delete_dhcpd_rangeset(const char **pRsp, const char *pRequest) { data13 dhcpRanges[MAX_DHCP_RANGES]; PHASH_MAP delMap = nullptr; int resCount = 0; + int errCode; if (pRequest == nullptr || strlen(pRequest) == 0) { sprintf(logBuff, "Requeset Json"); @@ -424,13 +441,20 @@ static int delete_dhcpd_rangeset(const char **pRsp, const char *pRequest) { return ERR_INPUT_PARAMS; } - pStrContent = proto_decode_context(pRequest, nullptr, nullptr); + pStrContent = proto_decode_context(pRequest, nullptr, nullptr, &errCode); if (pStrContent == nullptr) { sprintf(logBuff, "Requeset Json error %s", pRequest); logDHCPMess(logBuff, 1); return ERR_PROTO_DECODE; } +#ifdef JSON_SCHEMA_ON + if (errCode == ERR_JSON_VALID_SCH) { + *pRsp = pStrContent; + return ERR_SUCCESS; + } +#endif + cJSON *pRoot = cJSON_Parse(pStrContent); free((void *)pStrContent); if (!pRoot) { diff --git a/unit_test/json/json_validator.cpp b/unit_test/json/json_validator.cpp new file mode 100644 index 0000000..85edb24 --- /dev/null +++ b/unit_test/json/json_validator.cpp @@ -0,0 +1,54 @@ +// +// Created by xajhuang on 2023/2/23. +// +#include "doctest.h" +#ifdef __cplusplus +extern "C" { +#endif +#include "json_schema/jsoncdaccord.h" +#ifdef __cplusplus +} +#endif + +TEST_SUITE("Json schema validator functions") { + TEST_CASE("JSONCDAC VALIDATOR") { + const char *json = + "{\n" + " \"ver\": 3,\n" + " \"cryptoType\": 0,\n" + " \"timeStamp\": 1599187216753,\n" + " \"msgContent\": {\n" + " \"rangeSet\": [\n" + " {\n" + " \"dhcpRange\": \"192.168.30.50-192.168.30.60\",\n" + " \"netmask\": \"255.255.255.0\",\n" + " \"domainServer\": \"114.114.114.114, 8.8.8.8\",\n" + " \"gateway\": \"192.168.30.1\",\n" + " \"leaseTime\": 420\n" + " }\n" + " ]\n" + " }\n" + "}"; + + const char *sch = + "{\n" + "\t\"type\": \"object\",\n" + "\t\"properties\": {\n" + "\t\t\"ver\": {\n" + "\t\t\t\"type\": \"string\"\t\t\t\n" + "\t\t}\n" + "\t},\n" + "\t\"required\": [\n" + " \"ver\"\n" + " ]\n" + "}"; + + json_object *pJs = json_tokener_parse(json); + json_object *pSc = json_tokener_parse(sch); + + CHECK_NE(pJs, nullptr); + CHECK_NE(pSc, nullptr); + int ret = jdac_validate(pJs, pSc); + CHECK_EQ(JDAC_ERR_VALID, ret); + } +} \ No newline at end of file