From 095d15f6a51410c8eedff18275aa8dfcec2e16ce Mon Sep 17 00:00:00 2001 From: huangxin Date: Mon, 27 Feb 2023 10:34:43 +0800 Subject: [PATCH 01/31] =?UTF-8?q?OCT=201.=20=E5=A2=9E=E5=8A=A0=E5=8D=8F?= =?UTF-8?q?=E8=AE=AE=E6=8E=A5=E5=8F=A3=E5=8F=82=E6=95=B0=E5=AD=97=E6=AE=B5?= =?UTF-8?q?=E9=AA=8C=E8=AF=81=E6=A1=86=E6=9E=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- depend/json-c-0.16.tar.gz | Bin 0 -> 353091 bytes depend/third_libs.cmake | 30 + srcs/libs/CMakeLists.txt | 4 + srcs/libs/docs/json_schema/README.md | 130 ++ srcs/libs/docs/json_schema/json/flows.json | 1132 +++++++++++++++++ .../json_schema/json/side-test-pattern.json | 6 + .../json/side-test-patternProperties.json | 7 + .../docs/json_schema/json/test-anyof.json | 35 + .../libs/docs/json_schema/json/test-enum.json | 10 + .../docs/json_schema/json/test-items.json | 53 + .../json_schema/json/test-maxmin-items.json | 7 + .../json_schema/json/test-properties.json | 57 + .../json/test-reqs-recursively.json | 36 + .../docs/json_schema/json/test-required.json | 11 + .../libs/docs/json_schema/json/test-type.json | 8 + .../json_schema/json/test-uniqueitems.json | 10 + .../json_schema/json/vectors-variants.json | 106 ++ srcs/libs/docs/json_schema/json/vectors.json | 760 +++++++++++ srcs/libs/docs/json_schema/json/vectors2.json | 769 +++++++++++ srcs/libs/docs/json_schema/schema/empty.json | 1 + srcs/libs/docs/json_schema/schema/flows.json | 68 + .../json_schema/schema/side-test-pattern.json | 12 + .../schema/side-test-patternProperties.json | 23 + .../docs/json_schema/schema/test-anyof.json | 51 + .../docs/json_schema/schema/test-enum.json | 22 + .../docs/json_schema/schema/test-items.json | 51 + .../json_schema/schema/test-maxmin-items.json | 7 + .../json_schema/schema/test-properties.json | 80 ++ .../schema/test-reqs-recursively.json | 14 + .../json_schema/schema/test-required.json | 12 + .../docs/json_schema/schema/test-type.json | 12 + .../json_schema/schema/test-uniqueitems.json | 14 + .../libs/docs/json_schema/schema/vectors.json | 127 ++ srcs/libs/include/json_schema/internal.h | 26 + srcs/libs/include/json_schema/jsoncdaccord.h | 40 + srcs/libs/include/json_schema/optional.h | 36 + srcs/libs/include/proto.h | 4 +- srcs/libs/include/user_errno.h | 23 +- srcs/libs/json/json_schema/CMakeLists.txt | 42 + .../json/json_schema/additionalproperties.c | 58 + srcs/libs/json/json_schema/contains.c | 53 + srcs/libs/json/json_schema/download.c | 55 + srcs/libs/json/json_schema/jdac-cli.c | 103 ++ srcs/libs/json/json_schema/pattern.c | 27 + .../libs/json/json_schema/patternproperties.c | 32 + srcs/libs/json/json_schema/propertynames.c | 26 + srcs/libs/json/json_schema/ref.c | 88 ++ srcs/libs/json/json_schema/regex_match.c | 19 + srcs/libs/json/json_schema/store.c | 114 ++ srcs/libs/json/json_schema/subschemalogic.c | 116 ++ srcs/libs/json/json_schema/validate.c | 648 ++++++++++ srcs/libs/protocol/protocol.c | 76 +- srcs/opendhcp183/query.cpp | 30 +- unit_test/json/json_validator.cpp | 54 + 54 files changed, 5318 insertions(+), 17 deletions(-) create mode 100644 depend/json-c-0.16.tar.gz create mode 100644 srcs/libs/docs/json_schema/README.md create mode 100644 srcs/libs/docs/json_schema/json/flows.json create mode 100644 srcs/libs/docs/json_schema/json/side-test-pattern.json create mode 100644 srcs/libs/docs/json_schema/json/side-test-patternProperties.json create mode 100644 srcs/libs/docs/json_schema/json/test-anyof.json create mode 100644 srcs/libs/docs/json_schema/json/test-enum.json create mode 100644 srcs/libs/docs/json_schema/json/test-items.json create mode 100644 srcs/libs/docs/json_schema/json/test-maxmin-items.json create mode 100644 srcs/libs/docs/json_schema/json/test-properties.json create mode 100644 srcs/libs/docs/json_schema/json/test-reqs-recursively.json create mode 100644 srcs/libs/docs/json_schema/json/test-required.json create mode 100644 srcs/libs/docs/json_schema/json/test-type.json create mode 100644 srcs/libs/docs/json_schema/json/test-uniqueitems.json create mode 100644 srcs/libs/docs/json_schema/json/vectors-variants.json create mode 100644 srcs/libs/docs/json_schema/json/vectors.json create mode 100644 srcs/libs/docs/json_schema/json/vectors2.json create mode 100644 srcs/libs/docs/json_schema/schema/empty.json create mode 100644 srcs/libs/docs/json_schema/schema/flows.json create mode 100644 srcs/libs/docs/json_schema/schema/side-test-pattern.json create mode 100644 srcs/libs/docs/json_schema/schema/side-test-patternProperties.json create mode 100644 srcs/libs/docs/json_schema/schema/test-anyof.json create mode 100644 srcs/libs/docs/json_schema/schema/test-enum.json create mode 100644 srcs/libs/docs/json_schema/schema/test-items.json create mode 100644 srcs/libs/docs/json_schema/schema/test-maxmin-items.json create mode 100644 srcs/libs/docs/json_schema/schema/test-properties.json create mode 100644 srcs/libs/docs/json_schema/schema/test-reqs-recursively.json create mode 100644 srcs/libs/docs/json_schema/schema/test-required.json create mode 100644 srcs/libs/docs/json_schema/schema/test-type.json create mode 100644 srcs/libs/docs/json_schema/schema/test-uniqueitems.json create mode 100644 srcs/libs/docs/json_schema/schema/vectors.json create mode 100644 srcs/libs/include/json_schema/internal.h create mode 100644 srcs/libs/include/json_schema/jsoncdaccord.h create mode 100644 srcs/libs/include/json_schema/optional.h create mode 100644 srcs/libs/json/json_schema/CMakeLists.txt create mode 100644 srcs/libs/json/json_schema/additionalproperties.c create mode 100644 srcs/libs/json/json_schema/contains.c create mode 100644 srcs/libs/json/json_schema/download.c create mode 100644 srcs/libs/json/json_schema/jdac-cli.c create mode 100644 srcs/libs/json/json_schema/pattern.c create mode 100644 srcs/libs/json/json_schema/patternproperties.c create mode 100644 srcs/libs/json/json_schema/propertynames.c create mode 100644 srcs/libs/json/json_schema/ref.c create mode 100644 srcs/libs/json/json_schema/regex_match.c create mode 100644 srcs/libs/json/json_schema/store.c create mode 100644 srcs/libs/json/json_schema/subschemalogic.c create mode 100644 srcs/libs/json/json_schema/validate.c create mode 100644 unit_test/json/json_validator.cpp diff --git a/depend/json-c-0.16.tar.gz b/depend/json-c-0.16.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..53ee6be1f6df3be39bb7f6121f2a823440e1278f GIT binary patch literal 353091 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≀r7LV7cI6DagT>S0j! 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�lJ{j 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.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 From 68fb7845310a478e012e9ba33f5df661a8b853d7 Mon Sep 17 00:00:00 2001 From: huangxin Date: Mon, 27 Feb 2023 14:10:36 +0800 Subject: [PATCH 02/31] =?UTF-8?q?OCT=201.=20=E6=94=AF=E6=8C=81=E5=8D=8F?= =?UTF-8?q?=E8=AE=AE=E8=AF=B7=E6=B1=82JSON=E5=AD=97=E6=AE=B5=E8=A7=84?= =?UTF-8?q?=E8=8C=83=E6=A0=BC=E5=BC=8F=E6=A3=80=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 20 ++-- depend/third_libs.cmake | 54 ++++----- srcs/libs/CMakeLists.txt | 11 +- srcs/libs/include/proto.h | 2 - srcs/libs/include/zlog_module.h | 3 +- srcs/libs/json/json_schema/jdac-cli.c | 103 ------------------ .../libs/json/json_schema/patternproperties.c | 2 +- srcs/libs/json/json_schema/ref.c | 9 +- srcs/libs/json/json_schema/regex_match.c | 3 +- srcs/libs/json/json_schema/store.c | 31 +++++- srcs/libs/json/json_schema/validate.c | 43 ++++---- srcs/libs/protocol/protocol.c | 8 +- srcs/opendhcp183/query.cpp | 2 +- 13 files changed, 115 insertions(+), 176 deletions(-) delete mode 100644 srcs/libs/json/json_schema/jdac-cli.c diff --git a/CMakeLists.txt b/CMakeLists.txt index e429dfa..f9ce289 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,14 @@ CMAKE_MINIMUM_REQUIRED(VERSION 3.10 FATAL_ERROR) - INCLUDE(CMakeDependentOption) +OPTION(VCPE_AGENT "Enable vCPE agent test application" OFF) +OPTION(USED_LWIP "PPPoE of LWIP support for vCPE" OFF) +OPTION(USED_OPENDHCPD "DHCP server for vCPE" OFF) +OPTION(USED_OPENDHCPDDNS "DHCP And DNS server for vCPE" OFF) +OPTION(USED_USER_VNI "Support pass user vni id from console command line" OFF) +OPTION(USED_JSON_VALIDATE "Support json protocol field validate" OFF) +OPTION(BUILD_TESTING "Enable tests" OFF) + LIST(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/depend) IF (BUILD_TESTING) INCLUDE(doctest_framework) @@ -11,12 +18,7 @@ INCLUDE(system_libs) SET(VCPE_VERSION "1.0.1") PROJECT(vCPE VERSION "${VCPE_VERSION}") -OPTION(VCPE_AGENT "Enable vCPE agent test application" OFF) -OPTION(USED_LWIP "PPPoE of LWIP support for vCPE" OFF) -OPTION(USED_OPENDHCPD "DHCP server for vCPE" OFF) -OPTION(USED_OPENDHCPDDNS "DHCP And DNS server for vCPE" OFF) -OPTION(USED_USER_VNI "Support pass user vni id from console command line" OFF) -OPTION(BUILD_TESTING "Enable tests" OFF) + CMAKE_DEPENDENT_OPTION(USED_HTTP_SVR "Build-in http(s) server support" ON "USED_OPENDHCPD OR USED_OPENDHCPDDNS" OFF) # 数据库开关 @@ -82,6 +84,10 @@ SET(COMMON_LIBS "") INCLUDE(./depend/third_libs.cmake) +IF (USED_JSON_VALIDATE) + LIST(APPEND COMMON_DEFINE "-DJSON_SCHEMA_ON") +ENDIF() + IF (USED_USER_VNI) LIST(APPEND COMMON_DEFINE "-DUSERVNI_ON") MESSAGE("Select Option USED_USER_VNI") diff --git a/depend/third_libs.cmake b/depend/third_libs.cmake index 85f7eec..1573bce 100644 --- a/depend/third_libs.cmake +++ b/depend/third_libs.cmake @@ -4,15 +4,16 @@ 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 (USED_JSON_VALIDATE) + 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 - ) + 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 () ENDIF () - IF (NOT LIBUV_FOUND) FETCHCONTENT_DECLARE(libuv URL file://${CMAKE_SOURCE_DIR}/depend/libuv-v1.44.2.tar.gz @@ -43,10 +44,11 @@ IF (LIBZLOG STREQUAL "LIBZLOG-NOTFOUND") FETCHCONTENT_MAKEAVAILABLE(zlog) ENDIF () -IF (NOT LIBJSON-C_FOUND) - FETCHCONTENT_MAKEAVAILABLE(libjson-c) +IF (USED_JSON_VALIDATE) + IF (NOT LIBJSON-C_FOUND) + FETCHCONTENT_MAKEAVAILABLE(libjson-c) + ENDIF () ENDIF () - IF (NOT LIBUV_FOUND) MESSAGE(STATUS "libuv not found, will be used source code to build it ...") FETCHCONTENT_GETPROPERTIES(libuv) @@ -89,20 +91,22 @@ ELSE () 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}) +IF (USED_JSON_VALIDATE) + 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 () - 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 2a69bed..faaf2cd 100644 --- a/srcs/libs/CMakeLists.txt +++ b/srcs/libs/CMakeLists.txt @@ -2,8 +2,6 @@ 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) @@ -26,6 +24,11 @@ AUX_SOURCE_DIRECTORY(hardware C_SRC) AUX_SOURCE_DIRECTORY(protocol C_SRC) AUX_SOURCE_DIRECTORY(fs_watch C_SRC) AUX_SOURCE_DIRECTORY(zlog_module C_SRC) + +IF (USED_JSON_VALIDATE) + ADD_SUBDIRECTORY(./json/json_schema) +ENDIF () + IF (USED_HTTP_SVR) AUX_SOURCE_DIRECTORY(mongoose C_SRC) ENDIF () @@ -64,7 +67,9 @@ 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_JSON_VALIDATE) + TARGET_LINK_LIBRARIES(${LIB_PROJECT_TARGET} jsoncdac) +ENDIF () IF (USED_OPENDHCPD) TARGET_LINK_LIBRARIES(${LIB_PROJECT_TARGET} opendhcpd) diff --git a/srcs/libs/include/proto.h b/srcs/libs/include/proto.h index 46a075e..4291ef1 100644 --- a/srcs/libs/include/proto.h +++ b/srcs/libs/include/proto.h @@ -10,8 +10,6 @@ extern "C" { #endif #include -#define JSON_SCHEMA_ON (1) - typedef enum { CRYPTO_NONE = 0, CRYPTO_BASE64 = 1, diff --git a/srcs/libs/include/zlog_module.h b/srcs/libs/include/zlog_module.h index 6a7b582..90d6fcd 100644 --- a/srcs/libs/include/zlog_module.h +++ b/srcs/libs/include/zlog_module.h @@ -34,7 +34,8 @@ typedef enum { ZLOG_MOD(ZLOG_MOD_CRYPTO, ZLOG_LEVEL_DEBUG, "CRYPTO") \ ZLOG_MOD(ZLOG_MOD_MQ, ZLOG_LEVEL_DEBUG, "MQ") \ ZLOG_MOD(ZLOG_MOD_PROTO, ZLOG_LEVEL_DEBUG, "PROTO") \ - ZLOG_MOD(ZLOG_MOD_HTTPD, ZLOG_LEVEL_INFO, "HTTPD") \ + ZLOG_MOD(ZLOG_MOD_HTTPD, ZLOG_LEVEL_INFO, "HTTPD") \ + ZLOG_MOD(ZLOG_MOD_JSCHEM, ZLOG_LEVEL_INFO, "JSCHEM") \ ZLOG_MOD(ZLOG_MOD_USER, ZLOG_LEVEL_DEBUG, "USER") \ ZLOG_MOD(ZLOG_MOD_PPPOE, ZLOG_LEVEL_DEBUG, "PPPOE") \ ZLOG_MOD(ZLOG_MOD_VXLAN, ZLOG_LEVEL_DEBUG, "VXLAN") \ diff --git a/srcs/libs/json/json_schema/jdac-cli.c b/srcs/libs/json/json_schema/jdac-cli.c deleted file mode 100644 index d6745fd..0000000 --- a/srcs/libs/json/json_schema/jdac-cli.c +++ /dev/null @@ -1,103 +0,0 @@ -#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/patternproperties.c b/srcs/libs/json/json_schema/patternproperties.c index 352331d..bc18123 100644 --- a/srcs/libs/json/json_schema/patternproperties.c +++ b/srcs/libs/json/json_schema/patternproperties.c @@ -3,7 +3,7 @@ #include "optional.h" int _jdac_check_patternproperties(json_object *jobj, json_object *jschema) { - //printf("%s\n", __func__); + //LOG_MOD(debug, ZLOG_MOD_JSCHEM, "%s\n", __func__); // check jobj non object is already checked json_object *jpatprops = json_object_object_get(jschema, "patternProperties"); diff --git a/srcs/libs/json/json_schema/ref.c b/srcs/libs/json/json_schema/ref.c index 370b40e..a1b876a 100644 --- a/srcs/libs/json/json_schema/ref.c +++ b/srcs/libs/json/json_schema/ref.c @@ -3,6 +3,7 @@ #include "jsoncdaccord.h" #include "internal.h" #include "optional.h" +#include "../../include/zlog_module.h" char localpath[256] = {0}; @@ -33,7 +34,7 @@ int _jdac_check_ref(json_object *jobj, json_object *jschema, storage_node *stora 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); + LOG_MOD(trace, ZLOG_MOD_JSCHEM, "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) { @@ -47,7 +48,7 @@ int _jdac_check_ref(json_object *jobj, json_object *jschema, storage_node *stora if (!path_id) { char filepath[512]; snprintf(filepath, sizeof(filepath) - 1, "%s.%s", localpath, path_ref); - printf("filepath is %s\n", filepath); + LOG_MOD(trace, ZLOG_MOD_JSCHEM, "filepath is %s\n", filepath); json_object *jschemafromfile = json_object_from_file(filepath); if (!jschemafromfile) { return JDAC_ERR_JSON_NOT_FOUND; @@ -58,10 +59,10 @@ int _jdac_check_ref(json_object *jobj, json_object *jschema, storage_node *stora } if (strcmp(path_ref, path_id) != 0 && strlen(localpath) > 0) { - printf("yep\n"); + LOG_MOD(trace, ZLOG_MOD_JSCHEM, "yep\n"); char filepath[512]; snprintf(filepath, sizeof(filepath) - 1, "%s.%s", localpath, path_ref); - printf("filepath is %s\n", filepath); + LOG_MOD(trace, ZLOG_MOD_JSCHEM, "filepath is %s\n", filepath); json_object *jschemafromfile = json_object_from_file(filepath); if (!jschemafromfile) { return JDAC_ERR_JSON_NOT_FOUND; diff --git a/srcs/libs/json/json_schema/regex_match.c b/srcs/libs/json/json_schema/regex_match.c index c8e116b..a8967ee 100644 --- a/srcs/libs/json/json_schema/regex_match.c +++ b/srcs/libs/json/json_schema/regex_match.c @@ -2,12 +2,13 @@ #include #include #include "jsoncdaccord.h" +#include "../../include/zlog_module.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"); + LOG_MOD(error, ZLOG_MOD_JSCHEM, "Could not compile regex\n"); return JDAC_REGEX_COMPILE_FAILED; } reti = regexec(®ex, value, 0, NULL, 0); diff --git a/srcs/libs/json/json_schema/store.c b/srcs/libs/json/json_schema/store.c index 5810f7a..7ee88d9 100644 --- a/srcs/libs/json/json_schema/store.c +++ b/srcs/libs/json/json_schema/store.c @@ -2,6 +2,7 @@ #include #include "jsoncdaccord.h" #include "optional.h" +#include "../../include/zlog_module.h" void _jdac_store_append(storage_node **head, storage_node *ref) { storage_node *new_node = malloc(sizeof(storage_node)); @@ -69,7 +70,7 @@ int _jdac_store_traverse_json(storage_node **head, json_object *jschema, char *p } else { sprintf(pathbuf, "%s/%s", pathbuffer, jkey); } - //printf("%s\n", pathbuf); + //LOG_MOD(debug, ZLOG_MOD_JSCHEM, "%s\n", pathbuf); _jdac_store_traverse_json(head, jval, pathbuf); } } @@ -78,9 +79,29 @@ int _jdac_store_traverse_json(storage_node **head, json_object *jschema, char *p 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"); + LOG_MOD(trace, + ZLOG_MOD_JSCHEM, + "%-*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); + LOG_MOD(trace, + ZLOG_MOD_JSCHEM, + "%-*s %-*s %-*s %-*s\n", + 32, + list->JSONPtrURI, + 16, + list->anchor, + 16, + list->dynamicAnchor, + 32, + list->id); list = list->next; } } @@ -88,9 +109,9 @@ void _jdac_store_print(storage_node *head) { 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"); + //LOG_MOD(trace, ZLOG_MOD_JSCHEM, "%-*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); + //LOG_MOD(trace, ZLOG_MOD_JSCHEM, "%-*s %-*s %-*s %-*s\n", 32, list->JSONPtrURI, 16, list->anchor, 16, list->dynamicAnchor, 32, list->id); if (list->next == NULL) { return list; } diff --git a/srcs/libs/json/json_schema/validate.c b/srcs/libs/json/json_schema/validate.c index 55f5d23..5cecff4 100644 --- a/srcs/libs/json/json_schema/validate.c +++ b/srcs/libs/json/json_schema/validate.c @@ -6,6 +6,7 @@ #include "jsoncdaccord.h" #include "internal.h" #include "optional.h" +#include "../../include/zlog_module.h" json_object *json = NULL; json_object *schema = NULL; @@ -102,7 +103,7 @@ int __jdac_inspect_type(json_object *jobj, const char *type) { return JDAC_ERR_VALID; } } else { - printf("WARN unknown type in check type %s\n", type); + LOG_MOD(warn, ZLOG_MOD_JSCHEM, "WARN unknown type in check type %s\n", type); return JDAC_ERR_SCHEMA_ERROR; } return JDAC_ERR_INVALID_TYPE; @@ -136,7 +137,7 @@ int _jdac_check_type(json_object *jobj, json_object *jschema) { } int _jdac_check_required(json_object *jobj, json_object *jschema) { - //printf("%s\n%s\n", __func__, json_object_to_json_string(jobj)); + //LOG_MOD(debug, ZLOG_MOD_JSCHEM, "%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) { @@ -145,12 +146,12 @@ int _jdac_check_required(json_object *jobj, json_object *jschema) { json_object *iobj = json_object_array_get_idx(jarray, i); const char *key = json_object_get_string(iobj); if (key) { - //printf("%s\n", key); + //LOG_MOD(debug, ZLOG_MOD_JSCHEM, "%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); + LOG_MOD(debug, ZLOG_MOD_JSCHEM, "required key missing: %s\n", key); missing_required_key = 1; } } @@ -164,14 +165,14 @@ int _jdac_check_required(json_object *jobj, json_object *jschema) { } int _jdac_check_properties(json_object *jobj, json_object *jschema) { - // printf("%s\n", __func__); + // LOG_MOD(debug, ZLOG_MOD_JSCHEM, "%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); + // LOG_MOD(debug, ZLOG_MOD_JSCHEM, "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)); + //LOG_MOD(debug, ZLOG_MOD_JSCHEM, "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) { @@ -199,7 +200,7 @@ int _jdac_check_prefixItems_and_items(json_object *jobj, json_object *jschema) { prefixitems_arraylen = json_object_array_length(jprefixitems); for (int i = 0; i < jobj_arraylen && i < prefixitems_arraylen; i++) { - //printf("i=%d prefixitems\n", i); + //LOG_MOD(debug, ZLOG_MOD_JSCHEM, "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); @@ -215,7 +216,7 @@ int _jdac_check_prefixItems_and_items(json_object *jobj, json_object *jschema) { } for (int i = prefixitems_arraylen; i < jobj_arraylen; i++) { - //printf("i=%d items\n", i); + //LOG_MOD(debug, ZLOG_MOD_JSCHEM, "i=%d items\n", i); json_object *iobj = json_object_array_get_idx(jobj, i); int err = _jdac_validate_instance(iobj, jitems); if (err) { @@ -298,7 +299,7 @@ int _jdac_check_enums(json_object *jobj, json_object *jschema) { return JDAC_ERR_VALID; } } - printf("ERROR: enum check failed (%s not in enum)\n", json_object_to_json_string(jobj)); + LOG_MOD(error, ZLOG_MOD_JSCHEM, "ERROR: enum check failed (%s not in enum)\n", json_object_to_json_string(jobj)); return JDAC_ERR_INVALID_ENUMS; } @@ -353,7 +354,7 @@ int _jdac_check_maxmin_items(json_object *jobj, json_object *jschema) { } if (err) { - printf("ERROR: failed at maxItems or minItems check\n"); + LOG_MOD(error, ZLOG_MOD_JSCHEM, "ERROR: failed at maxItems or minItems check\n"); } return err; } @@ -440,7 +441,7 @@ int utf8_length(const char *str) { 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)); + //LOG_MOD(debug, ZLOG_MOD_JSCHEM, "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); @@ -536,15 +537,15 @@ int _jdac_validate_number(json_object *jobj, json_object *jschema, double value) } int _jdac_validate_boolean(json_object *jobj, json_object *jschema) { - // printf("%s\n", __func__); + // LOG_MOD(debug, ZLOG_MOD_JSCHEM, "%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)); + // LOG_MOD(debug, ZLOG_MOD_JSCHEM, "--validate instance--\n"); + // LOG_MOD(debug, ZLOG_MOD_JSCHEM, "%s\n", json_object_get_string(jobj)); + // LOG_MOD(debug, ZLOG_MOD_JSCHEM, "%s\n", json_object_get_string(jschema)); #ifdef JDAC_REF err = _jdac_check_ref(jobj, jschema, storagelist_head); @@ -580,13 +581,13 @@ int _jdac_validate_instance(json_object *jobj, json_object *jschema) { } // if (!json_object_is_type(jobj, json_type_null)) - // printf("%s\n", json_object_get_string(jobj)); + // LOG_MOD(debug, ZLOG_MOD_JSCHEM, "%s\n", json_object_get_string(jobj)); // else - // printf("jobj was null\n"); + // LOG_MOD(debug, ZLOG_MOD_JSCHEM, "jobj was null\n"); // if (!json_object_is_type(jschema, json_type_null)) - // printf("%s\n", json_object_get_string(jschema)); + // LOG_MOD(debug, ZLOG_MOD_JSCHEM, "%s\n", json_object_get_string(jschema)); // else - // printf("jschema was null\n"); + // LOG_MOD(debug, ZLOG_MOD_JSCHEM, "jschema was null\n"); #ifdef JDAC_SUBSCHEMALOGIC err = _jdac_check_subschemalogic(jobj, jschema); @@ -612,7 +613,7 @@ int _jdac_validate_instance(json_object *jobj, json_object *jschema) { } else if (type == json_type_null) { return JDAC_ERR_VALID; } else { - printf("WARN: type %d not handled\n", type); + LOG_MOD(warn, ZLOG_MOD_JSCHEM, "WARN: type %d not handled\n", type); } return JDAC_ERR_VALID; diff --git a/srcs/libs/protocol/protocol.c b/srcs/libs/protocol/protocol.c index 84ed316..eafda62 100644 --- a/srcs/libs/protocol/protocol.c +++ b/srcs/libs/protocol/protocol.c @@ -10,8 +10,9 @@ #include "crypto.h" #include "user_errno.h" #include "zlog_module.h" +#ifdef JSON_SCHEMA_ON #include "json_schema/jsoncdaccord.h" - +#endif #define CURRENT_PROTOCOL_VERSION (1) typedef struct { @@ -61,6 +62,7 @@ const char *proto_schema_validation(const char *pJsonStr) { cJSON_AddStringToObject(pRspRoot, "message", getErrorEnumDesc(ERR_JSON_VALID_SCH)); cJSON_AddStringToObject(pRspRoot, "details", g_json_sch[i].pErrMsg); json_object_put(pSc); + json_object_put(pJs); return proto_create_new(pRspRoot, 200); } @@ -92,6 +94,8 @@ const char *proto_decode_context(const char *pString, unsigned int *pVer, unsign return NULL; } + LOG_MOD(debug, ZLOG_MOD_PROTO, "Request: %s\n", pString); + #ifdef JSON_SCHEMA_ON pSchJson = proto_schema_validation(pString); if (pSchJson != NULL && strlen(pSchJson) > 0) { @@ -273,7 +277,7 @@ const char *proto_create_new(cJSON *pMsgCtx, int httpCode) { } pStrProto = cJSON_PrintUnformatted(pRoot); - LOG_MOD(trace, ZLOG_MOD_PROTO, "Create: \n%s\n", pStrProto); + LOG_MOD(debug, ZLOG_MOD_PROTO, "Create: %s\n", pStrProto); cJSON_Delete(pRoot); diff --git a/srcs/opendhcp183/query.cpp b/srcs/opendhcp183/query.cpp index a947eba..e71cd45 100644 --- a/srcs/opendhcp183/query.cpp +++ b/srcs/opendhcp183/query.cpp @@ -341,7 +341,7 @@ static int add_dhcpd_rangeset(const char **pRsp, const char *pRequest) { logDHCPMess(logBuff, 1); return ERR_PROTO_DECODE; } - + #ifdef JSON_SCHEMA_ON if (errCode == ERR_JSON_VALID_SCH) { *pRsp = pStrContent; From 2c9a9eeaf8ce016c4a48c56741a8493714671d40 Mon Sep 17 00:00:00 2001 From: huangxin Date: Fri, 3 Mar 2023 15:08:35 +0800 Subject: [PATCH 03/31] =?UTF-8?q?OCT=201.=20=E5=A2=9E=E5=8A=A0=E8=BF=9B?= =?UTF-8?q?=E7=A8=8B=E5=94=AF=E4=B8=80=E8=BF=90=E8=A1=8C=E6=A3=80=E6=B5=8B?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- srcs/libs/include/misc.h | 2 + srcs/libs/include/user_errno.h | 87 ++++++++++++++++---------------- srcs/libs/include/zlog_module.h | 7 +++ srcs/libs/init/init_runtime.c | 13 +++++ srcs/libs/misc/unique_instance.c | 34 +++++++++++++ 5 files changed, 99 insertions(+), 44 deletions(-) create mode 100644 srcs/libs/misc/unique_instance.c diff --git a/srcs/libs/include/misc.h b/srcs/libs/include/misc.h index de22005..0477ea5 100644 --- a/srcs/libs/include/misc.h +++ b/srcs/libs/include/misc.h @@ -61,6 +61,8 @@ int get_nic_info(const char *pName, unsigned char *pMac); int str_to_ipaddr(const char *pIp, unsigned int *ipAddr); unsigned long long get_current_time_ms(); +int process_lock_pidfile(char *pFilePath); +void process_unlock_pidfile(); #ifdef __cplusplus } #endif diff --git a/srcs/libs/include/user_errno.h b/srcs/libs/include/user_errno.h index 9648ffd..ebaf615 100644 --- a/srcs/libs/include/user_errno.h +++ b/srcs/libs/include/user_errno.h @@ -8,8 +8,6 @@ extern "C" { #endif -#define USED_USER_ERRNO - #define DEF_ERR_CODE(ERR_CODE) \ ERR_CODE(ERR_SUCCESS, 0, "成功") \ ERR_CODE(ERR_INPUT_PARAMS, 1, "输入参数错误") \ @@ -21,48 +19,49 @@ extern "C" { ERR_CODE(ERR_OPEN_FILE, 7, "打开文件失败") \ ERR_CODE(ERR_READ_FILE, 8, "读取文件失败") \ ERR_CODE(ERR_FILE_NOT_EXISTS, 9, "文件不存在") \ - ERR_CODE(ERR_GET_FILE_SIZE, 10, "获取文件大小失败") \ - ERR_CODE(ERR_COPY_FILE, 11, "复制文件失败") \ - ERR_CODE(ERR_MALLOC_MEMORY, 12, "分配内存失败") \ - ERR_CODE(ERR_EVP_KEY_SIZE, 13, "秘钥大小不正确") \ - ERR_CODE(ERR_UNSUP_EVP_TYPE, 14, "不支持的加解密算法") \ - ERR_CODE(ERR_EVP_INIT_KEY, 15, "初始化秘钥失败") \ - ERR_CODE(ERR_EVP_UPDATE, 16, "加解密数据失败") \ - ERR_CODE(ERR_EVP_FINALE, 17, "错误的加解密结果") \ - ERR_CODE(ERR_EVP_CREATE_CTX, 18, "初始化加解密失败") \ - ERR_CODE(ERR_AES128_KEYGEN, 19, "AES128秘钥失败") \ - ERR_CODE(ERR_EVP_ENCRYPTION, 20, "加密失败") \ - ERR_CODE(ERR_EVP_DECRYPTION, 21, "解密失败") \ - ERR_CODE(ERR_CONFIG_INIT, 22, "配置文件初始化失败") \ - ERR_CODE(ERR_UNCOMPATIBILITY_TYPE, 23, "未兼容的类型") \ - ERR_CODE(ERR_STRING_TO_NUMBER, 24, "字符串转数字失败") \ - ERR_CODE(ERR_UNKNOWN_CFG_ID, 25, "未识别的配置项") \ - ERR_CODE(ERR_ZLOG_INIT, 26, "日志系统初始化失败") \ - ERR_CODE(ERR_SYS_GET_CPU_INFO, 27, "获取CPU信息失败") \ - ERR_CODE(ERR_SYS_NOT_FOUND_CPU, 28, "找不到CPU信息") \ - ERR_CODE(ERR_SYS_DISK_GET_INFO, 29, "获取磁盘空间占用信息失败") \ - ERR_CODE(ERR_SYS_IPMI_UNSUP, 30, "服务器IPMI接口不支持") \ - ERR_CODE(ERR_SYS_SENSOR_GET_INFO, 31, "获取传感器信息失败") \ - ERR_CODE(ERR_DB_CONNECT, 32, "数据库连接失败") \ - ERR_CODE(ERR_MQ_CREATE_MQ, 33, "创建消息队列失败") \ - ERR_CODE(ERR_MQ_CREATE_REP, 34, "创建REP消息队列失败") \ - ERR_CODE(ERR_MQ_BIND_SOCKET, 35, "消息队列BIND Socket失败") \ - ERR_CODE(ERR_MQ_CONN_SERVER, 36, "消息队列连接服务器失败") \ - 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_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 协议解析失败") + ERR_CODE(ERR_FILE_LOCKED, 10, "文件被锁定") \ + ERR_CODE(ERR_GET_FILE_SIZE, 11, "获取文件大小失败") \ + ERR_CODE(ERR_COPY_FILE, 12, "复制文件失败") \ + ERR_CODE(ERR_MALLOC_MEMORY, 13, "分配内存失败") \ + ERR_CODE(ERR_EVP_KEY_SIZE, 14, "秘钥大小不正确") \ + ERR_CODE(ERR_UNSUP_EVP_TYPE, 15, "不支持的加解密算法") \ + ERR_CODE(ERR_EVP_INIT_KEY, 16, "初始化秘钥失败") \ + ERR_CODE(ERR_EVP_UPDATE, 17, "加解密数据失败") \ + ERR_CODE(ERR_EVP_FINALE, 18, "错误的加解密结果") \ + ERR_CODE(ERR_EVP_CREATE_CTX, 19, "初始化加解密失败") \ + ERR_CODE(ERR_AES_KEYGEN, 20, "AES秘钥失败") \ + ERR_CODE(ERR_EVP_ENCRYPTION, 21, "加密失败") \ + ERR_CODE(ERR_EVP_DECRYPTION, 22, "解密失败") \ + ERR_CODE(ERR_CONFIG_INIT, 23, "配置文件初始化失败") \ + ERR_CODE(ERR_UNCOMPATIBILITY_TYPE, 24, "未兼容的类型") \ + ERR_CODE(ERR_STRING_TO_NUMBER, 25, "字符串转数字失败") \ + ERR_CODE(ERR_UNKNOWN_CFG_ID, 26, "未识别的配置项") \ + ERR_CODE(ERR_ZLOG_INIT, 27, "日志系统初始化失败") \ + ERR_CODE(ERR_SYS_GET_CPU_INFO, 28, "获取CPU信息失败") \ + ERR_CODE(ERR_SYS_NOT_FOUND_CPU, 29, "找不到CPU信息") \ + ERR_CODE(ERR_SYS_DISK_GET_INFO, 30, "获取磁盘空间占用信息失败") \ + ERR_CODE(ERR_SYS_IPMI_UNSUP, 31, "服务器IPMI接口不支持") \ + ERR_CODE(ERR_SYS_SENSOR_GET_INFO, 32, "获取传感器信息失败") \ + ERR_CODE(ERR_DB_CONNECT, 33, "数据库连接失败") \ + ERR_CODE(ERR_MQ_CREATE_MQ, 34, "创建消息队列失败") \ + ERR_CODE(ERR_MQ_CREATE_REP, 35, "创建REP消息队列失败") \ + ERR_CODE(ERR_MQ_BIND_SOCKET, 36, "消息队列BIND Socket失败") \ + ERR_CODE(ERR_MQ_CONN_SERVER, 37, "消息队列连接服务器失败") \ + ERR_CODE(ERR_MQ_SEND_MSG, 38, "消息队列发送消息失败") \ + ERR_CODE(ERR_JSON_CREAT_OBJ, 39, "创建JSON对象失败") \ + ERR_CODE(ERR_JSON_PRASE_OBJ, 40, "解析JSON对象失败") \ + ERR_CODE(ERR_JSON_VALID_SCH, 41, "JSON数据验证失败") \ + ERR_CODE(ERR_CREATE_NETIF, 42, "创建网络接口失败") \ + ERR_CODE(ERR_CREATE_PPPOE_NETIF, 43, "创建PPPoE网络接口失败") \ + ERR_CODE(ERR_CREATE_PPP_SESSION, 44, "创建PPP连接失败") \ + ERR_CODE(ERR_MISC_GET_IPADDR, 45, "获取网卡IP地址失败") \ + ERR_CODE(ERR_MISC_GET_NETMASK, 46, "获取网卡子网掩码失败") \ + ERR_CODE(ERR_MISC_GET_GATEWAY, 47, "获取网卡网关地址失败") \ + ERR_CODE(ERR_MISC_GET_MACADDR, 48, "获取网卡MAC地址失败") \ + ERR_CODE(ERR_MENU_EXIT, 49, "菜单执行完后自动退出") \ + ERR_CODE(ERR_HTTP_UNSUP_METHOD, 50, "不支持的 HTTP 请求方法") \ + ERR_CODE(ERR_HTTP_UNSUP_PAGE, 51, "找不到 HTTP 服务") \ + ERR_CODE(ERR_PROTO_DECODE, 52, "HTTP 协议解析失败") #define GENERATE_ENUM(ENUM, no, x) ENUM, diff --git a/srcs/libs/include/zlog_module.h b/srcs/libs/include/zlog_module.h index 90d6fcd..27c7ff8 100644 --- a/srcs/libs/include/zlog_module.h +++ b/srcs/libs/include/zlog_module.h @@ -65,6 +65,9 @@ typedef enum { #define LOG_MOD(level, mod, format, ...) \ do { \ if (zlog_verify_level(level, mod)) { \ + if (zlog_vni_tag_get()) { \ + zlog_put_mdc("vni", zlog_vni_tag_get()); \ + } \ zlog_##level(zlog_get_mod_cat((mod)), format, ##__VA_ARGS__); \ } \ } while (0) @@ -72,12 +75,16 @@ typedef enum { #define LOG_MOD_HEX(level, mod, format, ...) \ do { \ if (zlog_verify_level(level, mod)) { \ + if (zlog_vni_tag_get()) { \ + zlog_put_mdc("vni", zlog_vni_tag_get()); \ + } \ hzlog_##level(zlog_get_mod_cat((mod)), format, ##__VA_ARGS__); \ } \ } while (0) zlog_category_t *zlog_get_mod_cat(ZLOG_MOD_NAME logMod); int zlog_verify_level(int level, ZLOG_MOD_NAME logMod); +const char *zlog_vni_tag_get(); #ifdef __cplusplus } #endif diff --git a/srcs/libs/init/init_runtime.c b/srcs/libs/init/init_runtime.c index 7eac072..19660fe 100644 --- a/srcs/libs/init/init_runtime.c +++ b/srcs/libs/init/init_runtime.c @@ -40,6 +40,7 @@ int user_init(const char *pAppCfgFile, const char *pCfgDirectory, const char *pK UT_string *pPath; char bufCfgFile[MAX_PATH]; char bufCfgDir[MAX_PATH]; + char pidfile[MAX_PATH]; g_pid = uv_os_getpid(); @@ -112,6 +113,17 @@ int user_init(const char *pAppCfgFile, const char *pCfgDirectory, const char *pK LOG_MOD(info, ZLOG_MOD_INIT, "Application build configure: [%s]\n", VCPE_BUILD_CONFIG); +#ifdef USERVNI_ON + sprintf(pidfile, "/tmp/%d_vcpe.pid", cfg_get_user_vni_id()); +#else + sprintf(pidfile, "/tmp/vcpe.pid"); +#endif + + if (process_lock_pidfile(pidfile) == -ERR_FILE_LOCKED) { + LOG_MOD(error, ZLOG_MOD_INIT, "!!!Another same process is running!!!, system exit......\n"); + exit(-ERR_FILE_LOCKED); + } + if (cfg_get_banner_enable()) { banner_show(); } @@ -148,5 +160,6 @@ void user_uninit() { zlog_fini(); uninit_config_system(); uv_loop_close(get_task_manager()); + process_unlock_pidfile(); } } diff --git a/srcs/libs/misc/unique_instance.c b/srcs/libs/misc/unique_instance.c new file mode 100644 index 0000000..6ca88b8 --- /dev/null +++ b/srcs/libs/misc/unique_instance.c @@ -0,0 +1,34 @@ +// +// Created by xajhuang on 2023/3/2. +// +#include +#include +#include +#include +#include +#include +#include "user_errno.h" +#include "misc.h" +static int g_lockfd = -1; +static char g_pidPath[MAX_PATH] = {0}; + +int process_lock_pidfile(char *pFilePath) { + if (pFilePath == NULL || strlen(pFilePath) == 0) { + return ERR_SUCCESS; + } + + strcpy(g_pidPath, pFilePath); + + g_lockfd = open(pFilePath, O_CREAT | O_RDWR, 0666); // open file + int rc = flock(g_lockfd, LOCK_EX | LOCK_NB); // lock access to open file + if (rc && EWOULDBLOCK == errno) { // check lock success + return -ERR_FILE_LOCKED; // another instance is running + } + + return ERR_SUCCESS; +} + +void process_unlock_pidfile() { + close(g_lockfd); + unlink(g_pidPath); +} \ No newline at end of file From 06a634ccee645107e38bf6afde916e59a9ac179e Mon Sep 17 00:00:00 2001 From: huangxin Date: Fri, 3 Mar 2023 15:17:20 +0800 Subject: [PATCH 04/31] =?UTF-8?q?OCT=201.=20=E5=A2=9E=E5=8A=A0=E5=A4=9A?= =?UTF-8?q?=E7=A7=9F=E6=88=B7=E5=88=86=E7=A6=BB=E6=97=A5=E5=BF=97=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E5=8A=9F=E8=83=BD=202.=20=E6=97=A5=E5=BF=97=E7=BB=9F?= =?UTF-8?q?=E4=B8=80=E4=BF=9D=E5=AD=98=E7=9B=AE=E5=BD=95=20/var/log/vcpe/?= =?UTF-8?q?=203.=20=E5=AF=B9=E6=89=80=E6=9C=89=E5=AD=90=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E7=BB=9F=E4=B8=80=E7=AC=AC=E4=B8=89=E6=96=B9=E4=BE=9D=E8=B5=96?= =?UTF-8?q?=20COMMON=5FLIBS=20=E6=9F=A5=E6=89=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 2 -- config/zlog.conf | 6 +++--- depend/third_libs.cmake | 13 +++++++++++++ srcs/CMakeLists.txt | 13 ------------- srcs/libs/crypto/symmetric.c | 10 +++++----- srcs/libs/zlog_module/zlog_module.c | 17 +++++++++++++++++ unit_test/CMakeLists.txt | 2 +- 7 files changed, 39 insertions(+), 24 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f9ce289..c4c0eb5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,8 +17,6 @@ INCLUDE(system_libs) SET(VCPE_VERSION "1.0.1") PROJECT(vCPE VERSION "${VCPE_VERSION}") - - CMAKE_DEPENDENT_OPTION(USED_HTTP_SVR "Build-in http(s) server support" ON "USED_OPENDHCPD OR USED_OPENDHCPDDNS" OFF) # 数据库开关 diff --git a/config/zlog.conf b/config/zlog.conf index 6376df4..842435a 100644 --- a/config/zlog.conf +++ b/config/zlog.conf @@ -9,7 +9,7 @@ buffer max = 2MB rotate lock file = self default format = "%d(%F %T.%l) %-6V (%c:%f:%L) %m%n" -file perms = 600 +file perms = 644 fsync period = 1K [levels] @@ -22,8 +22,8 @@ normal = "[%d(%F %T).%ms][%-6V][%c][%f:%L] %m" [rules] *.* >stdout; normal -*.INFO "./log/%c.log", \ - 1MB * 12 ~ "%E(HOME)/log/%c.%D(%F) #2r #3s.log"; \ +*.INFO "/var/log/vcpe/%M(vni)_vcpe.log", \ + 1MB * 12 ~ "/var/log/vcpe/%M(vni)_vcpe_.%D(%F) #2r #3s.log"; \ normal diff --git a/depend/third_libs.cmake b/depend/third_libs.cmake index 1573bce..284cf67 100644 --- a/depend/third_libs.cmake +++ b/depend/third_libs.cmake @@ -1,6 +1,19 @@ CMAKE_MINIMUM_REQUIRED(VERSION 3.14 FATAL_ERROR) INCLUDE(FetchContent) +PKG_SEARCH_MODULE(LIBCURL QUIET libcurl) +PKG_SEARCH_MODULE(LIBSSL QUIET libssl) +PKG_SEARCH_MODULE(LIBCRYPTO QUIET libcrypto) +PKG_SEARCH_MODULE(LIBZMQ QUIET libzmq) + +IF ((NOT LIBZMQ_FOUND) OR (NOT LIBCRYPTO_FOUND) OR (NOT LIBSSL_FOUND) OR (NOT LIBCURL_FOUND)) + LINUX_INSTALL_SYSTEM_PACKAGE() +ENDIF () + +LIST(APPEND COMMON_LIBS "${LIBCURL_LDFLAGS} ${LIBSSL_LDFLAGS} ${LIBCRYPTO_LDFLAGS}") +LIST(APPEND COMMON_LIBS "${LIBZMQ_LDFLAGS}") +LIST(APPEND COMMON_LIBS "-lm -lpthread") + PKG_SEARCH_MODULE(LIBUV QUIET libuv) PKG_SEARCH_MODULE(LIBCONFIG QUIET libconfig) FIND_LIBRARY(LIBZLOG zlog PATHS "/usr/local/lib") diff --git a/srcs/CMakeLists.txt b/srcs/CMakeLists.txt index e718093..c337c5b 100644 --- a/srcs/CMakeLists.txt +++ b/srcs/CMakeLists.txt @@ -6,19 +6,6 @@ ENDIF () PROJECT(${PROJECT_TARGET}) -PKG_SEARCH_MODULE(LIBCURL QUIET libcurl) -PKG_SEARCH_MODULE(LIBSSL QUIET libssl) -PKG_SEARCH_MODULE(LIBCRYPTO QUIET libcrypto) -PKG_SEARCH_MODULE(LIBZMQ QUIET libzmq) - -IF ((NOT LIBZMQ_FOUND) OR (NOT LIBCRYPTO_FOUND) OR (NOT LIBSSL_FOUND) OR (NOT LIBCURL_FOUND)) - LINUX_INSTALL_SYSTEM_PACKAGE() -ENDIF () - -LIST(APPEND COMMON_LIBS "${LIBCURL_LDFLAGS} ${LIBSSL_LDFLAGS} ${LIBCRYPTO_LDFLAGS}") -LIST(APPEND COMMON_LIBS "${LIBZMQ_LDFLAGS}") -LIST(APPEND COMMON_LIBS "-lm -lpthread") - ADD_DEFINITIONS(${COMMON_DEFINE}) INCLUDE_DIRECTORIES(include ./ ./include ./libs/include ./lwip/src/include ./lwip/src/arch_linux/include diff --git a/srcs/libs/crypto/symmetric.c b/srcs/libs/crypto/symmetric.c index d18fb62..5d713ae 100644 --- a/srcs/libs/crypto/symmetric.c +++ b/srcs/libs/crypto/symmetric.c @@ -68,7 +68,7 @@ static int sha1prng_for_aes_key(const char *pKey, unsigned char *pShaPrng16) { case AES128_ECB_PKCS7PADDING_SHA1PRNG: \ if (sha1prng_for_aes_key(pKey, keyBuf) != ERR_SUCCESS) { \ EVP_CIPHER_CTX_cleanup(pCtx); \ - return -ERR_AES128_KEYGEN; \ + return -ERR_AES_KEYGEN; \ } \ pCipher = EVP_aes_128_ecb(); \ break; \ @@ -165,8 +165,8 @@ int symmetric_decrypto(AES_TYPE algorithmType, return -ERR_EVP_UPDATE; } - *pOutBuf = pAesBuf; - pAesBuf += enBytes; + *pOutBuf = pAesBuf; + pAesBuf += enBytes; *pOutSize += enBytes; if (EVP_DecryptFinal_ex(pCtx, pAesBuf, &enBytes) == 0) { @@ -239,8 +239,8 @@ int symmetric_encrypto(AES_TYPE algorithmType, return -ERR_EVP_UPDATE; } - *pOutBuf = pAesBuf; - pAesBuf += enBytes; + *pOutBuf = pAesBuf; + pAesBuf += enBytes; *pOutSize += enBytes; if (EVP_EncryptFinal_ex(pCtx, pAesBuf, &enBytes) == 0) { diff --git a/srcs/libs/zlog_module/zlog_module.c b/srcs/libs/zlog_module/zlog_module.c index b739cb2..647d921 100644 --- a/srcs/libs/zlog_module/zlog_module.c +++ b/srcs/libs/zlog_module/zlog_module.c @@ -4,6 +4,7 @@ #include #include "zlog_module.h" #include "misc.h" +#include "config.h" #define MAX_ZLOG_MOD_LEN (8) @@ -20,6 +21,22 @@ static ZLOG_MODULE g_zlogModule[] = { DEF_ZLOG_MOD(GENERATE_ENUM_ARRAY) {-1, ZLOG_LEVEL_FATAL, NULL, "UNKNOWN"} }; +#ifdef USERVNI_ON +static char g_vni_str[64] = {0}; + +const char *zlog_vni_tag_get() { + if (strlen(g_vni_str) == 0) { + sprintf(g_vni_str, "%u", cfg_get_user_vni_id()); + } + + return (const char *)g_vni_str; +} +#else +const char *zlog_vni_tag_get() { + return "main"; +} +#endif + zlog_category_t *zlog_get_mod_cat(ZLOG_MOD_NAME logMod) { if (logMod >= ARRAY_SIZE(g_zlogModule) || logMod < 0) { logMod = ARRAY_SIZE(g_zlogModule) - 1; diff --git a/unit_test/CMakeLists.txt b/unit_test/CMakeLists.txt index 43f1b8e..35b590e 100644 --- a/unit_test/CMakeLists.txt +++ b/unit_test/CMakeLists.txt @@ -8,7 +8,7 @@ ADD_DEFINITIONS(-DPROJECT_NAME="${PROJECT_NAME}") ADD_EXECUTABLE(${PROJECT_NAME} ${UTEST_SRC}) -TARGET_LINK_LIBRARIES(${PROJECT_NAME} common) +TARGET_LINK_LIBRARIES(${PROJECT_NAME} common ${COMMON_LIBS}) #INCLUDE(CTest) #INCLUDE(doctest) From 3f91f98a22e577d71ad3dbf4fe6aa875d606ed72 Mon Sep 17 00:00:00 2001 From: dongwenze Date: Fri, 3 Mar 2023 16:37:52 +0800 Subject: [PATCH 05/31] =?UTF-8?q?OCT=201.=E4=BF=AE=E6=AD=A3=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E5=8A=9F=E8=83=BD=E7=9A=84=E5=86=85=E5=AD=98=E6=B3=84?= =?UTF-8?q?=E6=BC=8F=202.=E4=BF=AE=E6=AD=A3=E5=88=A0=E9=99=A4=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E5=8F=82=E6=95=B0=E5=BC=82=E5=B8=B8=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- srcs/opendhcp183/query.cpp | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/srcs/opendhcp183/query.cpp b/srcs/opendhcp183/query.cpp index e71cd45..69467c9 100644 --- a/srcs/opendhcp183/query.cpp +++ b/srcs/opendhcp183/query.cpp @@ -471,7 +471,6 @@ static int delete_dhcpd_rangeset(const char **pRsp, const char *pRequest) { cJSON *pdel_Item = cJSON_CreateObject(); cJSON *pdelRange = cJSON_GetArrayItem(pdhcp_range, i); char del_range[256]; - auto *delItem = (PHASH_MAP)malloc(sizeof(HASH_MAP)); if (!pdelRange) { continue; @@ -486,17 +485,21 @@ static int delete_dhcpd_rangeset(const char **pRsp, const char *pRequest) { mySplit(start, end, del_range, '-'); st_addr = htonl(inet_addr(start)); en_addr = htonl(inet_addr(end)); - if (en_addr == 0) { - cJSON_AddStringToObject(pdel_Item, "dhcpRange", del_range); + if (st_addr > en_addr) { + cJSON_AddStringToObject(pdel_Item, "dhcpRange", del_range); cJSON_AddNumberToObject(pdel_Item, "status", ERR_INPUT_PARAMS); cJSON_AddStringToObject(pdel_Item, "message", getErrorEnumDesc(ERR_INPUT_PARAMS)); cJSON_AddItemToArray(pdelArray, pdel_Item); + break; + } else { + cJSON_Delete(pdel_Item); } - HASH_FIND_INT(delMap, &st_addr, delItem); - if (delItem == nullptr) { - auto *s = (PHASH_MAP)malloc(sizeof(HASH_MAP)); + PHASH_MAP s; + HASH_FIND_INT(delMap, &st_addr, s); + if (s == nullptr) { + s = (PHASH_MAP)malloc(sizeof(HASH_MAP)); s->key = st_addr; s->value = en_addr; HASH_ADD_INT(delMap, key, s); @@ -533,14 +536,22 @@ static int delete_dhcpd_rangeset(const char **pRsp, const char *pRequest) { } HASH_DEL(delMap, delRange); + free(delRange); + + free(cfig.dhcpRanges[i].options); + free(cfig.dhcpRanges[i].expiry); + free(cfig.dhcpRanges[i].dhcpEntry); + free(*(cfig.dhcpRanges[i].dhcpEntry)); } else { memcpy(&dhcpRanges[resCount], &cfig.dhcpRanges[i], sizeof(struct data13)); resCount++; + + cJSON_Delete(pdel_Item); } } //The input parameter does not exist - if (resCount > cfig.rangeCount - cJSON_GetArraySize(pdhcp_range)) { - auto *s = (PHASH_MAP)malloc(sizeof(HASH_MAP)); + do { + PHASH_MAP s; PHASH_MAP tmp; char saddr[128]; char eaddr[128]; @@ -558,8 +569,11 @@ static int delete_dhcpd_rangeset(const char **pRsp, const char *pRequest) { cJSON_AddNumberToObject(pdel_Item, "status", ERR_ITEM_UNEXISTS); cJSON_AddStringToObject(pdel_Item, "message", getErrorEnumDesc(ERR_ITEM_UNEXISTS)); cJSON_AddItemToArray(pdelArray, pdel_Item); + + HASH_DEL(delMap, s); + free(s); } - } + } while (0); //Rewrite cfig.dhcpRanges for (int i = 0; i < cfig.rangeCount; i++) { From c3039602618972602e12f719b4f56612ee85fe0c Mon Sep 17 00:00:00 2001 From: huangxin Date: Mon, 6 Mar 2023 09:11:42 +0800 Subject: [PATCH 06/31] =?UTF-8?q?OCT=201.=20=E7=AE=80=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- srcs/libs/misc/unique_instance.c | 1 - srcs/opendhcp183/query.cpp | 39 +++++++++++++++----------------- 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/srcs/libs/misc/unique_instance.c b/srcs/libs/misc/unique_instance.c index 6ca88b8..cbaa38e 100644 --- a/srcs/libs/misc/unique_instance.c +++ b/srcs/libs/misc/unique_instance.c @@ -5,7 +5,6 @@ #include #include #include -#include #include #include "user_errno.h" #include "misc.h" diff --git a/srcs/opendhcp183/query.cpp b/srcs/opendhcp183/query.cpp index 69467c9..77c5c9f 100644 --- a/srcs/opendhcp183/query.cpp +++ b/srcs/opendhcp183/query.cpp @@ -550,30 +550,27 @@ static int delete_dhcpd_rangeset(const char **pRsp, const char *pRequest) { } } //The input parameter does not exist - do { - PHASH_MAP s; - PHASH_MAP tmp; - char saddr[128]; - char eaddr[128]; - char del_range[256]; - memset(del_range, 0, 256); + PHASH_MAP s; + PHASH_MAP tmp; + char saddr[128]; + char eaddr[128]; + char del_range[256]; + memset(del_range, 0, 256); + HASH_ITER(hh, delMap, s, tmp) HASH_ITER(hh, delMap, s, tmp) { + cJSON *pdel_Item = cJSON_CreateObject(); + IP2String(saddr, ntohl(s->key)); + IP2String(eaddr, ntohl(s->value)); - HASH_ITER(hh, delMap, s, tmp) { - cJSON *pdel_Item = cJSON_CreateObject(); - IP2String(saddr, ntohl(s->key)); - IP2String(eaddr, ntohl(s->value)); + sprintf(del_range, "%s-%s", saddr, eaddr); + cJSON_AddStringToObject(pdel_Item, "dhcpRange", del_range); - sprintf(del_range, "%s-%s", saddr, eaddr); - cJSON_AddStringToObject(pdel_Item, "dhcpRange", del_range); + cJSON_AddNumberToObject(pdel_Item, "status", ERR_ITEM_UNEXISTS); + cJSON_AddStringToObject(pdel_Item, "message", getErrorEnumDesc(ERR_ITEM_UNEXISTS)); + cJSON_AddItemToArray(pdelArray, pdel_Item); - cJSON_AddNumberToObject(pdel_Item, "status", ERR_ITEM_UNEXISTS); - cJSON_AddStringToObject(pdel_Item, "message", getErrorEnumDesc(ERR_ITEM_UNEXISTS)); - cJSON_AddItemToArray(pdelArray, pdel_Item); - - HASH_DEL(delMap, s); - free(s); - } - } while (0); + HASH_DEL(delMap, s); + free(s); + } //Rewrite cfig.dhcpRanges for (int i = 0; i < cfig.rangeCount; i++) { From 32fec2621b0fbca4f71135bc5e532727c5508c00 Mon Sep 17 00:00:00 2001 From: dongwenze Date: Mon, 6 Mar 2023 16:34:07 +0800 Subject: [PATCH 07/31] =?UTF-8?q?OCT=201.=E4=BF=AE=E6=AD=A3=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E5=86=85=E5=AD=98=E6=B3=84=E6=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- srcs/opendhcp183/query.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/srcs/opendhcp183/query.cpp b/srcs/opendhcp183/query.cpp index 77c5c9f..f3cd660 100644 --- a/srcs/opendhcp183/query.cpp +++ b/srcs/opendhcp183/query.cpp @@ -798,8 +798,11 @@ void iptvCacheCb(void *UNUSED(pArg)) { // 新发现设备没有被上报过 if (!pTemp) { auto pCacheDev = (PIPTV_DEV_SET)malloc(sizeof(IPTV_DEV_SET)); + auto pRepDev = (PIPTV_DEV_SET)malloc(sizeof(IPTV_DEV_SET)); + memcpy(pCacheDev, pDev, sizeof(IPTV_DEV_SET)); - HASH_ADD_STR(report, iptvMAC, pCacheDev); + memcpy(pRepDev, pDev, sizeof(IPTV_DEV_SET)); + HASH_ADD_STR(report, iptvMAC, pRepDev); isReport = true; // 添加到缓存列表供后续查询 @@ -836,6 +839,11 @@ void iptvCacheCb(void *UNUSED(pArg)) { inet_http_post_async(pUrl, pStrPro, on_http_response_cb, (void *)pStrPro); } + HASH_ITER(hh, report, pDev, pTmp) { + HASH_DEL(report, pDev); + free(pDev); + } + uv_sleep(10); } while (true); } From 701d9ac1f05616e94df6e87745fa529f6f9a0b69 Mon Sep 17 00:00:00 2001 From: dongwenze Date: Tue, 7 Mar 2023 18:45:37 +0800 Subject: [PATCH 08/31] =?UTF-8?q?OCT=201.=E7=AE=80=E5=8C=96=E5=88=A0?= =?UTF-8?q?=E9=99=A4=E6=8E=A5=E5=8F=A3=E9=83=A8=E5=88=86=E5=8F=98=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- srcs/opendhcp183/query.cpp | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/srcs/opendhcp183/query.cpp b/srcs/opendhcp183/query.cpp index f3cd660..20b1087 100644 --- a/srcs/opendhcp183/query.cpp +++ b/srcs/opendhcp183/query.cpp @@ -467,10 +467,12 @@ static int delete_dhcpd_rangeset(const char **pRsp, const char *pRequest) { pdelArray = cJSON_CreateArray(); cJSON_AddItemToObject(pRspRoot, "rangeSet", pdelArray); + char start[128]; + char end[128]; + char del_range[256]; for (int i = 0; i < cJSON_GetArraySize(pdhcp_range); i++) { cJSON *pdel_Item = cJSON_CreateObject(); cJSON *pdelRange = cJSON_GetArrayItem(pdhcp_range, i); - char del_range[256]; if (!pdelRange) { continue; @@ -480,8 +482,6 @@ static int delete_dhcpd_rangeset(const char **pRsp, const char *pRequest) { MYDWORD st_addr; MYDWORD en_addr; - char start[128]; - char end[128]; mySplit(start, end, del_range, '-'); st_addr = htonl(inet_addr(start)); en_addr = htonl(inet_addr(end)); @@ -509,17 +509,14 @@ static int delete_dhcpd_rangeset(const char **pRsp, const char *pRequest) { for (int i = 0; i < cfig.rangeCount; i++) { cJSON *pdel_Item = cJSON_CreateObject(); PHASH_MAP delRange; - char del_range[256]; - char saddr[128]; - char eaddr[128]; memset(del_range, 0, 256); HASH_FIND_INT(delMap, &cfig.dhcpRanges[i].rangeStart, delRange); if (delRange != nullptr) { - IP2String(saddr, ntohl(delRange->key)); - IP2String(eaddr, ntohl(delRange->value)); + IP2String(start, ntohl(delRange->key)); + IP2String(end, ntohl(delRange->value)); - sprintf(del_range, "%s-%s", saddr, eaddr); + sprintf(del_range, "%s-%s", start, end); cJSON_AddStringToObject(pdel_Item, "dhcpRange", del_range); //Determine whether the input is correct or not if (delRange->value == cfig.dhcpRanges[i].rangeEnd) { @@ -527,6 +524,10 @@ static int delete_dhcpd_rangeset(const char **pRsp, const char *pRequest) { cJSON_AddStringToObject(pdel_Item, "message", getErrorEnumDesc(ERR_SUCCESS)); cJSON_AddItemToArray(pdelArray, pdel_Item); + free(cfig.dhcpRanges[i].options); + free(cfig.dhcpRanges[i].expiry); + free(cfig.dhcpRanges[i].dhcpEntry); + free(*(cfig.dhcpRanges[i].dhcpEntry)); } else { memcpy(&dhcpRanges[resCount], &cfig.dhcpRanges[i], sizeof(struct data13)); resCount++; @@ -537,11 +538,6 @@ static int delete_dhcpd_rangeset(const char **pRsp, const char *pRequest) { HASH_DEL(delMap, delRange); free(delRange); - - free(cfig.dhcpRanges[i].options); - free(cfig.dhcpRanges[i].expiry); - free(cfig.dhcpRanges[i].dhcpEntry); - free(*(cfig.dhcpRanges[i].dhcpEntry)); } else { memcpy(&dhcpRanges[resCount], &cfig.dhcpRanges[i], sizeof(struct data13)); resCount++; @@ -552,16 +548,13 @@ static int delete_dhcpd_rangeset(const char **pRsp, const char *pRequest) { //The input parameter does not exist PHASH_MAP s; PHASH_MAP tmp; - char saddr[128]; - char eaddr[128]; - char del_range[256]; memset(del_range, 0, 256); HASH_ITER(hh, delMap, s, tmp) HASH_ITER(hh, delMap, s, tmp) { cJSON *pdel_Item = cJSON_CreateObject(); - IP2String(saddr, ntohl(s->key)); - IP2String(eaddr, ntohl(s->value)); + IP2String(start, ntohl(s->key)); + IP2String(end, ntohl(s->value)); - sprintf(del_range, "%s-%s", saddr, eaddr); + sprintf(del_range, "%s-%s", start, end); cJSON_AddStringToObject(pdel_Item, "dhcpRange", del_range); cJSON_AddNumberToObject(pdel_Item, "status", ERR_ITEM_UNEXISTS); From 05838baa80f918dad91dca6c51ec345ade9ab36f Mon Sep 17 00:00:00 2001 From: dongwenze Date: Mon, 13 Mar 2023 15:46:31 +0800 Subject: [PATCH 09/31] =?UTF-8?q?OCT=201.=E5=A2=9E=E5=8A=A0JSON=E5=AD=97?= =?UTF-8?q?=E6=AE=B5=E8=A7=84=E8=8C=83=E6=A0=BC=E5=BC=8F=E6=A3=80=E6=9F=A5?= =?UTF-8?q?=202.=E7=AE=80=E5=8C=96=E5=88=A0=E9=99=A4=E5=9C=B0=E5=9D=80?= =?UTF-8?q?=E6=B1=A0=E6=8E=A5=E5=8F=A3=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- srcs/libs/include/proto.h | 8 +++++- srcs/libs/protocol/protocol.c | 53 +++++++++++++++++++++++++++++++++-- srcs/opendhcp183/query.cpp | 18 ++---------- 3 files changed, 60 insertions(+), 19 deletions(-) diff --git a/srcs/libs/include/proto.h b/srcs/libs/include/proto.h index 4291ef1..017dc6b 100644 --- a/srcs/libs/include/proto.h +++ b/srcs/libs/include/proto.h @@ -18,8 +18,14 @@ typedef enum { CRYPTO_AES256 = 4, } PROTO_CRYPTO_TYPE; +typedef enum { + TYPE_ADD_RNG = 0, + TYPE_DEL_RNG = 1, + TYPE_QUE_USR = 2, +} PROTO_POST_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, int *pErrCode); +const char *proto_decode_context(const char *pString, unsigned int *pVer, unsigned long long *pTm, PROTO_POST_TYPE type_id, int *pErrCode); #ifdef __cplusplus } #endif diff --git a/srcs/libs/protocol/protocol.c b/srcs/libs/protocol/protocol.c index eafda62..0d305ce 100644 --- a/srcs/libs/protocol/protocol.c +++ b/srcs/libs/protocol/protocol.c @@ -36,6 +36,12 @@ static JSON_SCHEMA_CTX g_json_sch[] = { {"{\"type\":\"object\",\"required\":[\"msgContent\"]}", "Missing required field [msgContent]"}, }; +static JSON_SCHEMA_CTX g_json_msg[] = { + {"{\"type\":\"object\",\"required\":[\"rangeSet\"],\"properties\":{\"rangeSet\":{\"type\":\"array\",\"minItems\":1,\"items\":{\"type\":\"object\",\"required\":[\"dhcpRange\"]}}}}", "Incorrect [rangeSet]"}, + {"{\"type\":\"object\",\"required\":[\"dhcpRange\"],\"properties\":{\"dhcpRange\":{\"type\":\"array\",\"minItems\":1}}}", "Incorrect [dhcpRange]"}, + {"{\"type\":\"object\",\"required\":[\"userMac\"],\"properties\":{\"userMac\":{\"type\":\"array\",\"minItems\":1}}}", "Incorrect [userMac]"}, +}; + const char *proto_schema_validation(const char *pJsonStr) { int i; @@ -72,10 +78,39 @@ const char *proto_schema_validation(const char *pJsonStr) { json_object_put(pJs); return NULL; } + +const char *proto_msg_validation(const char *pJsonStr, PROTO_POST_TYPE type_id) { + 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); + } + + json_object *pSc = json_tokener_parse(g_json_msg[type_id].pSchJson); + if (!pSc) { + LOG_MOD(error, ZLOG_MOD_PROTO, "Json schema format error: [%s]\n", g_json_msg[type_id].pSchJson); + } + + 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_msg[type_id].pErrMsg); + json_object_put(pSc); + json_object_put(pJs); + 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; +const char *proto_decode_context(const char *pString, unsigned int *pVer, + unsigned long long *pTm, PROTO_POST_TYPE type_id, int *pErrCode) { unsigned char *pBase64; int decodeSize; unsigned int outSize = 0; @@ -132,7 +167,19 @@ const char *proto_decode_context(const char *pString, unsigned int *pVer, unsign } } - pMsgCtx = cJSON_GetObjectItem(pRoot, "msgContent"); + cJSON *pMsgCtx = cJSON_GetObjectItem(pRoot, "msgContent"); + char *msgCont = cJSON_Print(pMsgCtx); + +#ifdef JSON_SCHEMA_ON + pSchJson = proto_msg_validation(msgCont, type_id); + free((void *)msgCont); + + if (pSchJson != NULL && strlen(pSchJson) > 0) { + *pErrCode = ERR_JSON_VALID_SCH; + cJSON_Delete(pRoot); + return pSchJson; + } +#endif if (!pMsgCtx) { cJSON_Delete(pRoot); diff --git a/srcs/opendhcp183/query.cpp b/srcs/opendhcp183/query.cpp index 20b1087..4814432 100644 --- a/srcs/opendhcp183/query.cpp +++ b/srcs/opendhcp183/query.cpp @@ -56,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, &errCode); + pStrContent = proto_decode_context(pRequest, nullptr, nullptr, TYPE_QUE_USR, &errCode); if (pStrContent == nullptr) { sprintf(logBuff, "Requeset Json error %s", pRequest); @@ -334,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, &errCode); + pStrContent = proto_decode_context(pRequest, nullptr, nullptr, TYPE_ADD_RNG, &errCode); if (pStrContent == nullptr) { sprintf(logBuff, "Requeset Json error %s", pRequest); @@ -441,7 +441,7 @@ static int delete_dhcpd_rangeset(const char **pRsp, const char *pRequest) { return ERR_INPUT_PARAMS; } - pStrContent = proto_decode_context(pRequest, nullptr, nullptr, &errCode); + pStrContent = proto_decode_context(pRequest, nullptr, nullptr, TYPE_DEL_RNG, &errCode); if (pStrContent == nullptr) { sprintf(logBuff, "Requeset Json error %s", pRequest); logDHCPMess(logBuff, 1); @@ -471,7 +471,6 @@ static int delete_dhcpd_rangeset(const char **pRsp, const char *pRequest) { char end[128]; char del_range[256]; for (int i = 0; i < cJSON_GetArraySize(pdhcp_range); i++) { - cJSON *pdel_Item = cJSON_CreateObject(); cJSON *pdelRange = cJSON_GetArrayItem(pdhcp_range, i); if (!pdelRange) { @@ -486,16 +485,6 @@ static int delete_dhcpd_rangeset(const char **pRsp, const char *pRequest) { st_addr = htonl(inet_addr(start)); en_addr = htonl(inet_addr(end)); - if (st_addr > en_addr) { - cJSON_AddStringToObject(pdel_Item, "dhcpRange", del_range); - cJSON_AddNumberToObject(pdel_Item, "status", ERR_INPUT_PARAMS); - cJSON_AddStringToObject(pdel_Item, "message", getErrorEnumDesc(ERR_INPUT_PARAMS)); - cJSON_AddItemToArray(pdelArray, pdel_Item); - break; - } else { - cJSON_Delete(pdel_Item); - } - PHASH_MAP s; HASH_FIND_INT(delMap, &st_addr, s); if (s == nullptr) { @@ -527,7 +516,6 @@ static int delete_dhcpd_rangeset(const char **pRsp, const char *pRequest) { free(cfig.dhcpRanges[i].options); free(cfig.dhcpRanges[i].expiry); free(cfig.dhcpRanges[i].dhcpEntry); - free(*(cfig.dhcpRanges[i].dhcpEntry)); } else { memcpy(&dhcpRanges[resCount], &cfig.dhcpRanges[i], sizeof(struct data13)); resCount++; From f262e748ea5718e7759eb78bf05f1f7313b7aa1e Mon Sep 17 00:00:00 2001 From: huangxin Date: Mon, 13 Mar 2023 17:03:02 +0800 Subject: [PATCH 10/31] =?UTF-8?q?OCT=201.=20=E4=BC=98=E5=8C=96vCPE=20BRAS?= =?UTF-8?q?=E5=90=8E=E7=BA=BF=E7=A8=8Blinux=E7=B3=BB=E7=BB=9F=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E6=8F=8F=E8=BF=B0=E7=AC=A6fd=E8=B5=84=E6=BA=90?= =?UTF-8?q?=E5=8D=A0=E7=94=A8=201100+->300+?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 16 +++++++-- srcs/CMakeLists.txt | 2 +- srcs/libs/CMakeLists.txt | 4 ++- srcs/libs/configure/config.c | 2 ++ srcs/libs/configure/config_help.c | 4 ++- srcs/libs/hardware/cpu.c | 27 +++------------ srcs/libs/hardware/disk.c | 26 +++----------- srcs/libs/hardware/hardware.c | 57 +++++++++++++++---------------- srcs/libs/hardware/ipmltools.c | 42 +++++++++-------------- srcs/libs/hardware/memory.c | 32 +++++------------ srcs/libs/include/config.h | 2 ++ srcs/libs/init/init_runtime.c | 5 ++- 12 files changed, 90 insertions(+), 129 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c4c0eb5..bdfefa9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ CMAKE_MINIMUM_REQUIRED(VERSION 3.10 FATAL_ERROR) INCLUDE(CMakeDependentOption) OPTION(VCPE_AGENT "Enable vCPE agent test application" OFF) -OPTION(USED_LWIP "PPPoE of LWIP support for vCPE" OFF) +OPTION(VCPE_PPPOE "Enable vCPE work befof PPPoE mode" OFF) OPTION(USED_OPENDHCPD "DHCP server for vCPE" OFF) OPTION(USED_OPENDHCPDDNS "DHCP And DNS server for vCPE" OFF) OPTION(USED_USER_VNI "Support pass user vni id from console command line" OFF) @@ -18,6 +18,8 @@ INCLUDE(system_libs) SET(VCPE_VERSION "1.0.1") PROJECT(vCPE VERSION "${VCPE_VERSION}") CMAKE_DEPENDENT_OPTION(USED_HTTP_SVR "Build-in http(s) server support" ON "USED_OPENDHCPD OR USED_OPENDHCPDDNS" OFF) +CMAKE_DEPENDENT_OPTION(USED_LWIP "PPPoE of LWIP support for vCPE" ON "VCPE_PPPOE" OFF) +CMAKE_DEPENDENT_OPTION(USED_ZMQ "ZeroMQ support for vCPE" ON "VCPE_PPPOE" OFF) # 数据库开关 OPTION(USED_REDIS "Add redis database support for vCPE" OFF) @@ -84,7 +86,7 @@ INCLUDE(./depend/third_libs.cmake) IF (USED_JSON_VALIDATE) LIST(APPEND COMMON_DEFINE "-DJSON_SCHEMA_ON") -ENDIF() +ENDIF () IF (USED_USER_VNI) LIST(APPEND COMMON_DEFINE "-DUSERVNI_ON") @@ -96,6 +98,16 @@ IF (USED_HTTP_SVR) MESSAGE("Select Option USED_HTTP_SVR") ENDIF () +IF (USED_ZMQ) + LIST(APPEND COMMON_DEFINE "-DZEROMQ_ON") + MESSAGE("Select Option USED_ZMQ") +ENDIF () + +IF (VCPE_PPPOE) + LIST(APPEND COMMON_DEFINE "-DPPPOE_ON") + MESSAGE("Select Option VCPE_PPPOE") +ENDIF () + IF (USED_LWIP) LIST(APPEND COMMON_DEFINE "-DLWIP_ON") MESSAGE("Select Option USED_LWIP") diff --git a/srcs/CMakeLists.txt b/srcs/CMakeLists.txt index c337c5b..f26eeef 100644 --- a/srcs/CMakeLists.txt +++ b/srcs/CMakeLists.txt @@ -16,7 +16,7 @@ SET(CMAKE_C_STANDARD 99) FILE(GLOB VCPE_HEADS include/*.h include/uthash/*.h include/s2j/*.h ./httpserver/include/*.h ./httpserver/src/haywire/*.h ./httpserver/src/haywire/configuration/*.h) -IF (USED_LWIP) +IF (VCPE_PPPOE) AUX_SOURCE_DIRECTORY(pppoe VCPE_SRC) AUX_SOURCE_DIRECTORY(user VCPE_SRC) ENDIF () diff --git a/srcs/libs/CMakeLists.txt b/srcs/libs/CMakeLists.txt index faaf2cd..4ca2b34 100644 --- a/srcs/libs/CMakeLists.txt +++ b/srcs/libs/CMakeLists.txt @@ -17,7 +17,9 @@ AUX_SOURCE_DIRECTORY(banner C_SRC) AUX_SOURCE_DIRECTORY(configure C_SRC) AUX_SOURCE_DIRECTORY(network C_SRC) AUX_SOURCE_DIRECTORY(task C_SRC) -AUX_SOURCE_DIRECTORY(mq C_SRC) +IF (USED_ZMQ) + AUX_SOURCE_DIRECTORY(mq C_SRC) +ENDIF () AUX_SOURCE_DIRECTORY(cmdline C_SRC) AUX_SOURCE_DIRECTORY(crypto C_SRC) AUX_SOURCE_DIRECTORY(hardware C_SRC) diff --git a/srcs/libs/configure/config.c b/srcs/libs/configure/config.c index 2500260..bd68487 100644 --- a/srcs/libs/configure/config.c +++ b/srcs/libs/configure/config.c @@ -98,10 +98,12 @@ static CFG_ITEM g_cfgItem[] = { DEF_CFG_ITEM(CFG_DB_SQLITE_DB_NAME, "database.sqlite_dbname", VAL_STR, "", "SQLite3 database file name"), DEF_CFG_ITEM(CFG_DB_SQLITE_PASSWD, "database.sqlite_passwd", VAL_STR, ".main", "SQLite3 database password"), #endif +#ifdef ZEROMQ_ON /* 消息队列相配置 */ /* ZeroMq配置 */ DEF_CFG_ITEM(CFG_MQ_SVR_PORT, "zero_mq.svr_port", VAL_INT, "6278", "ZeroMQ server port"), DEF_CFG_ITEM(CFG_MQ_DATA_PATH, "zero_mq.agent_addr", VAL_STR, "ipc:///tmp/msg_fifo0", "ZeroMQ Agent data path"), +#endif /* vxLan 隧道配置 */ DEF_CFG_ITEM(CFG_VXLAN_NIC_NAME, "vxlan_wan.nic", VAL_STR, "", "Network card name to send data"), DEF_CFG_ITEM(CFG_VXLAN_SUPPORT, "vxlan_wan.enable", VAL_BOOL, "1", "Is support vxLan tune"), diff --git a/srcs/libs/configure/config_help.c b/srcs/libs/configure/config_help.c index 218229c..49670a7 100644 --- a/srcs/libs/configure/config_help.c +++ b/srcs/libs/configure/config_help.c @@ -183,10 +183,12 @@ const char *cfg_get_mysql_database() { } #endif +#ifdef ZEROMQ_ON int cfg_get_zero_mq_port() { return (unsigned short)cfg_get_integral_value(CFG_MQ_SVR_PORT); } const char *cfg_get_zero_mq_data_path() { return cfg_get_string_value(CFG_MQ_DATA_PATH); -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/srcs/libs/hardware/cpu.c b/srcs/libs/hardware/cpu.c index ae080ef..9502d91 100644 --- a/srcs/libs/hardware/cpu.c +++ b/srcs/libs/hardware/cpu.c @@ -20,7 +20,7 @@ typedef struct { #define CMD_CPU_CORE ("cat /proc/cpuinfo| grep \"cpu cores\" | uniq | awk \'{print $4}\'") #define CMP_CPU_LOGIC_CORE ("cat /proc/cpuinfo| grep \"cpu cores\" | wc -l") -#define SUM_CPU_TIME(x) ((x).user + (x).nice + (x).sys + (x).idle + (x).irq) +#define SUM_CPU_TIME(x) ((x).user + (x).nice + (x).sys + (x).idle + (x).irq) static int g_isInit = FALSE; static CPU_INFO g_cpuInfo; @@ -103,7 +103,7 @@ static int get_cpu_time_info(unsigned long *pTotal, unsigned long *pIdle) { *pTotal = *pIdle = 0; for (i = 0; i < nCpu; i++) { - *pIdle += pCpu[i].cpu_times.idle; + *pIdle += pCpu[i].cpu_times.idle; *pTotal += SUM_CPU_TIME(pCpu[i].cpu_times); } @@ -135,25 +135,7 @@ static void cpuUsedRefresh() { } } -_Noreturn void cpuCalcCb(void *UNUSED(pArg)) { - do { - unsigned int period = cfg_get_cpu_refresh_period(); - - if (cfg_get_watch_cpu()) { - cpuUsedRefresh(); - } - - if (period < REFRESH_MAX_PERIOD && period >= 1) { - uv_sleep(1000 * period); - } else { - uv_sleep(1000); - } - } while (TRUE); -} - int cpu_watch_init() { - static uv_thread_t uvThread; - memset(&g_cpuInfo, 0, sizeof(CPU_INFO)); g_cpuInfo.nCpus = get_cpu_number(); @@ -163,9 +145,6 @@ int cpu_watch_init() { get_cpu_desc(&g_cpuInfo.cpuCoreDesc); g_isInit = TRUE; - - uv_thread_create(&uvThread, cpuCalcCb, NULL); - return ERR_SUCCESS; } @@ -184,6 +163,8 @@ int get_cpu_info(PCPU_INFO pInfo) { get_cpu_desc(&g_cpuInfo.cpuCoreDesc); g_isInit = TRUE; + } else if (cfg_get_watch_cpu()) { + cpuUsedRefresh(); } memcpy(pInfo, &g_cpuInfo, sizeof(CPU_INFO)); diff --git a/srcs/libs/hardware/disk.c b/srcs/libs/hardware/disk.c index c5fee99..da61056 100644 --- a/srcs/libs/hardware/disk.c +++ b/srcs/libs/hardware/disk.c @@ -12,7 +12,7 @@ #include "uthash/uthash.h" #include "config.h" -#define MAX_SIZE_LEN (16) +#define MAX_SIZE_LEN (16) //#define CMD_DISK_FILESYSTEM ("df -h | awk \'{if (NR > 1){print $0}}\'") #define CMD_DISK_FILESYSTEM ("df -h | awk \'{if (NR > 1){print $1\"|\"$2\"|\"$3\"|\"$4\"|\"$5\"|\"$6}}\'") @@ -105,6 +105,10 @@ int get_disk_info(PDISK_INFO pInfo) { return -ERR_INPUT_PARAMS; } + if (cfg_get_watch_disk()) { + disk_info_refresh(); + } + uv_rwlock_rdlock(&g_uvLock); pInfo->nItems = HASH_COUNT(g_diskPartInfo); pInfo->timestamp = time(NULL); @@ -122,32 +126,12 @@ int get_disk_info(PDISK_INFO pInfo) { return ERR_SUCCESS; } -_Noreturn void diskRefreshCb(void *UNUSED(pArg)) { - do { - unsigned int period = cfg_get_disk_refresh_period(); - - if (cfg_get_watch_disk()) { - disk_info_refresh(); - } - - if (period < REFRESH_MAX_PERIOD && period >= 1) { - uv_sleep(1000 * period); - } else { - uv_sleep(1000); - } - } while (TRUE); -} - int disk_watch_info() { - static uv_thread_t uvThread; - uv_rwlock_init(&g_uvLock); if (disk_info_refresh() != ERR_SUCCESS) { return -ERR_SYS_DISK_GET_INFO; } - uv_thread_create(&uvThread, diskRefreshCb, NULL); - return ERR_SUCCESS; } \ No newline at end of file diff --git a/srcs/libs/hardware/hardware.c b/srcs/libs/hardware/hardware.c index cafa018..6b941b9 100644 --- a/srcs/libs/hardware/hardware.c +++ b/srcs/libs/hardware/hardware.c @@ -9,51 +9,48 @@ #include "user_errno.h" #include "config.h" #include "misc.h" +#include "task_manager.h" static HARDWARE_INFO g_hardwareInfo; -_Noreturn void hardwareRefreshCb(void *UNUSED(pArg)) { - do { - unsigned int period = cfg_get_hardware_refresh_period(); +void hardwareRefreshCb(uv_timer_t *UNUSED(pArg)) { + if (cfg_get_watch_sensor()) { + get_sensor_info(&g_hardwareInfo.sensorInfo); + } + if (cfg_get_watch_disk()) { + get_disk_info(&g_hardwareInfo.diskInfo); + } + if (cfg_get_watch_cpu()) { + get_cpu_info(&g_hardwareInfo.cpuInfo); + } + if (cfg_get_watch_memory()) { + get_memory_info(&g_hardwareInfo.memInfo); + } - if (cfg_get_watch_sensor()) { - get_sensor_info(&g_hardwareInfo.sensorInfo); - } - - if (cfg_get_watch_disk()) { - get_disk_info(&g_hardwareInfo.diskInfo); - } - - if (cfg_get_watch_cpu()) { - get_cpu_info(&g_hardwareInfo.cpuInfo); - } - - if (cfg_get_watch_memory()) { - get_memory_info(&g_hardwareInfo.memInfo); - } - - if (period < REFRESH_MAX_PERIOD && period >= 1) { - uv_sleep(1000 * period); - } else { - uv_sleep(1000); - } - - uv_sleep(1000); - } while (TRUE); + printf("%s\n", get_hardware_json()); } int init_hardware() { - static uv_thread_t uvThread; + static uv_timer_t uvTm; + unsigned int period = cfg_get_hardware_refresh_period(); + + if (period >= REFRESH_MAX_PERIOD) { + period = 10000; + } else { + period *= 1000; + } + + uv_timer_init(get_task_manager(), &uvTm); memset(&g_hardwareInfo, 0, sizeof(HARDWARE_INFO)); - uv_thread_create(&uvThread, hardwareRefreshCb, NULL); - cpu_watch_init(); memory_watch_init(); disk_watch_info(); sensor_watch_init(); + uv_timer_start(&uvTm, hardwareRefreshCb, period, period); + return ERR_SUCCESS; } diff --git a/srcs/libs/hardware/ipmltools.c b/srcs/libs/hardware/ipmltools.c index 57bb077..13b84b3 100644 --- a/srcs/libs/hardware/ipmltools.c +++ b/srcs/libs/hardware/ipmltools.c @@ -12,10 +12,11 @@ #include "misc.h" #include "config.h" -#define MAX_SENSOR_STR (32) +#define MAX_SENSOR_STR (32) //#define CMD_SENSOR_INFO ("ipmitool sensor | awk '{gsub(/\|/, \"\"); print $0}'") -#define CMD_SENSOR_INFO ("ipmitool sensor") +#define CMD_SENSOR_INFO ("ipmitool sensor") +#define CMD_IPMITOOL_ENABLE ("whereis ipmitool | awk '{print $2}'") typedef struct { char name[MAX_SENSOR_STR]; ///< 传感器名称 @@ -42,9 +43,16 @@ static int sensor_info_refresh() { int errCode = ERR_SUCCESS; FILE *fp; char buf[1024]; + char *pRet = NULL; + + if (shell_with_output(CMD_IPMITOOL_ENABLE, &pRet) != ERR_SUCCESS || pRet == NULL || strlen(pRet) == 0) { + if (pRet) { + free(pRet); + } + return -ERR_ITEM_UNEXISTS; + } fp = popen(CMD_SENSOR_INFO, "r"); - if (fp == NULL) { return -ERR_OPEN_FILE; } @@ -132,6 +140,10 @@ int get_sensor_info(PSENSOR_INFO pInfo) { return -ERR_INPUT_PARAMS; } + if (cfg_get_watch_sensor()) { + sensor_info_refresh(); + } + uv_rwlock_rdlock(&g_uvLock); pInfo->nItems = HASH_COUNT(g_pSensorInfo); pInfo->timestamp = time(NULL); @@ -149,27 +161,9 @@ int get_sensor_info(PSENSOR_INFO pInfo) { return ERR_SUCCESS; } -_Noreturn void sensorRefreshCb(void *UNUSED(pArg)) { - do { - unsigned int period = cfg_get_sensor_refresh_period(); - - if (cfg_get_watch_sensor()) { - sensor_info_refresh(); - } - - if (period < REFRESH_MAX_PERIOD && period >= 1) { - uv_sleep(1000 * period); - } else { - uv_sleep(1000); - } - } while (TRUE); -} - int sensor_watch_init() { - - static uv_thread_t uvThread; - int i, devOk = FALSE; - int n = ARRAY_SIZE(g_ipmiDev); + int i, devOk = FALSE; + int n = ARRAY_SIZE(g_ipmiDev); uv_rwlock_init(&g_uvLock); @@ -188,7 +182,5 @@ int sensor_watch_init() { return -ERR_SYS_SENSOR_GET_INFO; } - uv_thread_create(&uvThread, sensorRefreshCb, NULL); - return ERR_SUCCESS; } \ No newline at end of file diff --git a/srcs/libs/hardware/memory.c b/srcs/libs/hardware/memory.c index 5d5cc9a..e777a1a 100644 --- a/srcs/libs/hardware/memory.c +++ b/srcs/libs/hardware/memory.c @@ -14,11 +14,11 @@ #define CMD_MEMORY_INFO ("cat /proc/meminfo | awk \'{print $1\"|\"$2\"|\"$3}\'") -#define M_SIZE (1024 * 1024) +#define M_SIZE (1024 * 1024) -#define KEY_NAME (32) -#define KEY_VALUE (32) -#define VALUE_UNIT (8) +#define KEY_NAME (32) +#define KEY_VALUE (32) +#define VALUE_UNIT (8) typedef struct { char itemName[KEY_NAME]; @@ -95,33 +95,13 @@ static int memory_info_refresh() { return errCode; } -_Noreturn void memRefreshCb(void *UNUSED(pArg)) { - do { - unsigned int period = cfg_get_mem_refresh_period(); - - if (cfg_get_watch_memory()) { - memory_info_refresh(); - } - - if (period < REFRESH_MAX_PERIOD && period >= 1) { - uv_sleep(1000 * period); - } else { - uv_sleep(1000); - } - } while (TRUE); -} - int memory_watch_init() { - static uv_thread_t uvThread; - uv_rwlock_init(&g_uvLock); if (memory_info_refresh() != ERR_SUCCESS) { return -ERR_SYS_DISK_GET_INFO; } - uv_thread_create(&uvThread, memRefreshCb, NULL); - return ERR_SUCCESS; } @@ -142,6 +122,10 @@ int get_memory_info(PMEMORY_INFO pInfo) { return -ERR_INPUT_PARAMS; } + if (cfg_get_watch_memory()) { + memory_info_refresh(); + } + uv_rwlock_rdlock(&g_uvLock); for (i = 0; i < n; i++) { diff --git a/srcs/libs/include/config.h b/srcs/libs/include/config.h index 8d8f40b..1730a78 100644 --- a/srcs/libs/include/config.h +++ b/srcs/libs/include/config.h @@ -61,8 +61,10 @@ typedef enum { CFG_DB_SQLITE_DB_NAME, CFG_DB_SQLITE_PASSWD, #endif +#ifdef ZEROMQ_ON CFG_MQ_SVR_PORT, CFG_MQ_DATA_PATH, +#endif CFG_VXLAN_NIC_NAME, CFG_VXLAN_SUPPORT, CFG_VXLAN_PEER_IP, diff --git a/srcs/libs/init/init_runtime.c b/srcs/libs/init/init_runtime.c index 19660fe..a72dd56 100644 --- a/srcs/libs/init/init_runtime.c +++ b/srcs/libs/init/init_runtime.c @@ -134,7 +134,7 @@ int user_init(const char *pAppCfgFile, const char *pCfgDirectory, const char *pK if (cfg_get_hardware_watch_enable()) { init_hardware(); } - +#ifdef ZEROMQ_ON if ((ret = mq_init()) != ERR_SUCCESS) { LOG_MOD(error, ZLOG_MOD_INIT, "Message queue init error: %d\n", ret); } @@ -142,6 +142,7 @@ int user_init(const char *pAppCfgFile, const char *pCfgDirectory, const char *pK if ((ret = mq_data_init()) != ERR_SUCCESS) { LOG_MOD(error, ZLOG_MOD_INIT, "Message queue init error: %d\n", ret); } +#endif #ifdef HTTPSERVER_ON http_svr_init(); #endif @@ -156,7 +157,9 @@ void user_uninit() { #ifdef HTTPSERVER_ON http_svr_uinit(); #endif +#ifdef ZEROMQ_ON mq_uninit(); +#endif zlog_fini(); uninit_config_system(); uv_loop_close(get_task_manager()); From cf5375c8931ecf73fd92f8b68577c4b52a9be49d Mon Sep 17 00:00:00 2001 From: huangxin Date: Mon, 13 Mar 2023 17:26:54 +0800 Subject: [PATCH 11/31] =?UTF-8?q?OCT=201.=20=E7=A7=BB=E9=99=A4=E5=A4=9A?= =?UTF-8?q?=E4=BD=99=E7=A1=AC=E4=BB=B6=E7=9B=91=E6=8E=A7=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/vcpe.cfg | 5 ----- srcs/libs/configure/config.c | 5 ----- srcs/libs/configure/config_help.c | 16 ---------------- srcs/libs/include/config.h | 10 ++-------- 4 files changed, 2 insertions(+), 34 deletions(-) diff --git a/config/vcpe.cfg b/config/vcpe.cfg index 319ff48..6b9f483 100644 --- a/config/vcpe.cfg +++ b/config/vcpe.cfg @@ -24,11 +24,6 @@ application: memory = true; # 是否开启内存监控 disk = true; # 是否开启磁盘空间占用监控 sensor = true; # 是否开启传感器状态监控 - - cpu_refresh = 1; # CPU 状态刷新频率(秒) - mem_refresh = 10; # 内存状态刷新频率(秒) - disk_refresh = 10; # 磁盘状态刷新频率(秒) - sensor_refresh = 10; # 传感器状态刷新频率(秒) }; # 数据库相关配置 diff --git a/srcs/libs/configure/config.c b/srcs/libs/configure/config.c index bd68487..abe896a 100644 --- a/srcs/libs/configure/config.c +++ b/srcs/libs/configure/config.c @@ -73,11 +73,6 @@ static CFG_ITEM g_cfgItem[] = { DEF_CFG_ITEM(CFG_WATCH_MEMORY, "watch_params.memory", VAL_BOOL, "1", "Monitor memory information"), DEF_CFG_ITEM(CFG_WATCH_DISK, "watch_params.disk", VAL_BOOL, "1", "Monitor disk partition information"), DEF_CFG_ITEM(CFG_WATCH_SENSOR, "watch_params.sensor", VAL_BOOL, "1", "Sensor information refresh frequency"), - /* 系统监控设备刷频率 */ - DEF_CFG_ITEM(CFG_CPU_REFRESH, "watch_params.cpu_refresh", VAL_INT, "10", "CPU information refresh frequency"), - DEF_CFG_ITEM(CFG_MEM_REFRESH, "watch_params.mem_refresh", VAL_INT, "10", "Memory information refresh frequency"), - DEF_CFG_ITEM(CFG_DISK_REFRESH, "watch_params.disk_refresh", VAL_INT, "10", "Disk information refresh frequency"), - DEF_CFG_ITEM(CFG_SENSOR_REFRESH, "watch_params.sensor_refresh", VAL_INT, "10", "Sensor information refresh frequency"), /* 数据库相配置 */ /* Redis配置 */ #ifdef USED_REDIS diff --git a/srcs/libs/configure/config_help.c b/srcs/libs/configure/config_help.c index 49670a7..1f7c22a 100644 --- a/srcs/libs/configure/config_help.c +++ b/srcs/libs/configure/config_help.c @@ -121,22 +121,6 @@ int cfg_get_watch_sensor() { return cfg_get_bool_value(CFG_WATCH_SENSOR); } -unsigned int cfg_get_cpu_refresh_period() { - return cfg_get_integral_value(CFG_CPU_REFRESH); -} - -unsigned int cfg_get_mem_refresh_period() { - return cfg_get_integral_value(CFG_MEM_REFRESH); -} - -unsigned int cfg_get_disk_refresh_period() { - return cfg_get_integral_value(CFG_DISK_REFRESH); -} - -unsigned int cfg_get_sensor_refresh_period() { - return cfg_get_integral_value(CFG_SENSOR_REFRESH); -} - #ifdef USED_SQLITE const char *cfg_get_sqlite_db_name() { return cfg_get_string_value(CFG_DB_SQLITE_DB_NAME); diff --git a/srcs/libs/include/config.h b/srcs/libs/include/config.h index 1730a78..687e981 100644 --- a/srcs/libs/include/config.h +++ b/srcs/libs/include/config.h @@ -41,10 +41,6 @@ typedef enum { CFG_WATCH_MEMORY, CFG_WATCH_DISK, CFG_WATCH_SENSOR, - CFG_CPU_REFRESH, - CFG_MEM_REFRESH, - CFG_DISK_REFRESH, - CFG_SENSOR_REFRESH, #ifdef USED_REDIS CFG_DB_REDIS_SERVER, CFG_DB_REDIS_PORT, @@ -94,10 +90,6 @@ int cfg_get_watch_cpu(); int cfg_get_watch_memory(); int cfg_get_watch_disk(); int cfg_get_watch_sensor(); -unsigned int cfg_get_cpu_refresh_period(); -unsigned int cfg_get_mem_refresh_period(); -unsigned int cfg_get_disk_refresh_period(); -unsigned int cfg_get_sensor_refresh_period(); #ifdef USED_REDIS const char *cfg_get_redis_server(); int cfg_get_redis_port(); @@ -114,8 +106,10 @@ const char *cfg_get_mysql_database(); const char *cfg_get_sqlite_db_name(); const char *cfg_get_sqlite_passwd(); #endif +#ifdef ZEROMQ_ON int cfg_get_zero_mq_port(); const char *cfg_get_zero_mq_data_path(); +#endif const char *cfg_get_string_value(CONFIG_ITEM_ID id); c_vector cfg_get_vector(CONFIG_ITEM_ID id); From 789ddbc71638528d459f2bce4e9e728456025149 Mon Sep 17 00:00:00 2001 From: huangxin Date: Mon, 13 Mar 2023 17:33:59 +0800 Subject: [PATCH 12/31] =?UTF-8?q?OCT=201.=20=E4=BC=98=E5=8C=96vCPE=20BRAS?= =?UTF-8?q?=E5=90=8E=E7=BA=BF=E7=A8=8Blinux=E7=B3=BB=E7=BB=9F=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E6=8F=8F=E8=BF=B0=E7=AC=A6fd=E8=B5=84=E6=BA=90?= =?UTF-8?q?=E5=8D=A0=E7=94=A8=20340+->264=202.=20=E5=88=A0=E9=99=A4?= =?UTF-8?q?=E5=A4=9A=E4=BD=99=E7=A1=AC=E4=BB=B6=E4=BF=A1=E6=81=AF=E8=B0=83?= =?UTF-8?q?=E8=AF=95=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- srcs/libs/hardware/hardware.c | 2 +- srcs/libs/init/init_runtime.c | 4 +- srcs/opendhcp183/query.cpp | 123 +++++++++++++++++----------------- 3 files changed, 65 insertions(+), 64 deletions(-) diff --git a/srcs/libs/hardware/hardware.c b/srcs/libs/hardware/hardware.c index 6b941b9..3f12302 100644 --- a/srcs/libs/hardware/hardware.c +++ b/srcs/libs/hardware/hardware.c @@ -27,7 +27,7 @@ void hardwareRefreshCb(uv_timer_t *UNUSED(pArg)) { get_memory_info(&g_hardwareInfo.memInfo); } - printf("%s\n", get_hardware_json()); + //printf("%s\n", get_hardware_json()); } int init_hardware() { diff --git a/srcs/libs/init/init_runtime.c b/srcs/libs/init/init_runtime.c index a72dd56..a0f99f3 100644 --- a/srcs/libs/init/init_runtime.c +++ b/srcs/libs/init/init_runtime.c @@ -14,11 +14,13 @@ #include "inet_misc.h" #include "crypto.h" #include "hardware.h" -#include "msg_queue.h" #include "http_svr.h" #include "lib_config.h" #include "prj_config.h" #include "zlog_module.h" +#ifdef ZEROMQ_ON +#include "msg_queue.h" +#endif #define DEFAULT_CONFIG_FILE ("vcpe.cfg") #define DEFAULT_CONFIG_DIR ("config") diff --git a/srcs/opendhcp183/query.cpp b/srcs/opendhcp183/query.cpp index 4814432..fe5e883 100644 --- a/srcs/opendhcp183/query.cpp +++ b/srcs/opendhcp183/query.cpp @@ -25,6 +25,7 @@ using namespace std; #include "sds/sds.h" #include "inet_misc.h" #include "http_svr.h" +#include "task_manager.h" extern data2 cfig; extern bool kRunning; @@ -341,7 +342,7 @@ static int add_dhcpd_rangeset(const char **pRsp, const char *pRequest) { logDHCPMess(logBuff, 1); return ERR_PROTO_DECODE; } - + #ifdef JSON_SCHEMA_ON if (errCode == ERR_JSON_VALID_SCH) { *pRsp = pStrContent; @@ -488,7 +489,7 @@ static int delete_dhcpd_rangeset(const char **pRsp, const char *pRequest) { PHASH_MAP s; HASH_FIND_INT(delMap, &st_addr, s); if (s == nullptr) { - s = (PHASH_MAP)malloc(sizeof(HASH_MAP)); + s = (PHASH_MAP)malloc(sizeof(HASH_MAP)); s->key = st_addr; s->value = en_addr; HASH_ADD_INT(delMap, key, s); @@ -765,68 +766,63 @@ static void on_http_response_cb(void *pData, free(pUserData); } -void iptvCacheCb(void *UNUSED(pArg)) { - do { - bool isReport = false; - const char *pUrl = config_get_agent_iptv_report_url(); - PIPTV_DEV_SET report = nullptr; - PIPTV_DEV_SET pDev, pTmp = nullptr; - uv_rwlock_wrlock(&g_uvCacheLock); - HASH_ITER(hh, g_iptvNewDevs, pDev, pTmp) { - PIPTV_DEV_SET pTemp; - const char *pDevMac = strdup(pDev->iptvMAC); - HASH_FIND_STR(g_iptvCacheDevs, pDevMac, pTemp); - // 新发现设备没有被上报过 - if (!pTemp) { - auto pCacheDev = (PIPTV_DEV_SET)malloc(sizeof(IPTV_DEV_SET)); - auto pRepDev = (PIPTV_DEV_SET)malloc(sizeof(IPTV_DEV_SET)); - - memcpy(pCacheDev, pDev, sizeof(IPTV_DEV_SET)); - memcpy(pRepDev, pDev, sizeof(IPTV_DEV_SET)); - HASH_ADD_STR(report, iptvMAC, pRepDev); - - isReport = true; - // 添加到缓存列表供后续查询 - HASH_ADD_STR(g_iptvCacheDevs, iptvMAC, pCacheDev); - LOG_MOD(debug, - ZLOG_MOD_OPENDHCPD, - "Add IPTV device %s vni %d to cache\n", - pCacheDev->iptvMAC, - pCacheDev->vni); - } - - HASH_DEL(g_iptvNewDevs, pDev); - free(pDev); - free((void *)pDevMac); - } - uv_rwlock_wrunlock(&g_uvCacheLock); - - if (isReport && pUrl && strlen(pUrl) > 0) { - cJSON *pRspMsg = cJSON_CreateObject(); - cJSON *pMsgArray = cJSON_CreateArray(); - cJSON_AddItemToObject(pRspMsg, "iptvDevs", pMsgArray); - - HASH_ITER(hh, report, pDev, pTmp) { - cJSON *pRspItem = cJSON_CreateObject(); - cJSON_AddStringToObject(pRspItem, "mac", pDev->iptvMAC); - cJSON_AddNumberToObject(pRspItem, "vni", pDev->vni); - pDev->isReport = 1; - cJSON_AddItemToArray(pMsgArray, pRspItem); - } - - const char *pStrPro = proto_create_new(pRspMsg, 200); - - // Report new IPTV device MAC - inet_http_post_async(pUrl, pStrPro, on_http_response_cb, (void *)pStrPro); - } +void iptvCacheCb(uv_timer_t *UNUSED(pArg)) { + bool isReport = false; + const char *pUrl = config_get_agent_iptv_report_url(); + PIPTV_DEV_SET report = nullptr; + PIPTV_DEV_SET pDev, pTmp = nullptr; + uv_rwlock_wrlock(&g_uvCacheLock); + HASH_ITER(hh, g_iptvNewDevs, pDev, pTmp) uv_sleep(10); + HASH_ITER(hh, report, pDev, pTmp) { + HASH_DEL(report, pDev); + free(pDev); + } + HASH_ITER(hh, report, pDev, pTmp) if (isReport && pUrl && strlen(pUrl) > 0) { + cJSON *pRspMsg = cJSON_CreateObject(); + cJSON *pMsgArray = cJSON_CreateArray(); + cJSON_AddItemToObject(pRspMsg, "iptvDevs", pMsgArray); HASH_ITER(hh, report, pDev, pTmp) { - HASH_DEL(report, pDev); - free(pDev); + cJSON *pRspItem = cJSON_CreateObject(); + cJSON_AddStringToObject(pRspItem, "mac", pDev->iptvMAC); + cJSON_AddNumberToObject(pRspItem, "vni", pDev->vni); + pDev->isReport = 1; + cJSON_AddItemToArray(pMsgArray, pRspItem); } - uv_sleep(10); - } while (true); + const char *pStrPro = proto_create_new(pRspMsg, 200); + + // Report new IPTV device MAC + inet_http_post_async(pUrl, pStrPro, on_http_response_cb, (void *)pStrPro); + } + uv_rwlock_wrunlock(&g_uvCacheLock); + HASH_ITER(hh, g_iptvNewDevs, pDev, pTmp) { + PIPTV_DEV_SET pTemp; + const char *pDevMac = strdup(pDev->iptvMAC); + HASH_FIND_STR(g_iptvCacheDevs, pDevMac, pTemp); + // 新发现设备没有被上报过 + if (!pTemp) { + auto pCacheDev = (PIPTV_DEV_SET)malloc(sizeof(IPTV_DEV_SET)); + auto pRepDev = (PIPTV_DEV_SET)malloc(sizeof(IPTV_DEV_SET)); + + memcpy(pCacheDev, pDev, sizeof(IPTV_DEV_SET)); + memcpy(pRepDev, pDev, sizeof(IPTV_DEV_SET)); + HASH_ADD_STR(report, iptvMAC, pRepDev); + + isReport = true; + // 添加到缓存列表供后续查询 + HASH_ADD_STR(g_iptvCacheDevs, iptvMAC, pCacheDev); + LOG_MOD(debug, + ZLOG_MOD_OPENDHCPD, + "Add IPTV device %s vni %d to cache\n", + pCacheDev->iptvMAC, + pCacheDev->vni); + } + + HASH_DEL(g_iptvNewDevs, pDev); + free(pDev); + free((void *)pDevMac); + } } /** @@ -950,10 +946,13 @@ static HTTP_ROUTE_INFO g_routeTable[] = { void opendhcp_init_http_server() { static int added = FALSE; static uv_thread_t uvThread; - + static uv_timer_t uvTm; if (!added) { uv_rwlock_init(&g_uvCacheLock); - uv_thread_create(&uvThread, iptvCacheCb, nullptr); + uv_timer_init(get_task_manager(), &uvTm); + + uv_timer_start(&uvTm, iptvCacheCb, 1000, 1000); + //uv_thread_create(&uvThread, iptvCacheCb, nullptr); for (auto &i : g_routeTable) { PHTTP_ROUTE_INFO p = &i; http_add_route(p->routeName, p->method, p->priority, p->cb, p->pUserData); From 44d3cb5984448fdb34cb5ad2cde9b3de8b338981 Mon Sep 17 00:00:00 2001 From: huangxin Date: Mon, 13 Mar 2023 17:39:33 +0800 Subject: [PATCH 13/31] =?UTF-8?q?OCT=201.=20=E5=88=A0=E9=99=A4=E5=A4=9A?= =?UTF-8?q?=E4=BD=99=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- srcs/opendhcp183/query.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/srcs/opendhcp183/query.cpp b/srcs/opendhcp183/query.cpp index fe5e883..e1ff481 100644 --- a/srcs/opendhcp183/query.cpp +++ b/srcs/opendhcp183/query.cpp @@ -945,14 +945,12 @@ static HTTP_ROUTE_INFO g_routeTable[] = { void opendhcp_init_http_server() { static int added = FALSE; - static uv_thread_t uvThread; static uv_timer_t uvTm; if (!added) { uv_rwlock_init(&g_uvCacheLock); uv_timer_init(get_task_manager(), &uvTm); uv_timer_start(&uvTm, iptvCacheCb, 1000, 1000); - //uv_thread_create(&uvThread, iptvCacheCb, nullptr); for (auto &i : g_routeTable) { PHTTP_ROUTE_INFO p = &i; http_add_route(p->routeName, p->method, p->priority, p->cb, p->pUserData); From 7ebc4fd9d9e3bc0f5b386779949060e8a6635ed7 Mon Sep 17 00:00:00 2001 From: huangxin Date: Tue, 14 Mar 2023 09:55:40 +0800 Subject: [PATCH 14/31] =?UTF-8?q?OCT=201.=20=E7=A7=BB=E9=99=A4=E9=9D=9E?= =?UTF-8?q?=E5=BF=85=E8=A6=81=E6=83=85=E5=86=B5=E4=B8=8B=E7=9A=84zmq?= =?UTF-8?q?=E5=BA=93=E8=B0=83=E7=94=A8=202.=20=E8=BF=9B=E7=A8=8B=E5=8F=A5?= =?UTF-8?q?=E6=9F=84=E4=BC=98=E5=8C=96266-->116?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- depend/third_libs.cmake | 5 +++-- srcs/libs/cmdline/cmd_menu.c | 7 +++++-- srcs/libs/{cmdline => mq}/mq_cmd.c | 0 3 files changed, 8 insertions(+), 4 deletions(-) rename srcs/libs/{cmdline => mq}/mq_cmd.c (100%) diff --git a/depend/third_libs.cmake b/depend/third_libs.cmake index 284cf67..71d7fb2 100644 --- a/depend/third_libs.cmake +++ b/depend/third_libs.cmake @@ -4,8 +4,9 @@ INCLUDE(FetchContent) PKG_SEARCH_MODULE(LIBCURL QUIET libcurl) PKG_SEARCH_MODULE(LIBSSL QUIET libssl) PKG_SEARCH_MODULE(LIBCRYPTO QUIET libcrypto) -PKG_SEARCH_MODULE(LIBZMQ QUIET libzmq) - +IF (USED_ZMQ) + PKG_SEARCH_MODULE(LIBZMQ QUIET libzmq) +ENDIF () IF ((NOT LIBZMQ_FOUND) OR (NOT LIBCRYPTO_FOUND) OR (NOT LIBSSL_FOUND) OR (NOT LIBCURL_FOUND)) LINUX_INSTALL_SYSTEM_PACKAGE() ENDIF () diff --git a/srcs/libs/cmdline/cmd_menu.c b/srcs/libs/cmdline/cmd_menu.c index 2984ebe..f57c451 100644 --- a/srcs/libs/cmdline/cmd_menu.c +++ b/srcs/libs/cmdline/cmd_menu.c @@ -186,8 +186,11 @@ static int on_cmd2(void *pTbl[], const char *pName, void *pInfo) { } pJsonStr = cJSON_PrintUnformatted(pJsonObj); - ret = mq_cmd_run(pSvr, pJsonStr); - +#ifdef ZEROMQ_ON + ret = mq_cmd_run(pSvr, pJsonStr); +#else + ret = ERR_SUCCESS; +#endif cJSON_Delete(pJsonObj); free((void *)pJsonStr); diff --git a/srcs/libs/cmdline/mq_cmd.c b/srcs/libs/mq/mq_cmd.c similarity index 100% rename from srcs/libs/cmdline/mq_cmd.c rename to srcs/libs/mq/mq_cmd.c From 5287e57f75297675d45d979423cd931f48a4fd7c Mon Sep 17 00:00:00 2001 From: huangxin Date: Tue, 14 Mar 2023 10:00:11 +0800 Subject: [PATCH 15/31] =?UTF-8?q?OCT=201.=20CMake=20OPTION=20=E9=80=89?= =?UTF-8?q?=E9=A1=B9=E9=A1=BA=E5=BA=8F=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bdfefa9..21c623b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,6 +8,13 @@ OPTION(USED_OPENDHCPDDNS "DHCP And DNS server for vCPE" OFF) OPTION(USED_USER_VNI "Support pass user vni id from console command line" OFF) OPTION(USED_JSON_VALIDATE "Support json protocol field validate" OFF) OPTION(BUILD_TESTING "Enable tests" OFF) +# 数据库开关 +OPTION(USED_REDIS "Add redis database support for vCPE" OFF) +OPTION(USED_MYSQL "Add mysql database support for vCPE" OFF) +OPTION(USED_SQLITE "Add sqlite3 database support for vCPE" OFF) +CMAKE_DEPENDENT_OPTION(USED_HTTP_SVR "Build-in http(s) server support" ON "USED_OPENDHCPD OR USED_OPENDHCPDDNS" OFF) +CMAKE_DEPENDENT_OPTION(USED_LWIP "PPPoE of LWIP support for vCPE" ON "VCPE_PPPOE" OFF) +CMAKE_DEPENDENT_OPTION(USED_ZMQ "ZeroMQ support for vCPE" ON "VCPE_PPPOE" OFF) LIST(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/depend) IF (BUILD_TESTING) @@ -17,14 +24,6 @@ INCLUDE(system_libs) SET(VCPE_VERSION "1.0.1") PROJECT(vCPE VERSION "${VCPE_VERSION}") -CMAKE_DEPENDENT_OPTION(USED_HTTP_SVR "Build-in http(s) server support" ON "USED_OPENDHCPD OR USED_OPENDHCPDDNS" OFF) -CMAKE_DEPENDENT_OPTION(USED_LWIP "PPPoE of LWIP support for vCPE" ON "VCPE_PPPOE" OFF) -CMAKE_DEPENDENT_OPTION(USED_ZMQ "ZeroMQ support for vCPE" ON "VCPE_PPPOE" OFF) - -# 数据库开关 -OPTION(USED_REDIS "Add redis database support for vCPE" OFF) -OPTION(USED_MYSQL "Add mysql database support for vCPE" OFF) -OPTION(USED_SQLITE "Add sqlite3 database support for vCPE" OFF) SET(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:/usr/local/lib/pkgconfig") SET(URI_HOME "${CMAKE_SOURCE_DIR}/libs") From 8122c31a44748dc393f38270f533849fcb9e8f07 Mon Sep 17 00:00:00 2001 From: huangxin Date: Tue, 14 Mar 2023 11:02:03 +0800 Subject: [PATCH 16/31] =?UTF-8?q?OCT=201.=20=E6=9B=B4=E6=96=B0=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 21c623b..5ffb35d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,7 +22,7 @@ IF (BUILD_TESTING) ENDIF () INCLUDE(system_libs) -SET(VCPE_VERSION "1.0.1") +SET(VCPE_VERSION "1.2.0") PROJECT(vCPE VERSION "${VCPE_VERSION}") SET(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:/usr/local/lib/pkgconfig") From c7e85cd0a52a48e60eea713eb78569ab098516a7 Mon Sep 17 00:00:00 2001 From: huangxin Date: Tue, 14 Mar 2023 14:14:14 +0800 Subject: [PATCH 17/31] =?UTF-8?q?OCT=201.=20=E6=9B=B4=E6=96=B0=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F=E4=BE=9D=E8=B5=96=E5=AE=89=E8=A3=85=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- depend/third_libs.cmake | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/depend/third_libs.cmake b/depend/third_libs.cmake index 71d7fb2..7dd46b0 100644 --- a/depend/third_libs.cmake +++ b/depend/third_libs.cmake @@ -6,8 +6,11 @@ PKG_SEARCH_MODULE(LIBSSL QUIET libssl) PKG_SEARCH_MODULE(LIBCRYPTO QUIET libcrypto) IF (USED_ZMQ) PKG_SEARCH_MODULE(LIBZMQ QUIET libzmq) + IF (NOT LIBZMQ_FOUND) + LINUX_INSTALL_SYSTEM_PACKAGE() + ENDIF () ENDIF () -IF ((NOT LIBZMQ_FOUND) OR (NOT LIBCRYPTO_FOUND) OR (NOT LIBSSL_FOUND) OR (NOT LIBCURL_FOUND)) +IF ((NOT LIBCRYPTO_FOUND) OR (NOT LIBSSL_FOUND) OR (NOT LIBCURL_FOUND)) LINUX_INSTALL_SYSTEM_PACKAGE() ENDIF () From cd2005bdab2dce207c587901af49691a914fb593 Mon Sep 17 00:00:00 2001 From: huangxin Date: Wed, 15 Mar 2023 09:36:16 +0800 Subject: [PATCH 18/31] =?UTF-8?q?OCT=201.=20=E5=85=B3=E9=97=ADvcpe?= =?UTF-8?q?=E5=90=AF=E5=8A=A8=E3=80=81=E9=80=80=E5=87=BA=E5=90=91Agent?= =?UTF-8?q?=E5=8F=91=E9=80=81=E9=80=9A=E7=9F=A5=E6=B6=88=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- srcs/vcpe_main.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/srcs/vcpe_main.c b/srcs/vcpe_main.c index 2fb59f3..683193e 100644 --- a/srcs/vcpe_main.c +++ b/srcs/vcpe_main.c @@ -7,7 +7,6 @@ #include "cmdline.h" #include "task_manager.h" #include "init.h" -#include "inet_misc.h" #include "config.h" #include "zlog_module.h" @@ -103,12 +102,13 @@ int main(int argc, char **argv) { pppoe_session_init(); #endif - const char *pStrSetup = proto_create_new(create_app_process_status(1), 200); - inet_http_post_async(config_get_agent_moniter_report_url(), pStrSetup, on_http_response_cb, (void *)pStrSetup); - task_manager_run(); + // const char *pStrSetup = proto_create_new(create_app_process_status(1), 200); + // inet_http_post_async(config_get_agent_moniter_report_url(), pStrSetup, on_http_response_cb, (void *)pStrSetup); - const char *pStrExit = proto_create_new(create_app_process_status(0), 200); - inet_http_post_async(config_get_agent_moniter_report_url(), pStrSetup, on_http_response_cb, (void *)pStrExit); + task_manager_run(); + // + // const char *pStrExit = proto_create_new(create_app_process_status(0), 200); + // inet_http_post_async(config_get_agent_moniter_report_url(), pStrSetup, on_http_response_cb, (void *)pStrExit); while (!is_system_cleanup()) { sleep(1); From 50aba54eaef3e5815f2b1f36b0ef1956b0372af7 Mon Sep 17 00:00:00 2001 From: huangxin Date: Wed, 15 Mar 2023 14:13:11 +0800 Subject: [PATCH 19/31] =?UTF-8?q?OCT=201.=20=E5=A2=9E=E5=8A=A0=E5=85=B3?= =?UTF-8?q?=E9=97=ADvcpe=E5=90=AF=E5=8A=A8=E3=80=81=E9=80=80=E5=87=BA?= =?UTF-8?q?=E5=90=91Agent=E5=8F=91=E9=80=81=E9=80=9A=E7=9F=A5=E5=AE=8F?= =?UTF-8?q?=E5=AE=9A=E4=B9=89=E5=BC=80=E5=85=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/vcpe.cfg | 4 ++-- srcs/vcpe_main.c | 19 ++++++++++++++----- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/config/vcpe.cfg b/config/vcpe.cfg index 6b9f483..2458966 100644 --- a/config/vcpe.cfg +++ b/config/vcpe.cfg @@ -13,8 +13,8 @@ application: }; agent: { - iptv_report_url = "http://10.0.0.1:50012/vcpe/dhcp/info"; # IPTV 设备上报接口 - moniter_rep_url = "http://10.0.0.1:50012/vcpe/dhcp/restart"; # vCPE设备上下线接口 + iptv_report_url = ""; # IPTV 设备上报接口 + moniter_rep_url = ""; # vCPE设备上下线接口 }; # 硬件监控相关配置内容 diff --git a/srcs/vcpe_main.c b/srcs/vcpe_main.c index 683193e..65d8979 100644 --- a/srcs/vcpe_main.c +++ b/srcs/vcpe_main.c @@ -7,6 +7,9 @@ #include "cmdline.h" #include "task_manager.h" #include "init.h" +#ifdef NOTIFY_VCPE_STATUS +#include "inet_misc.h" +#endif #include "config.h" #include "zlog_module.h" @@ -47,6 +50,7 @@ static void lwip_init_env() { } #endif +#ifdef NOTIFY_VCPE_STATUS cJSON *create_app_process_status(int isStart) { cJSON *pRspMsg = cJSON_CreateObject(); #ifdef USERVNI_ON @@ -73,6 +77,7 @@ static void on_http_response_cb(void *pData, free(pUserData); } +#endif int main(int argc, char **argv) { int ret; @@ -102,13 +107,17 @@ int main(int argc, char **argv) { pppoe_session_init(); #endif - // const char *pStrSetup = proto_create_new(create_app_process_status(1), 200); - // inet_http_post_async(config_get_agent_moniter_report_url(), pStrSetup, on_http_response_cb, (void *)pStrSetup); +#ifdef NOTIFY_VCPE_STATUS + const char *pStrSetup = proto_create_new(create_app_process_status(1), 200); + inet_http_post_async(config_get_agent_moniter_report_url(), pStrSetup, on_http_response_cb, (void *)pStrSetup); +#endif task_manager_run(); - // - // const char *pStrExit = proto_create_new(create_app_process_status(0), 200); - // inet_http_post_async(config_get_agent_moniter_report_url(), pStrSetup, on_http_response_cb, (void *)pStrExit); + +#ifdef NOTIFY_VCPE_STATUS + const char *pStrExit = proto_create_new(create_app_process_status(0), 200); + inet_http_post_async(config_get_agent_moniter_report_url(), pStrSetup, on_http_response_cb, (void *)pStrExit); +#endif while (!is_system_cleanup()) { sleep(1); From 18e296c19cb4de6c030b81ee6d3e8b9b9d1fab31 Mon Sep 17 00:00:00 2001 From: huangxin Date: Wed, 15 Mar 2023 14:19:49 +0800 Subject: [PATCH 20/31] =?UTF-8?q?OCT=201.=20=E6=A0=B9=E6=8D=AE=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E9=A1=B9=E5=BC=80=E5=85=B3=E5=86=B3=E5=AE=9A=E5=85=B3?= =?UTF-8?q?=E9=97=ADvcpe=E5=90=AF=E5=8A=A8=E3=80=81=E9=80=80=E5=87=BA?= =?UTF-8?q?=E5=90=91Agent=E5=8F=91=E9=80=81=E9=80=9A=E7=9F=A5=E5=AE=8F?= =?UTF-8?q?=E5=AE=9A=E4=B9=89=E5=BC=80=E5=85=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/vcpe.cfg | 2 +- srcs/vcpe_main.c | 27 ++++++++++++++------------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/config/vcpe.cfg b/config/vcpe.cfg index 2458966..7292984 100644 --- a/config/vcpe.cfg +++ b/config/vcpe.cfg @@ -13,7 +13,7 @@ application: }; agent: { - iptv_report_url = ""; # IPTV 设备上报接口 + iptv_report_url = "http://10.0.0.1:50012/vcpe/dhcp/info"; # IPTV 设备上报接口 moniter_rep_url = ""; # vCPE设备上下线接口 }; diff --git a/srcs/vcpe_main.c b/srcs/vcpe_main.c index 65d8979..409781b 100644 --- a/srcs/vcpe_main.c +++ b/srcs/vcpe_main.c @@ -4,12 +4,11 @@ #include #include #include +#include #include "cmdline.h" #include "task_manager.h" #include "init.h" -#ifdef NOTIFY_VCPE_STATUS #include "inet_misc.h" -#endif #include "config.h" #include "zlog_module.h" @@ -50,7 +49,6 @@ static void lwip_init_env() { } #endif -#ifdef NOTIFY_VCPE_STATUS cJSON *create_app_process_status(int isStart) { cJSON *pRspMsg = cJSON_CreateObject(); #ifdef USERVNI_ON @@ -77,10 +75,11 @@ static void on_http_response_cb(void *pData, free(pUserData); } -#endif int main(int argc, char **argv) { - int ret; + int ret; + const char *pRspUrl; + #ifdef OPENDHCPDDNS_ON return dual_server_main(argc, argv); #else @@ -107,17 +106,19 @@ int main(int argc, char **argv) { pppoe_session_init(); #endif -#ifdef NOTIFY_VCPE_STATUS - const char *pStrSetup = proto_create_new(create_app_process_status(1), 200); - inet_http_post_async(config_get_agent_moniter_report_url(), pStrSetup, on_http_response_cb, (void *)pStrSetup); -#endif + pRspUrl = config_get_agent_moniter_report_url(); + + if (pRspUrl && strlen(pRspUrl) > 0) { + const char *pStrSetup = proto_create_new(create_app_process_status(1), 200); + inet_http_post_async(config_get_agent_moniter_report_url(), pStrSetup, on_http_response_cb, (void *)pStrSetup); + } task_manager_run(); -#ifdef NOTIFY_VCPE_STATUS - const char *pStrExit = proto_create_new(create_app_process_status(0), 200); - inet_http_post_async(config_get_agent_moniter_report_url(), pStrSetup, on_http_response_cb, (void *)pStrExit); -#endif + if (pRspUrl && strlen(pRspUrl) > 0) { + const char *pStrExit = proto_create_new(create_app_process_status(0), 200); + inet_http_post_async(config_get_agent_moniter_report_url(), pStrExit, on_http_response_cb, (void *)pStrExit); + } while (!is_system_cleanup()) { sleep(1); From f06cc2b7482b9ca18a855f74e4d6803132939bc8 Mon Sep 17 00:00:00 2001 From: dongwenze Date: Wed, 15 Mar 2023 18:12:07 +0800 Subject: [PATCH 21/31] =?UTF-8?q?OCT=201.=E4=BF=AE=E6=94=B9JSON=E5=AD=97?= =?UTF-8?q?=E6=AE=B5=E8=A7=84=E8=8C=83=E6=A0=BC=E5=BC=8F=E6=A3=80=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- srcs/libs/include/proto.h | 3 +- srcs/libs/include/user_errno.h | 6 ++-- srcs/libs/protocol/protocol.c | 49 +++++++++----------------- srcs/opendhcp183/query.cpp | 63 +++++++++++++++++++++++++--------- 4 files changed, 68 insertions(+), 53 deletions(-) diff --git a/srcs/libs/include/proto.h b/srcs/libs/include/proto.h index 017dc6b..3a53f7c 100644 --- a/srcs/libs/include/proto.h +++ b/srcs/libs/include/proto.h @@ -24,8 +24,9 @@ typedef enum { TYPE_QUE_USR = 2, } PROTO_POST_TYPE; +const char *proto_msg_validation(const char *pJsonStr, const char *msgJson); const char *proto_create_new(cJSON *pMsgCtx, int httpCode); -const char *proto_decode_context(const char *pString, unsigned int *pVer, unsigned long long *pTm, PROTO_POST_TYPE type_id, int *pErrCode); +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 ebaf615..3982104 100644 --- a/srcs/libs/include/user_errno.h +++ b/srcs/libs/include/user_errno.h @@ -49,7 +49,7 @@ extern "C" { ERR_CODE(ERR_MQ_CONN_SERVER, 37, "消息队列连接服务器失败") \ ERR_CODE(ERR_MQ_SEND_MSG, 38, "消息队列发送消息失败") \ ERR_CODE(ERR_JSON_CREAT_OBJ, 39, "创建JSON对象失败") \ - ERR_CODE(ERR_JSON_PRASE_OBJ, 40, "解析JSON对象失败") \ + ERR_CODE(ERR_JSON_PARSE_OBJ, 40, "解析JSON对象失败") \ ERR_CODE(ERR_JSON_VALID_SCH, 41, "JSON数据验证失败") \ ERR_CODE(ERR_CREATE_NETIF, 42, "创建网络接口失败") \ ERR_CODE(ERR_CREATE_PPPOE_NETIF, 43, "创建PPPoE网络接口失败") \ @@ -61,8 +61,8 @@ extern "C" { ERR_CODE(ERR_MENU_EXIT, 49, "菜单执行完后自动退出") \ ERR_CODE(ERR_HTTP_UNSUP_METHOD, 50, "不支持的 HTTP 请求方法") \ ERR_CODE(ERR_HTTP_UNSUP_PAGE, 51, "找不到 HTTP 服务") \ - ERR_CODE(ERR_PROTO_DECODE, 52, "HTTP 协议解析失败") - + ERR_CODE(ERR_PROTO_DECODE, 52, "HTTP 协议解析失败") \ + ERR_CODE(ERR_MSG_CONTENT, 53, "msgContent内容不正确") #define GENERATE_ENUM(ENUM, no, x) ENUM, typedef enum { diff --git a/srcs/libs/protocol/protocol.c b/srcs/libs/protocol/protocol.c index 0d305ce..149e22b 100644 --- a/srcs/libs/protocol/protocol.c +++ b/srcs/libs/protocol/protocol.c @@ -30,18 +30,12 @@ typedef struct { } 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\":[\"ver\"],\"properties\":{\"ver\":{\"type\":\"integer\"}}}", "Incorrect field [ver]"}, + {"{\"type\":\"object\",\"required\":[\"cryptoType\"],\"properties\":{\"cryptoType\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":4}}}", "Incorrect field [cryptoType]"}, + {"{\"type\":\"object\",\"required\":[\"timeStamp\"],\"properties\":{\"timeStamp\":{\"type\":\"integer\"}}}", "Incorrect field [timeStamp]"}, {"{\"type\":\"object\",\"required\":[\"msgContent\"]}", "Missing required field [msgContent]"}, }; -static JSON_SCHEMA_CTX g_json_msg[] = { - {"{\"type\":\"object\",\"required\":[\"rangeSet\"],\"properties\":{\"rangeSet\":{\"type\":\"array\",\"minItems\":1,\"items\":{\"type\":\"object\",\"required\":[\"dhcpRange\"]}}}}", "Incorrect [rangeSet]"}, - {"{\"type\":\"object\",\"required\":[\"dhcpRange\"],\"properties\":{\"dhcpRange\":{\"type\":\"array\",\"minItems\":1}}}", "Incorrect [dhcpRange]"}, - {"{\"type\":\"object\",\"required\":[\"userMac\"],\"properties\":{\"userMac\":{\"type\":\"array\",\"minItems\":1}}}", "Incorrect [userMac]"}, -}; - const char *proto_schema_validation(const char *pJsonStr) { int i; @@ -49,8 +43,9 @@ const char *proto_schema_validation(const char *pJsonStr) { if (!pJs) { cJSON *pRspRoot = cJSON_CreateObject(); - cJSON_AddNumberToObject(pRspRoot, "status", ERR_JSON_PRASE_OBJ); - cJSON_AddStringToObject(pRspRoot, "message", getErrorEnumDesc(ERR_JSON_PRASE_OBJ)); + cJSON_AddNumberToObject(pRspRoot, "status", ERR_JSON_PARSE_OBJ); + cJSON_AddStringToObject(pRspRoot, "message", getErrorEnumDesc(ERR_JSON_PARSE_OBJ)); + printf("ERR_JSON_PARSE_OBJ\n"); return proto_create_new(pRspRoot, 200); } @@ -79,25 +74,25 @@ const char *proto_schema_validation(const char *pJsonStr) { return NULL; } -const char *proto_msg_validation(const char *pJsonStr, PROTO_POST_TYPE type_id) { +const char *proto_msg_validation(const char *pJsonStr, const char *msgJson) { 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)); + cJSON_AddNumberToObject(pRspRoot, "status", ERR_JSON_PARSE_OBJ); + cJSON_AddStringToObject(pRspRoot, "message", getErrorEnumDesc(ERR_JSON_PARSE_OBJ)); return proto_create_new(pRspRoot, 200); } - json_object *pSc = json_tokener_parse(g_json_msg[type_id].pSchJson); + json_object *pSc = json_tokener_parse(msgJson); if (!pSc) { - LOG_MOD(error, ZLOG_MOD_PROTO, "Json schema format error: [%s]\n", g_json_msg[type_id].pSchJson); + LOG_MOD(error, ZLOG_MOD_PROTO, "Json schema format error: [%s]\n", msgJson); } 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_msg[type_id].pErrMsg); + cJSON_AddStringToObject(pRspRoot, "details", getErrorEnumDesc(ERR_MSG_CONTENT)); json_object_put(pSc); json_object_put(pJs); return proto_create_new(pRspRoot, 200); @@ -109,15 +104,15 @@ const char *proto_msg_validation(const char *pJsonStr, PROTO_POST_TYPE type_id) } #endif -const char *proto_decode_context(const char *pString, unsigned int *pVer, - unsigned long long *pTm, PROTO_POST_TYPE type_id, int *pErrCode) { +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; #ifdef JSON_SCHEMA_ON - const char *pSchJson; + const char *pSchJson; #endif if (pErrCode == NULL) { @@ -167,19 +162,7 @@ const char *proto_decode_context(const char *pString, unsigned int *pVer, } } - cJSON *pMsgCtx = cJSON_GetObjectItem(pRoot, "msgContent"); - char *msgCont = cJSON_Print(pMsgCtx); - -#ifdef JSON_SCHEMA_ON - pSchJson = proto_msg_validation(msgCont, type_id); - free((void *)msgCont); - - if (pSchJson != NULL && strlen(pSchJson) > 0) { - *pErrCode = ERR_JSON_VALID_SCH; - cJSON_Delete(pRoot); - return pSchJson; - } -#endif + pMsgCtx = cJSON_GetObjectItem(pRoot, "msgContent"); if (!pMsgCtx) { cJSON_Delete(pRoot); diff --git a/srcs/opendhcp183/query.cpp b/srcs/opendhcp183/query.cpp index 4814432..5b3462a 100644 --- a/srcs/opendhcp183/query.cpp +++ b/srcs/opendhcp183/query.cpp @@ -43,23 +43,31 @@ static PIPTV_DEV_SET g_iptvNewDevs = nullptr; static PIPTV_DEV_SET g_iptvCacheDevs = nullptr; static uv_rwlock_t g_uvCacheLock; +#ifdef JSON_SCHEMA_ON +const char *g_json_msg[] = { + "{\"type\":\"object\",\"required\":[\"rangeSet\"],\"properties\":{\"rangeSet\":{\"type\":\"array\",\"minItems\":1,\"items\":{\"type\":\"object\",\"required\":[\"dhcpRange\"]}}}}", + "{\"type\":\"object\",\"required\":[\"dhcpRange\"],\"properties\":{\"dhcpRange\":{\"type\":\"array\",\"minItems\":1}}}", + "{\"type\":\"object\",\"required\":[\"userMac\"],\"properties\":{\"userMac\":{\"type\":\"array\",\"minItems\":1}}}" +}; +#endif + static int dhcp_get_user_info(const char **pRsp, const char *pRequest) { char logBuff[512]; const char *pStrContent; int k; - int errCode; + int errCode = 0; dhcpMap::iterator p; if (pRequest == nullptr || strlen(pRequest) == 0) { - sprintf(logBuff, "Requeset Json"); + sprintf(logBuff, "Request Json"); logDHCPMess(logBuff, 1); return ERR_INPUT_PARAMS; } - pStrContent = proto_decode_context(pRequest, nullptr, nullptr, TYPE_QUE_USR, &errCode); + pStrContent = proto_decode_context(pRequest, nullptr, nullptr, &errCode); if (pStrContent == nullptr) { - sprintf(logBuff, "Requeset Json error %s", pRequest); + sprintf(logBuff, "Request Json error %s", pRequest); logDHCPMess(logBuff, 1); return ERR_PROTO_DECODE; } @@ -69,20 +77,28 @@ static int dhcp_get_user_info(const char **pRsp, const char *pRequest) { *pRsp = pStrContent; return ERR_SUCCESS; } + + const char *pSchJson; + pSchJson = proto_msg_validation(pStrContent, g_json_msg[TYPE_QUE_USR]); + if (pSchJson != nullptr && strlen(pSchJson) > 0) { + *pRsp = pSchJson; + return ERR_SUCCESS; + } + free((void *)pSchJson); #endif cJSON *pRoot = cJSON_Parse(pStrContent); free((void *)pStrContent); if (!pRoot) { - return ERR_JSON_PRASE_OBJ; + return ERR_JSON_PARSE_OBJ; } cJSON *pUserMac = cJSON_GetObjectItem(pRoot, "userMac"); if (!pUserMac) { cJSON_Delete(pRoot); - return ERR_JSON_PRASE_OBJ; + return ERR_JSON_PARSE_OBJ; } cJSON *pRspMsg = cJSON_CreateObject(); @@ -326,18 +342,18 @@ static int add_dhcpd_rangeset(const char **pRsp, const char *pRequest) { OBJ_DHCP_RNG range; cJSON *pRspRoot; cJSON *pExpandArray; - int errCode; + int errCode = 0; if (pRequest == nullptr || strlen(pRequest) == 0) { - sprintf(logBuff, "Requeset Json"); + sprintf(logBuff, "Request Json"); logDHCPMess(logBuff, 1); return ERR_INPUT_PARAMS; } - pStrContent = proto_decode_context(pRequest, nullptr, nullptr, TYPE_ADD_RNG, &errCode); + pStrContent = proto_decode_context(pRequest, nullptr, nullptr, &errCode); if (pStrContent == nullptr) { - sprintf(logBuff, "Requeset Json error %s", pRequest); + sprintf(logBuff, "Request Json error %s", pRequest); logDHCPMess(logBuff, 1); return ERR_PROTO_DECODE; } @@ -347,13 +363,21 @@ static int add_dhcpd_rangeset(const char **pRsp, const char *pRequest) { *pRsp = pStrContent; return ERR_SUCCESS; } + + const char *pSchJson; + pSchJson = proto_msg_validation(pStrContent, g_json_msg[TYPE_ADD_RNG]); + if (pSchJson != nullptr && strlen(pSchJson) > 0) { + *pRsp = pSchJson; + return ERR_SUCCESS; + } + free((void *)pSchJson); #endif cJSON *pRoot = cJSON_Parse(pStrContent); free((void *)pStrContent); if (!pRoot) { - return ERR_JSON_PRASE_OBJ; + return ERR_JSON_PARSE_OBJ; } cJSON *prange_set = cJSON_GetObjectItem(pRoot, "rangeSet"); @@ -433,17 +457,17 @@ 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; + int errCode = 0; if (pRequest == nullptr || strlen(pRequest) == 0) { - sprintf(logBuff, "Requeset Json"); + sprintf(logBuff, "Request Json"); logDHCPMess(logBuff, 1); return ERR_INPUT_PARAMS; } - pStrContent = proto_decode_context(pRequest, nullptr, nullptr, TYPE_DEL_RNG, &errCode); + pStrContent = proto_decode_context(pRequest, nullptr, nullptr, &errCode); if (pStrContent == nullptr) { - sprintf(logBuff, "Requeset Json error %s", pRequest); + sprintf(logBuff, "Request Json error %s", pRequest); logDHCPMess(logBuff, 1); return ERR_PROTO_DECODE; } @@ -453,12 +477,19 @@ static int delete_dhcpd_rangeset(const char **pRsp, const char *pRequest) { *pRsp = pStrContent; return ERR_SUCCESS; } + + const char *pSchJson; + pSchJson = proto_msg_validation(pStrContent, g_json_msg[TYPE_DEL_RNG]); + if (pSchJson != nullptr && strlen(pSchJson) > 0) { + *pRsp = pSchJson; + return ERR_SUCCESS; + } #endif cJSON *pRoot = cJSON_Parse(pStrContent); free((void *)pStrContent); if (!pRoot) { - return ERR_JSON_PRASE_OBJ; + return ERR_JSON_PARSE_OBJ; } cJSON *pdhcp_range = cJSON_GetObjectItem(pRoot, "dhcpRange"); From c72dca0a44212da257038a97ebf3760387a136a0 Mon Sep 17 00:00:00 2001 From: huangxin Date: Wed, 15 Mar 2023 18:20:26 +0800 Subject: [PATCH 22/31] =?UTF-8?q?OCT=201.=20=E5=A2=9E=E5=8A=A0DHCP?= =?UTF-8?q?=E5=93=8D=E5=BA=94=E5=8C=85=E5=B9=BF=E6=92=AD=E5=9C=B0=E5=9D=80?= =?UTF-8?q?=E5=BC=80=E5=85=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- srcs/opendhcp183/opendhcpd.cpp | 21 ++++++++++++++++++--- srcs/opendhcp183/opendhcpd.h | 4 ++++ srcs/opendhcp183/query.cpp | 2 ++ 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/srcs/opendhcp183/opendhcpd.cpp b/srcs/opendhcp183/opendhcpd.cpp index 878f1ac..2cc5233 100644 --- a/srcs/opendhcp183/opendhcpd.cpp +++ b/srcs/opendhcp183/opendhcpd.cpp @@ -1904,18 +1904,33 @@ MYDWORD sdmess(data9 *req) { req->dhcpp.header.bp_op = BOOTP_REPLY; errno = 0; +#if DISCOVER_RSP_NOT_BOARDCAST sockaddr_in cliAddr = get_cliAddr(nicif, tempbuff, req); - +#endif if (req->req_type == DHCP_MESS_DISCOVER && !req->remote.sin_addr.s_addr) { req->bytes = (int)sendto(network.dhcpConn[req->sockInd].sock, req->raw, packSize, MSG_DONTROUTE, +#if DISCOVER_RSP_NOT_BOARDCAST (sockaddr *)&cliAddr, sizeof(cliAddr)); +#else + (sockaddr *)&req->remote, + sizeof(req->remote)); +#endif } else { - req->bytes = (int) - sendto(network.dhcpConn[req->sockInd].sock, req->raw, packSize, 0, (sockaddr *)&cliAddr, sizeof(cliAddr)); + req->bytes = (int)sendto(network.dhcpConn[req->sockInd].sock, + req->raw, + packSize, + 0, +#if DISCOVER_RSP_NOT_BOARDCAST + (sockaddr *)&cliAddr, + sizeof(cliAddr)); +#else + (sockaddr *)&req->remote, + sizeof(req->remote)); +#endif } if (errno || req->bytes <= 0) { diff --git a/srcs/opendhcp183/opendhcpd.h b/srcs/opendhcp183/opendhcpd.h index 1dec740..4b43817 100644 --- a/srcs/opendhcp183/opendhcpd.h +++ b/srcs/opendhcp183/opendhcpd.h @@ -84,6 +84,8 @@ typedef struct in_pktinfo IN_PKTINFO; #define STR2INT(val) ((int)strtol((val), nullptr, 10)) +#define DISCOVER_RSP_NOT_BOARDCAST (0) + #define logDHCPMess(logBuff, logLevel) \ do { \ switch ((logLevel)) { \ @@ -775,5 +777,7 @@ int opendhcp_add_listener(); unsigned int opendhcp_set_lease_time(); int getHwAddr(char *buff, char *mac); int arpSet(const char *ifname, char *ipStr, char *mac); +#if DISCOVER_RSP_NOT_BOARDCAST sockaddr_in get_cliAddr(char *nicif, char *tempbuff, data9 *req); +#endif #pragma clang diagnostic pop \ No newline at end of file diff --git a/srcs/opendhcp183/query.cpp b/srcs/opendhcp183/query.cpp index e1ff481..10bf680 100644 --- a/srcs/opendhcp183/query.cpp +++ b/srcs/opendhcp183/query.cpp @@ -675,6 +675,7 @@ int getHwAddr(char *buff, char *mac) { #pragma clang diagnostic pop +#if DISCOVER_RSP_NOT_BOARDCAST int arpSet(const char *ifname, char *ipStr, char *mac) { if (ifname == nullptr || ipStr == nullptr || mac == nullptr) { LOG_MOD(error, ZLOG_MOD_OPENDHCPD, "para is null.\n"); @@ -724,6 +725,7 @@ sockaddr_in get_cliAddr(char *nicif, char *tempbuff, data9 *req) { cliAddr.sin_addr.s_addr = inet_addr(IP2String(tempbuff, req->dhcpp.header.bp_yiaddr)); return cliAddr; } +#endif unsigned int opendhcp_set_lease_time() { return config_get_dhcp_server_lease_time(); From 878486b58f1532c0f426868196982615272ebd83 Mon Sep 17 00:00:00 2001 From: dongwenze Date: Wed, 15 Mar 2023 18:37:42 +0800 Subject: [PATCH 23/31] =?UTF-8?q?OCT=201.=E5=88=A0=E9=99=A4=E6=97=A0?= =?UTF-8?q?=E7=94=A8=E6=A0=87=E8=AE=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- srcs/libs/protocol/protocol.c | 1 - 1 file changed, 1 deletion(-) diff --git a/srcs/libs/protocol/protocol.c b/srcs/libs/protocol/protocol.c index 149e22b..c024e91 100644 --- a/srcs/libs/protocol/protocol.c +++ b/srcs/libs/protocol/protocol.c @@ -45,7 +45,6 @@ const char *proto_schema_validation(const char *pJsonStr) { cJSON *pRspRoot = cJSON_CreateObject(); cJSON_AddNumberToObject(pRspRoot, "status", ERR_JSON_PARSE_OBJ); cJSON_AddStringToObject(pRspRoot, "message", getErrorEnumDesc(ERR_JSON_PARSE_OBJ)); - printf("ERR_JSON_PARSE_OBJ\n"); return proto_create_new(pRspRoot, 200); } From eb9c03c9084e2eb930a6b1d9c88d56cec17dd215 Mon Sep 17 00:00:00 2001 From: huangxin Date: Thu, 16 Mar 2023 09:57:04 +0800 Subject: [PATCH 24/31] =?UTF-8?q?OCT=201.=20=E6=9B=B4=E6=96=B0=E5=A4=B4?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E5=AE=9A=E4=B9=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- srcs/opendhcp183/opendhcpd.h | 164 +++++++++++++++++------------------ srcs/opendhcp183/query.cpp | 23 ++--- 2 files changed, 95 insertions(+), 92 deletions(-) diff --git a/srcs/opendhcp183/opendhcpd.h b/srcs/opendhcp183/opendhcpd.h index 4b43817..01334b2 100644 --- a/srcs/opendhcp183/opendhcpd.h +++ b/srcs/opendhcp183/opendhcpd.h @@ -39,7 +39,7 @@ #endif #ifndef SIOCGIFCONF -#include +#include #endif #ifndef _UIO_H_ @@ -70,19 +70,19 @@ typedef struct in_pktinfo IN_PKTINFO; #define IFF_DYNAMIC 0x8000 #endif -#define MYWORD unsigned short -#define MYBYTE unsigned char -#define MYDWORD unsigned int -#define SOCKET_ERROR -1 -#define INVALID_SOCKET -1 -#define SOCKADDR_IN sockaddr_in -#define SOCKADDR sockaddr -#define SOCKET int -#define BOOL bool -#define LPSOCKADDR sockaddr * -#define closesocket close +#define MYWORD unsigned short +#define MYBYTE unsigned char +#define MYDWORD unsigned int +#define SOCKET_ERROR -1 +#define INVALID_SOCKET -1 +#define SOCKADDR_IN sockaddr_in +#define SOCKADDR sockaddr +#define SOCKET int +#define BOOL bool +#define LPSOCKADDR sockaddr * +#define closesocket close -#define STR2INT(val) ((int)strtol((val), nullptr, 10)) +#define STR2INT(val) ((int)strtol((val), nullptr, 10)) #define DISCOVER_RSP_NOT_BOARDCAST (0) @@ -707,77 +707,77 @@ struct data15 { }; //Function Prototypes -bool checkIP(data9 *req, data17 *rangeData, MYDWORD ip); -bool checkMask(MYDWORD); -bool checkRange(data17 *, char); -bool detectChange(); -bool getSection(const char *, char *, MYBYTE, char *); -bool isInt(char *); -bool isIP(char *); -FILE *openSection(const char *, MYBYTE); -MYBYTE pIP(void *, MYDWORD); -MYBYTE pUInt(void *raw, MYDWORD data); -MYBYTE pUShort(void *, MYWORD); -MYBYTE addServer(MYDWORD *, MYBYTE, MYDWORD); -int getRangeInd(MYDWORD); -char *myTrim(char *, char *); -char *myGetToken(char *, MYBYTE); -char *cloneString(char *); -char *getHexValue(MYBYTE *, char *, MYBYTE *); -char *genHostName(char *, const MYBYTE *, MYBYTE); -char *hex2String(char *, const MYBYTE *, MYBYTE); -char *IP2String(char *, MYDWORD); -char *myUpper(char *string); -char *myLower(char *string); -char *readSection(char *, FILE *); -data7 *findDHCPEntry(char *); -data7 *createCache(data71 *pLump); -MYDWORD alad(data9 *); -MYDWORD resad(data9 *); -MYDWORD sdmess(data9 *); -MYDWORD sendRepl(data9 *req); -MYDWORD *findServer(MYDWORD *, MYBYTE, MYDWORD); -int getIndex(char, MYDWORD); -int addDHCPRange(char *dp); -void addVendClass(MYBYTE rangeSetInd, char *vendClass, MYBYTE vendClassSize); -void addUserClass(MYBYTE rangeSetInd, char *userClass, MYBYTE userClassSize); -void addMacRange(MYBYTE rangeSetInd, char *macRange); -void addOptions(data9 *req); -void calcRangeLimits(MYDWORD, MYDWORD, MYDWORD *, MYDWORD *); -void closeConn(); -bool getInterfaces(data1 *pNetwork); -void *init(void *); -void lockOptions(FILE *); -void loadOptions(FILE *, const char *, data20 *); -void mySplit(char *, char *, const char *, char); -void *sendHTTP(void *); -void procHTTP(data19 *); -void pvdata(data9 *, data3 *); -void recvRepl(data9 *); -void lockIP(MYDWORD); -void sendStatus(data19 *req); -void setTempLease(data7 *); -void setLeaseExpiry(data7 *); -void setLeaseExpiry(data7 *, MYDWORD); -void *updateStateFile(void *); -MYWORD fUShort(void *); -MYWORD gdmess(data9 *, MYBYTE); -MYWORD myTokenize(char *, char *, const char *, bool); -MYDWORD fIP(void *raw); -MYDWORD fUInt(void *raw); -int prepareUserHtmlRespStatus(const char **pRsp); +bool checkIP(data9 *req, data17 *rangeData, MYDWORD ip); +bool checkMask(MYDWORD); +bool checkRange(data17 *, char); +bool detectChange(); +bool getSection(const char *, char *, MYBYTE, char *); +bool isInt(char *); +bool isIP(char *); +FILE *openSection(const char *, MYBYTE); +MYBYTE pIP(void *, MYDWORD); +MYBYTE pUInt(void *raw, MYDWORD data); +MYBYTE pUShort(void *, MYWORD); +MYBYTE addServer(MYDWORD *, MYBYTE, MYDWORD); +int getRangeInd(MYDWORD); +char *myTrim(char *, char *); +char *myGetToken(char *, MYBYTE); +char *cloneString(char *); +char *getHexValue(MYBYTE *, char *, MYBYTE *); +char *genHostName(char *, const MYBYTE *, MYBYTE); +char *hex2String(char *, const MYBYTE *, MYBYTE); +char *IP2String(char *, MYDWORD); +char *myUpper(char *string); +char *myLower(char *string); +char *readSection(char *, FILE *); +data7 *findDHCPEntry(char *); +data7 *createCache(data71 *pLump); +MYDWORD alad(data9 *); +MYDWORD resad(data9 *); +MYDWORD sdmess(data9 *); +MYDWORD sendRepl(data9 *req); +MYDWORD *findServer(MYDWORD *, MYBYTE, MYDWORD); +int getIndex(char, MYDWORD); +int addDHCPRange(char *dp); +void addVendClass(MYBYTE rangeSetInd, char *vendClass, MYBYTE vendClassSize); +void addUserClass(MYBYTE rangeSetInd, char *userClass, MYBYTE userClassSize); +void addMacRange(MYBYTE rangeSetInd, char *macRange); +void addOptions(data9 *req); +void calcRangeLimits(MYDWORD, MYDWORD, MYDWORD *, MYDWORD *); +void closeConn(); +bool getInterfaces(data1 *pNetwork); +void *init(void *); +void lockOptions(FILE *); +void loadOptions(FILE *, const char *, data20 *); +void mySplit(char *, char *, const char *, char); +void *sendHTTP(void *); +void procHTTP(data19 *); +void pvdata(data9 *, data3 *); +void recvRepl(data9 *); +void lockIP(MYDWORD); +void sendStatus(data19 *req); +void setTempLease(data7 *); +void setLeaseExpiry(data7 *); +void setLeaseExpiry(data7 *, MYDWORD); +void *updateStateFile(void *); +MYWORD fUShort(void *); +MYWORD gdmess(data9 *, MYBYTE); +MYWORD myTokenize(char *, char *, const char *, bool); +MYDWORD fIP(void *raw); +MYDWORD fUInt(void *raw); +int prepareUserHtmlRespStatus(const char **pRsp); #ifdef HTTPSERVER_ON -void opendhcp_init_http_server(); +void opendhcp_init_http_server(); #endif -void opendhcp_set_replication_svr(); -void opendhcp_add_ip_pool_set(); -void opendhcp_add_mac_filter(); -int process_iptv_multicast(const unsigned char *p, int size, const char *mac); -int opendhcp_add_listener(); -unsigned int opendhcp_set_lease_time(); -int getHwAddr(char *buff, char *mac); -int arpSet(const char *ifname, char *ipStr, char *mac); +void opendhcp_set_replication_svr(); +void opendhcp_add_ip_pool_set(); +void opendhcp_add_mac_filter(); +int process_iptv_multicast(const unsigned char *p, int size, const char *mac); +int opendhcp_add_listener(); +unsigned int opendhcp_set_lease_time(); +int getHwAddr(char *buff, char *mac); +int arpSet(const char *ifname, char *ipStr, char *mac); #if DISCOVER_RSP_NOT_BOARDCAST -sockaddr_in get_cliAddr(char *nicif, char *tempbuff, data9 *req); +sockaddr_in get_cliAddr(char *nicif, char *tempbuff, data9 *req); #endif #pragma clang diagnostic pop \ No newline at end of file diff --git a/srcs/opendhcp183/query.cpp b/srcs/opendhcp183/query.cpp index b7d7f75..7b442e1 100644 --- a/srcs/opendhcp183/query.cpp +++ b/srcs/opendhcp183/query.cpp @@ -5,18 +5,14 @@ #include using namespace std; -#include -#include #include #include #include -#include #include #include #include "opendhcpd.h" #include "s2j/cJSON.h" #include "misc.h" -#include #include #include "config.h" #include "proto.h" @@ -27,6 +23,12 @@ using namespace std; #include "http_svr.h" #include "task_manager.h" +#if DISCOVER_RSP_NOT_BOARDCAST +#include +#include +#include +#endif + extern data2 cfig; extern bool kRunning; extern dhcpMap dhcpCache; @@ -45,11 +47,12 @@ static PIPTV_DEV_SET g_iptvCacheDevs = nullptr; static uv_rwlock_t g_uvCacheLock; #ifdef JSON_SCHEMA_ON +// clang-format off const char *g_json_msg[] = { - "{\"type\":\"object\",\"required\":[\"rangeSet\"],\"properties\":{\"rangeSet\":{\"type\":\"array\",\"minItems\":1,\"items\":{\"type\":\"object\",\"required\":[\"dhcpRange\"]}}}}", - "{\"type\":\"object\",\"required\":[\"dhcpRange\"],\"properties\":{\"dhcpRange\":{\"type\":\"array\",\"minItems\":1}}}", - "{\"type\":\"object\",\"required\":[\"userMac\"],\"properties\":{\"userMac\":{\"type\":\"array\",\"minItems\":1}}}" -}; + R"({"type":"object","required":["rangeSet"],"properties":{"rangeSet":{"type":"array","minItems":1,"items":{"type":"object","required":["dhcpRange"]}}}})", + R"({"type":"object","required":["dhcpRange"],"properties":{"dhcpRange":{"type":"array","minItems":1}}})", + R"({"type":"object","required":["userMac"],"properties":{"userMac":{"type":"array","minItems":1}}})"}; +// clang-format on #endif static int dhcp_get_user_info(const char **pRsp, const char *pRequest) { @@ -977,8 +980,8 @@ static HTTP_ROUTE_INFO g_routeTable[] = { }; void opendhcp_init_http_server() { - static int added = FALSE; - static uv_timer_t uvTm; + static int added = FALSE; + static uv_timer_t uvTm; if (!added) { uv_rwlock_init(&g_uvCacheLock); uv_timer_init(get_task_manager(), &uvTm); From 7d95eda1b680eb3b6987f3d204bc05af20830229 Mon Sep 17 00:00:00 2001 From: dongwenze Date: Thu, 16 Mar 2023 18:02:02 +0800 Subject: [PATCH 25/31] =?UTF-8?q?OCT=201.=E6=9B=B4=E6=94=B9JSON=E8=A7=84?= =?UTF-8?q?=E8=8C=83=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- srcs/libs/protocol/protocol.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/srcs/libs/protocol/protocol.c b/srcs/libs/protocol/protocol.c index c024e91..d0044d7 100644 --- a/srcs/libs/protocol/protocol.c +++ b/srcs/libs/protocol/protocol.c @@ -30,10 +30,14 @@ typedef struct { } JSON_SCHEMA_CTX, *PJSON_SCHEMA_CTX; static JSON_SCHEMA_CTX g_json_sch[] = { - {"{\"type\":\"object\",\"required\":[\"ver\"],\"properties\":{\"ver\":{\"type\":\"integer\"}}}", "Incorrect field [ver]"}, - {"{\"type\":\"object\",\"required\":[\"cryptoType\"],\"properties\":{\"cryptoType\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":4}}}", "Incorrect field [cryptoType]"}, - {"{\"type\":\"object\",\"required\":[\"timeStamp\"],\"properties\":{\"timeStamp\":{\"type\":\"integer\"}}}", "Incorrect field [timeStamp]"}, - {"{\"type\":\"object\",\"required\":[\"msgContent\"]}", "Missing required field [msgContent]"}, + {"{\"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]" }, + {"{\"properties\":{\"ver\":{\"type\":\"integer\"}}}", "[ver] should be an integer value" }, + {"{\"properties\":{\"cryptoType\":{\"type\":\"integer\"}}}", "[cryptoType] should be an integer value"}, + {"{\"properties\":{\"cryptoType\":{\"minimum\":0,\"maximum\":4}}}", "Undefined type in field [cryptoType]" }, + {"{\"properties\":{\"timeStamp\":{\"type\":\"integer\"}}}", "[timeStamp] should be an integer value" }, }; const char *proto_schema_validation(const char *pJsonStr) { From f1524656e4a2ba7c19c089c4ac7d9a8413a17642 Mon Sep 17 00:00:00 2001 From: dongwenze Date: Mon, 20 Mar 2023 15:43:33 +0800 Subject: [PATCH 26/31] =?UTF-8?q?OCT=201.=E5=AF=B9=E9=83=A8=E5=88=86?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E8=BF=9B=E8=A1=8C=E5=8D=95=E5=85=83=E6=B5=8B?= =?UTF-8?q?=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- unit_test/crypto/crypto_test.cpp | 41 ++++++++++++++++++++++++ unit_test/s2j/s2j_test.cpp | 54 ++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 unit_test/crypto/crypto_test.cpp create mode 100644 unit_test/s2j/s2j_test.cpp diff --git a/unit_test/crypto/crypto_test.cpp b/unit_test/crypto/crypto_test.cpp new file mode 100644 index 0000000..6455b18 --- /dev/null +++ b/unit_test/crypto/crypto_test.cpp @@ -0,0 +1,41 @@ +// +// Created by dongwenzhe on 2023/3/14. +// +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include "doctest.h" +#include "crypto.h" + +TEST_SUITE("Crypto functions") { + TEST_CASE("BASE64") { + auto *pSrc = (unsigned char *)"HELLOWORLD"; + const char *encode; + + encode = base64_encode(pSrc, 16); + INFO("BASE64_ENCODING:", encode); + + unsigned char *decode; + unsigned int outputSize = 0; + decode = base64_decode(encode, &outputSize); + INFO("BASE64_DECODING:", decode); + + for(int i = 0; ichild->valueint, 3); + for(i = 0; i < strlen(origin_Server.name); i++) { + CHECK_EQ(origin_Server.name[i], testServer->child->next->valuestring[i]); + } + for(i = 0; i < strlen(origin_Server.msg.info); i++) { + CHECK_EQ(origin_Server.msg.info[i], testServer->child->next->next->child->valuestring[i]); + } + } + + TEST_CASE("J2S") { + char originServer[] = R"({"id":3, "name":"dhcpServer", "message":{"info":"opendhcpd"}})"; + cJSON *testServer = cJSON_Parse(originServer); + + s2j_create_struct_obj(pServer, Service); + s2j_struct_get_basic_element(pServer, testServer, int, id); + s2j_struct_get_basic_element(pServer, testServer, string, name); + s2j_struct_get_struct_element(pMsg, pServer, testInfo, testServer, Message, msg); + s2j_struct_get_basic_element(pMsg, testInfo, string, info); + + CHECK_EQ(pServer->id, 3); + } +} From b256424f7105673e64d098e3d7b54ee3d1439e73 Mon Sep 17 00:00:00 2001 From: dongwenze Date: Tue, 28 Mar 2023 14:21:35 +0800 Subject: [PATCH 27/31] =?UTF-8?q?OCT=201.=E5=AF=B9=E9=83=A8=E5=88=86?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E8=BF=9B=E8=A1=8C=E5=8D=95=E5=85=83=E6=B5=8B?= =?UTF-8?q?=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- unit_test/hardware/hardware_test.cpp | 20 ++++++++++++ unit_test/uthash/utarray_test.cpp | 49 ++++++++++++++++++++++++++++ unit_test/uthash/utstring_test.cpp | 46 ++++++++++++++++++++++++++ 3 files changed, 115 insertions(+) create mode 100644 unit_test/hardware/hardware_test.cpp create mode 100644 unit_test/uthash/utarray_test.cpp create mode 100644 unit_test/uthash/utstring_test.cpp diff --git a/unit_test/hardware/hardware_test.cpp b/unit_test/hardware/hardware_test.cpp new file mode 100644 index 0000000..612c2fc --- /dev/null +++ b/unit_test/hardware/hardware_test.cpp @@ -0,0 +1,20 @@ +// +// Created by dongwenzhe on 2023/3/16. +// +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include "hardware.h" +#include "doctest.h" + +TEST_SUITE("Hardware functions") { + TEST_CASE("CPU") { + PCPU_INFO cpuInfo; + memset(cpuInfo, 0, sizeof(CPU_INFO)); + + cpu_watch_init(); + + get_cpu_info(cpuInfo); + CHECK_NE(cpuInfo->nCores, 0); + CHECK_NE(cpuInfo->cpuUsed, 0); + CHECK_NE(cpuInfo->cpuCoreDesc.cpuName, nullptr); + } +} diff --git a/unit_test/uthash/utarray_test.cpp b/unit_test/uthash/utarray_test.cpp new file mode 100644 index 0000000..89e358a --- /dev/null +++ b/unit_test/uthash/utarray_test.cpp @@ -0,0 +1,49 @@ +// +// Created by dongwenzhe on 2023/3/21. +// +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include "uthash/utarray.h" +#include "doctest.h" + +typedef struct { + int a; + char *b; +} COMBO,*PCOMBO; + +UT_icd ut_combo_icd = {sizeof(COMBO), nullptr, nullptr, nullptr}; + +TEST_SUITE("UTArray") { + UT_array *nums; + COMBO t_combo; + + TEST_CASE("PUSH") { + PCOMBO p_combo; + + utarray_new(nums, &ut_combo_icd); + + t_combo.a = 1; + t_combo.b = "hello "; + utarray_push_back(nums, &t_combo); + t_combo.a = 2; + t_combo.b = "world"; + utarray_push_back(nums, &t_combo); + + while((PCOMBO)utarray_next(nums, p_combo) != nullptr) { + p_combo = (PCOMBO)utarray_next(nums, p_combo); + MESSAGE("COMBO:", p_combo->a); + MESSAGE(doctest::String(p_combo->b)); + } + } + + TEST_CASE("POP") { + PCOMBO p_combo; + MESSAGE("original len: ", utarray_len(nums)); + while(utarray_len(nums) != 0) { + p_combo = (PCOMBO)utarray_next(nums, p_combo); + utarray_pop_back(nums); + MESSAGE("len: ", utarray_len(nums)); + } + + utarray_free(nums); + } +} \ No newline at end of file diff --git a/unit_test/uthash/utstring_test.cpp b/unit_test/uthash/utstring_test.cpp new file mode 100644 index 0000000..3a789ee --- /dev/null +++ b/unit_test/uthash/utstring_test.cpp @@ -0,0 +1,46 @@ +// +// Created by dongwenzhe on 2023/3/21. +// + +//#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include "uthash/utstring.h" +#include "doctest.h" + +TEST_SUITE("UTString") { + + TEST_CASE("Append") { + UT_string *s; + UT_string *t; + + utstring_new(s); + utstring_new(t); + utstring_printf(s, "hello "); + utstring_printf(t, "world"); + + utstring_concat(s, t); + char *body = utstring_body(s); + + MESSAGE(doctest::String(body)); + CHECK(doctest::String(body) == "hello world"); + + utstring_free(s); + utstring_free(t); + } + + TEST_CASE("Binary") { + UT_string *s; + utstring_new(s); + utstring_printf(s, "hello world"); + MESSAGE(utstring_len(s)); + + char binary[] = "\xff\xff"; + utstring_bincpy(s, binary, sizeof(binary)); + MESSAGE(utstring_len(s)); + char *body = utstring_body(s); + MESSAGE(doctest::String(body)); + + utstring_clear(s); + + utstring_free(s); + } +} \ No newline at end of file From e21d9994e05b93a0f2768f1c5cecc1943c3b7521 Mon Sep 17 00:00:00 2001 From: dongwenze Date: Tue, 28 Mar 2023 18:06:46 +0800 Subject: [PATCH 28/31] =?UTF-8?q?OCT=201.=E7=BB=86=E5=8C=96JSON=E8=A7=84?= =?UTF-8?q?=E8=8C=83=E9=94=99=E8=AF=AF=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- srcs/libs/include/proto.h | 2 +- srcs/libs/include/user_errno.h | 3 +- srcs/libs/protocol/protocol.c | 4 +-- srcs/opendhcp183/query.cpp | 58 ++++++++++++++++++++++++---------- 4 files changed, 46 insertions(+), 21 deletions(-) diff --git a/srcs/libs/include/proto.h b/srcs/libs/include/proto.h index 3a53f7c..aa5d56c 100644 --- a/srcs/libs/include/proto.h +++ b/srcs/libs/include/proto.h @@ -24,7 +24,7 @@ typedef enum { TYPE_QUE_USR = 2, } PROTO_POST_TYPE; -const char *proto_msg_validation(const char *pJsonStr, const char *msgJson); +const char *proto_msg_validation(const char *pJsonStr, const char *msgJson, const char *errMsg); const char *proto_create_new(cJSON *pMsgCtx, int httpCode); const char *proto_decode_context(const char *pString, unsigned int *pVer, unsigned long long *pTm, int *pErrCode); #ifdef __cplusplus diff --git a/srcs/libs/include/user_errno.h b/srcs/libs/include/user_errno.h index 3982104..d474216 100644 --- a/srcs/libs/include/user_errno.h +++ b/srcs/libs/include/user_errno.h @@ -61,8 +61,7 @@ extern "C" { ERR_CODE(ERR_MENU_EXIT, 49, "菜单执行完后自动退出") \ ERR_CODE(ERR_HTTP_UNSUP_METHOD, 50, "不支持的 HTTP 请求方法") \ ERR_CODE(ERR_HTTP_UNSUP_PAGE, 51, "找不到 HTTP 服务") \ - ERR_CODE(ERR_PROTO_DECODE, 52, "HTTP 协议解析失败") \ - ERR_CODE(ERR_MSG_CONTENT, 53, "msgContent内容不正确") + ERR_CODE(ERR_PROTO_DECODE, 52, "HTTP 协议解析失败") #define GENERATE_ENUM(ENUM, no, x) ENUM, typedef enum { diff --git a/srcs/libs/protocol/protocol.c b/srcs/libs/protocol/protocol.c index d0044d7..3c7c92f 100644 --- a/srcs/libs/protocol/protocol.c +++ b/srcs/libs/protocol/protocol.c @@ -77,7 +77,7 @@ const char *proto_schema_validation(const char *pJsonStr) { return NULL; } -const char *proto_msg_validation(const char *pJsonStr, const char *msgJson) { +const char *proto_msg_validation(const char *pJsonStr, const char *msgJson, const char *errMsg) { json_object *pJs = json_tokener_parse(pJsonStr); if (!pJs) { cJSON *pRspRoot = cJSON_CreateObject(); @@ -95,7 +95,7 @@ const char *proto_msg_validation(const char *pJsonStr, const char *msgJson) { 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", getErrorEnumDesc(ERR_MSG_CONTENT)); + cJSON_AddStringToObject(pRspRoot, "details", errMsg); json_object_put(pSc); json_object_put(pJs); return proto_create_new(pRspRoot, 200); diff --git a/srcs/opendhcp183/query.cpp b/srcs/opendhcp183/query.cpp index 7b442e1..67d20e1 100644 --- a/srcs/opendhcp183/query.cpp +++ b/srcs/opendhcp183/query.cpp @@ -48,10 +48,29 @@ static uv_rwlock_t g_uvCacheLock; #ifdef JSON_SCHEMA_ON // clang-format off -const char *g_json_msg[] = { - R"({"type":"object","required":["rangeSet"],"properties":{"rangeSet":{"type":"array","minItems":1,"items":{"type":"object","required":["dhcpRange"]}}}})", - R"({"type":"object","required":["dhcpRange"],"properties":{"dhcpRange":{"type":"array","minItems":1}}})", - R"({"type":"object","required":["userMac"],"properties":{"userMac":{"type":"array","minItems":1}}})"}; +typedef struct { + const char *pSchJson; + const char *pErrMsg; +} JSON_POST_CTX; + +static JSON_POST_CTX g_add_msg[] = { + {R"({"type":"object","required":["rangeSet"]})", "Missing required field [rangeSet]"}, + {R"({"properties":{"rangeSet":{"type":"array","minItems":1}}})", "No content in field [rangeSet]"}, + {R"({"properties":{"rangeSet":{"items":{"type":"object","required":["dhcpRange"]}}}})", "Missing required field [dhcpRange]"}, +// {R"({"properties":{"rangeSet":{"items":{"properties":{"dhcpRange":{"type":"string","minLength":15}}}}}})", "Error in field [dhcpRange]"} +}; + +static JSON_POST_CTX g_del_msg[] = { + {R"({"type":"object","required":["dhcpRange"]})", "Missing required field [dhcpRange]"}, + {R"({"properties":{"dhcpRange":{"type":"array","minItems":1}}})", "No content in field [dhcpRange]"}, +// {R"({"properties":{"dhcpRange":{"items":{"type":"string","minLength":15}}}})", "Error in field [dhcpRange]"} +}; + +static JSON_POST_CTX g_que_msg[] = { + {R"({"type":"object","required":["userMac"]})", "Missing required field [userMac]"}, + {R"({"properties":{"userMac":{"type":"array","minItems":1}}})", "No content in field [userMac]"}, +// {R"({"properties":{"userMac":{"items":{"type":"string","minLength":17,"maxLength":17}}}})", "Error in field [userMac]"} +}; // clang-format on #endif @@ -83,10 +102,12 @@ static int dhcp_get_user_info(const char **pRsp, const char *pRequest) { } const char *pSchJson; - pSchJson = proto_msg_validation(pStrContent, g_json_msg[TYPE_QUE_USR]); - if (pSchJson != nullptr && strlen(pSchJson) > 0) { - *pRsp = pSchJson; - return ERR_SUCCESS; + for(auto i : g_que_msg) { + pSchJson = proto_msg_validation(pStrContent, i.pSchJson, i.pErrMsg); + if (pSchJson != nullptr && strlen(pSchJson) > 0) { + *pRsp = pSchJson; + return ERR_SUCCESS; + } } free((void *)pSchJson); #endif @@ -369,10 +390,12 @@ static int add_dhcpd_rangeset(const char **pRsp, const char *pRequest) { } const char *pSchJson; - pSchJson = proto_msg_validation(pStrContent, g_json_msg[TYPE_ADD_RNG]); - if (pSchJson != nullptr && strlen(pSchJson) > 0) { - *pRsp = pSchJson; - return ERR_SUCCESS; + for(auto i : g_add_msg) { + pSchJson = proto_msg_validation(pStrContent, i.pSchJson, i.pErrMsg); + if (pSchJson != nullptr && strlen(pSchJson) > 0) { + *pRsp = pSchJson; + return ERR_SUCCESS; + } } free((void *)pSchJson); #endif @@ -483,11 +506,14 @@ static int delete_dhcpd_rangeset(const char **pRsp, const char *pRequest) { } const char *pSchJson; - pSchJson = proto_msg_validation(pStrContent, g_json_msg[TYPE_DEL_RNG]); - if (pSchJson != nullptr && strlen(pSchJson) > 0) { - *pRsp = pSchJson; - return ERR_SUCCESS; + for(auto i : g_del_msg) { + pSchJson = proto_msg_validation(pStrContent, i.pSchJson, i.pErrMsg); + if (pSchJson != nullptr && strlen(pSchJson) > 0) { + *pRsp = pSchJson; + return ERR_SUCCESS; + } } + free((void*)pSchJson); #endif cJSON *pRoot = cJSON_Parse(pStrContent); From ad3ee42e09619d17a5db807a8c376c3de5b6d7ce Mon Sep 17 00:00:00 2001 From: dongwenze Date: Wed, 29 Mar 2023 16:16:02 +0800 Subject: [PATCH 29/31] =?UTF-8?q?OCT=201.=E4=BF=AE=E6=AD=A3=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E7=9A=84=E8=BF=94=E5=9B=9E=E6=B6=88=E6=81=AFIP?= =?UTF-8?q?=E6=B1=A0=E9=94=99=E8=AF=AF=202.=E5=88=A0=E9=99=A4=E6=97=A0?= =?UTF-8?q?=E7=94=A8=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- srcs/libs/include/proto.h | 6 ------ srcs/opendhcp183/query.cpp | 35 ++++++++++++++++++++++------------- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/srcs/libs/include/proto.h b/srcs/libs/include/proto.h index aa5d56c..33fbc4a 100644 --- a/srcs/libs/include/proto.h +++ b/srcs/libs/include/proto.h @@ -18,12 +18,6 @@ typedef enum { CRYPTO_AES256 = 4, } PROTO_CRYPTO_TYPE; -typedef enum { - TYPE_ADD_RNG = 0, - TYPE_DEL_RNG = 1, - TYPE_QUE_USR = 2, -} PROTO_POST_TYPE; - const char *proto_msg_validation(const char *pJsonStr, const char *msgJson, const char *errMsg); const char *proto_create_new(cJSON *pMsgCtx, int httpCode); const char *proto_decode_context(const char *pString, unsigned int *pVer, unsigned long long *pTm, int *pErrCode); diff --git a/srcs/opendhcp183/query.cpp b/srcs/opendhcp183/query.cpp index 67d20e1..f75ecac 100644 --- a/srcs/opendhcp183/query.cpp +++ b/srcs/opendhcp183/query.cpp @@ -54,20 +54,20 @@ typedef struct { } JSON_POST_CTX; static JSON_POST_CTX g_add_msg[] = { - {R"({"type":"object","required":["rangeSet"]})", "Missing required field [rangeSet]"}, - {R"({"properties":{"rangeSet":{"type":"array","minItems":1}}})", "No content in field [rangeSet]"}, + {R"({"type":"object","required":["rangeSet"]})", "Missing required field [rangeSet]"}, + {R"({"properties":{"rangeSet":{"type":"array","minItems":1}}})", "No content in field [rangeSet]"}, {R"({"properties":{"rangeSet":{"items":{"type":"object","required":["dhcpRange"]}}}})", "Missing required field [dhcpRange]"}, // {R"({"properties":{"rangeSet":{"items":{"properties":{"dhcpRange":{"type":"string","minLength":15}}}}}})", "Error in field [dhcpRange]"} }; static JSON_POST_CTX g_del_msg[] = { - {R"({"type":"object","required":["dhcpRange"]})", "Missing required field [dhcpRange]"}, + {R"({"type":"object","required":["dhcpRange"]})", "Missing required field [dhcpRange]"}, {R"({"properties":{"dhcpRange":{"type":"array","minItems":1}}})", "No content in field [dhcpRange]"}, // {R"({"properties":{"dhcpRange":{"items":{"type":"string","minLength":15}}}})", "Error in field [dhcpRange]"} }; static JSON_POST_CTX g_que_msg[] = { - {R"({"type":"object","required":["userMac"]})", "Missing required field [userMac]"}, + {R"({"type":"object","required":["userMac"]})", "Missing required field [userMac]"}, {R"({"properties":{"userMac":{"type":"array","minItems":1}}})", "No content in field [userMac]"}, // {R"({"properties":{"userMac":{"items":{"type":"string","minLength":17,"maxLength":17}}}})", "Error in field [userMac]"} }; @@ -543,16 +543,25 @@ static int delete_dhcpd_rangeset(const char **pRsp, const char *pRequest) { MYDWORD st_addr; MYDWORD en_addr; mySplit(start, end, del_range, '-'); - st_addr = htonl(inet_addr(start)); - en_addr = htonl(inet_addr(end)); - PHASH_MAP s; - HASH_FIND_INT(delMap, &st_addr, s); - if (s == nullptr) { - s = (PHASH_MAP)malloc(sizeof(HASH_MAP)); - s->key = st_addr; - s->value = en_addr; - HASH_ADD_INT(delMap, key, s); + if(isIP(start) && isIP(end)) { + st_addr = htonl(inet_addr(start)); + en_addr = htonl(inet_addr(end)); + + PHASH_MAP s; + HASH_FIND_INT(delMap, &st_addr, s); + if (s == nullptr) { + s = (PHASH_MAP)malloc(sizeof(HASH_MAP)); + s->key = st_addr; + s->value = en_addr; + HASH_ADD_INT(delMap, key, s); + } + } else { + cJSON *pdel_Item = cJSON_CreateObject(); + cJSON_AddStringToObject(pdel_Item, "dhcpRange", del_range); + cJSON_AddNumberToObject(pdel_Item, "status", ERR_INPUT_PARAMS); + cJSON_AddStringToObject(pdel_Item, "message", getErrorEnumDesc(ERR_INPUT_PARAMS)); + cJSON_AddItemToArray(pdelArray, pdel_Item); } } From 760a6eb14ae29b6f1ac1ec300c7c475cd0f95645 Mon Sep 17 00:00:00 2001 From: dongwenze Date: Thu, 30 Mar 2023 10:09:05 +0800 Subject: [PATCH 30/31] =?UTF-8?q?OCT=201.=E4=BF=AE=E6=AD=A3=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E5=86=85=E5=AD=98=E6=B3=84=E6=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- srcs/opendhcp183/query.cpp | 6 ++++++ unit_test/crypto/crypto_test.cpp | 6 ++++++ unit_test/s2j/s2j_test.cpp | 4 ++++ 3 files changed, 16 insertions(+) diff --git a/srcs/opendhcp183/query.cpp b/srcs/opendhcp183/query.cpp index f75ecac..b95c5a3 100644 --- a/srcs/opendhcp183/query.cpp +++ b/srcs/opendhcp183/query.cpp @@ -92,6 +92,7 @@ static int dhcp_get_user_info(const char **pRsp, const char *pRequest) { if (pStrContent == nullptr) { sprintf(logBuff, "Request Json error %s", pRequest); logDHCPMess(logBuff, 1); + free((void *)pStrContent); return ERR_PROTO_DECODE; } @@ -106,6 +107,7 @@ static int dhcp_get_user_info(const char **pRsp, const char *pRequest) { pSchJson = proto_msg_validation(pStrContent, i.pSchJson, i.pErrMsg); if (pSchJson != nullptr && strlen(pSchJson) > 0) { *pRsp = pSchJson; + free((void *)pStrContent); return ERR_SUCCESS; } } @@ -380,6 +382,7 @@ static int add_dhcpd_rangeset(const char **pRsp, const char *pRequest) { if (pStrContent == nullptr) { sprintf(logBuff, "Request Json error %s", pRequest); logDHCPMess(logBuff, 1); + free((void *)pStrContent); return ERR_PROTO_DECODE; } @@ -394,6 +397,7 @@ static int add_dhcpd_rangeset(const char **pRsp, const char *pRequest) { pSchJson = proto_msg_validation(pStrContent, i.pSchJson, i.pErrMsg); if (pSchJson != nullptr && strlen(pSchJson) > 0) { *pRsp = pSchJson; + free((void *)pStrContent); return ERR_SUCCESS; } } @@ -496,6 +500,7 @@ static int delete_dhcpd_rangeset(const char **pRsp, const char *pRequest) { if (pStrContent == nullptr) { sprintf(logBuff, "Request Json error %s", pRequest); logDHCPMess(logBuff, 1); + free((void*)pStrContent); return ERR_PROTO_DECODE; } @@ -510,6 +515,7 @@ static int delete_dhcpd_rangeset(const char **pRsp, const char *pRequest) { pSchJson = proto_msg_validation(pStrContent, i.pSchJson, i.pErrMsg); if (pSchJson != nullptr && strlen(pSchJson) > 0) { *pRsp = pSchJson; + free((void*)pStrContent); return ERR_SUCCESS; } } diff --git a/unit_test/crypto/crypto_test.cpp b/unit_test/crypto/crypto_test.cpp index 6455b18..afbfae1 100644 --- a/unit_test/crypto/crypto_test.cpp +++ b/unit_test/crypto/crypto_test.cpp @@ -21,6 +21,9 @@ TEST_SUITE("Crypto functions") { for(int i = 0; ichild->next->next->child->valuestring[i]); } + + cJSON_Delete(testServer); } TEST_CASE("J2S") { @@ -50,5 +52,7 @@ TEST_SUITE("S2J_TEST") { s2j_struct_get_basic_element(pMsg, testInfo, string, info); CHECK_EQ(pServer->id, 3); + cJSON_Delete(testServer); + free(pServer); } } From f55b4b56475a283e9b309f1d748472f0cfb5a606 Mon Sep 17 00:00:00 2001 From: dongwenze Date: Mon, 3 Apr 2023 09:55:01 +0800 Subject: [PATCH 31/31] =?UTF-8?q?OCT=201.=E5=A2=9E=E5=8A=A0add=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E8=A7=84=E8=8C=83=202.=E4=BF=AE=E6=AD=A3=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E4=BB=A3=E7=A0=81=E4=B8=AD=E7=9A=84=E6=9C=AA=E5=88=9D?= =?UTF-8?q?=E5=A7=8B=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- srcs/opendhcp183/query.cpp | 10 +++++++--- unit_test/hardware/hardware_test.cpp | 2 +- unit_test/uthash/utarray_test.cpp | 2 ++ 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/srcs/opendhcp183/query.cpp b/srcs/opendhcp183/query.cpp index b95c5a3..103cda4 100644 --- a/srcs/opendhcp183/query.cpp +++ b/srcs/opendhcp183/query.cpp @@ -54,9 +54,13 @@ typedef struct { } JSON_POST_CTX; static JSON_POST_CTX g_add_msg[] = { - {R"({"type":"object","required":["rangeSet"]})", "Missing required field [rangeSet]"}, - {R"({"properties":{"rangeSet":{"type":"array","minItems":1}}})", "No content in field [rangeSet]"}, - {R"({"properties":{"rangeSet":{"items":{"type":"object","required":["dhcpRange"]}}}})", "Missing required field [dhcpRange]"}, + {R"({"type":"object","required":["rangeSet"]})", "Missing required field [rangeSet]"}, + {R"({"properties":{"rangeSet":{"type":"array","minItems":1}}})", "No content in array [rangeSet]"}, + {R"({"properties":{"rangeSet":{"items":{"type":"object","required":["dhcpRange"]}}}})", "Missing required field [dhcpRange]"}, + {R"({"properties":{"rangeSet":{"items":{"properties":{"netmask":{"type":"string"}}}}}})", "[netmask] should be a string value"}, + {R"({"properties":{"rangeSet":{"items":{"properties":{"domainServer":{"type":"string"}}}}}})", "[domainServer] should be a string value"}, + {R"({"properties":{"rangeSet":{"items":{"properties":{"gateway":{"type":"string"}}}}}})", "[gateway] should be a string value"}, + {R"({"properties":{"rangeSet":{"items":{"properties":{"leaseTime":{"type":"integer"}}}}}})", "[leaseTime] should be an integer value"}, // {R"({"properties":{"rangeSet":{"items":{"properties":{"dhcpRange":{"type":"string","minLength":15}}}}}})", "Error in field [dhcpRange]"} }; diff --git a/unit_test/hardware/hardware_test.cpp b/unit_test/hardware/hardware_test.cpp index 612c2fc..3df6ba9 100644 --- a/unit_test/hardware/hardware_test.cpp +++ b/unit_test/hardware/hardware_test.cpp @@ -8,7 +8,7 @@ TEST_SUITE("Hardware functions") { TEST_CASE("CPU") { PCPU_INFO cpuInfo; - memset(cpuInfo, 0, sizeof(CPU_INFO)); + memset(&cpuInfo, 0, sizeof(PCPU_INFO)); cpu_watch_init(); diff --git a/unit_test/uthash/utarray_test.cpp b/unit_test/uthash/utarray_test.cpp index 89e358a..be26e9b 100644 --- a/unit_test/uthash/utarray_test.cpp +++ b/unit_test/uthash/utarray_test.cpp @@ -18,6 +18,7 @@ TEST_SUITE("UTArray") { TEST_CASE("PUSH") { PCOMBO p_combo; + memset(&p_combo, 0, sizeof(PCOMBO)); utarray_new(nums, &ut_combo_icd); @@ -37,6 +38,7 @@ TEST_SUITE("UTArray") { TEST_CASE("POP") { PCOMBO p_combo; + memset(&p_combo, 0, sizeof(PCOMBO)); MESSAGE("original len: ", utarray_len(nums)); while(utarray_len(nums) != 0) { p_combo = (PCOMBO)utarray_next(nums, p_combo);