// SPDX-License-Identifier: AGPL-3.0-or-later #ifndef DISABLE_JSON #include "routing/compat/json_config.h" #include "routing/compat/node.h" #include "routing/compat/router.h" #include "ud3tn/result.h" #include "testud3tn_unity.h" #include #include #include static const char *TEST_STRING_ADD_FULL = ( "{" "\"command\": \"ADD\"," "\"node_id\": \"dtn://next-hop.dtn/\"," "\"cla_addr\": \"mtcp:127.0.0.1:5555\"," "\"reachable_eids\": [" "\"ipn:1.0\"," "\"dtn://abc.dtn/\"" "]," "\"contact_list\": [" "{" "\"start\": 1234," "\"end\": 5678," "\"data_rate\": 1234," "\"reachable_eids\": [" "\"ipn:2.0\"," "\"ipn:3.0\"" "]" "}," "{" "\"start\": 5679," "\"end\": 6789," "\"data_rate\": 5555" "}" "]" "}" ); static const char *TEST_STRING_INVALID_CMD = ( "{" "\"command\": \"update\"," "\"node_id\": \"dtn://next-hop.dtn/\"" "}" ); static const char *TEST_STRING_DELETE = ( "{" "\"command\": \"DELETE\"," "\"node_id\": \"ipn:2.0\"" "}" ); static const char *TEST_STRING_SIMPLE_UPDATE = ( "{" "\"command\": \"UPDATE\"," "\"cla_addr\": \"mtcp:127.0.0.1:5555\"," "\"node_id\": \"dtn://test/\"" "}" ); static const char *TEST_STRING_SIMPLE_QUERY = ( "{" "\"command\": \"QUERY\"," "\"node_id\": \"dtn://test/\"" "}" ); static const char *TEST_STRING_BAD_EIDS_TYPE = ( "{" "\"command\": \"ADD\"," "\"node_id\": \"dtn://test/\"," "\"reachable_eids\": \"not-an-array\"" "}" ); static const char *TEST_STRING_BAD_CONTACTS_TYPE = ( "{" "\"command\": \"ADD\"," "\"node_id\": \"dtn://test/\"," "\"contact_list\": 123" "}" ); static const char *TEST_STRING_NEG_START = ( "{" "\"command\":\"ADD\"," "\"node_id\":\"dtn://test/\"," "\"contact_list\":[{" "\"start\": -1," "\"end\": 100," "\"data_rate\": 10" "}]" "}" ); // UINT64_MAX/1000 = 18446744073709551 (rounded down) static const char *TEST_STRING_OVERFLOW_START = ( "{" "\"command\": \"ADD\"," "\"node_id\": \"dtn://test/\"," "\"contact_list\": [{" "\"start\": 18446744073709551," "\"end\": 1," "\"data_rate\": 1" "}]" "}" ); static const char *TEST_STRING_NO_OVERFLOW_START = ( "{" "\"command\": \"ADD\"," "\"node_id\": \"dtn://test/\"," "\"contact_list\": [{" "\"start\": 18446744073709550," "\"end\": 1," "\"data_rate\": 1" "}]" "}" ); // UINT32_MAX = 4294967295 static const char *TEST_STRING_OVERFLOW_RATE = ( "{" "\"command\": \"ADD\"," "\"node_id\": \"dtn://test/\"," "\"contact_list\": [{" "\"start\": 1," "\"end\": 1," "\"data_rate\": 4294967296" "}]" "}" ); static const char *TEST_STRING_NO_OVERFLOW_RATE = ( "{" "\"command\": \"ADD\"," "\"node_id\": \"dtn://test/\"," "\"contact_list\": [{" "\"start\": 1," "\"end\": 1," "\"data_rate\": 4294967295" "}]" "}" ); static const char *TEST_STRING_BAD_NODE_EID = ( "{" "\"command\": \"ADD\"," "\"cla_addr\": \"mtcp:127.0.0.1:5555\"," "\"node_id\": \"not-an-eid\"" "}" ); static const char *TEST_STRING_BAD_EID_IN_LIST = ( "{" "\"command\": \"ADD\"," "\"node_id\": \"dtn://test/\"," "\"reachable_eids\": [\"invalid_eid\"]" "}" ); static const char *TEST_STRING_BAD_EID_TYPE_IN_LIST = ( "{" "\"command\": \"ADD\"," "\"node_id\": \"dtn://test/\"," "\"reachable_eids\": [55]" "}" ); static const char *TEST_STRING_BAD_EID_TEST_VALID_COUNTEREXAMPLE = ( "{" "\"command\": \"ADD\"," "\"node_id\": \"dtn://test/\"," "\"contact_list\": [{" "\"start\": 1," "\"end\": 1," "\"data_rate\": 1," "\"reachable_eids\": [\"ipn:1.0\"]" "}]" "}" ); static const char *TEST_STRING_BAD_EID_IN_CONTACT_LIST = ( "{" "\"command\": \"ADD\"," "\"node_id\": \"dtn://test/\"," "\"contact_list\": [{" "\"start\": 1," "\"end\": 1," "\"data_rate\": 1," "\"reachable_eids\": [\"invalid_eid\"]" "}]" "}" ); static const char *TEST_STRING_BAD_EID_TYPE_IN_CONTACT_LIST = ( "{" "\"command\": \"ADD\"," "\"node_id\": \"dtn://test/\"," "\"contact_list\": [{" "\"start\": 1," "\"end\": 1," "\"data_rate\": 1," "\"reachable_eids\": [55]" "}]" "}" ); TEST_GROUP(json_config); TEST_SETUP(json_config) {} TEST_TEAR_DOWN(json_config) {} TEST(json_config, parse_valid_full) { struct router_command *const cmd = parse_json_command( TEST_STRING_ADD_FULL, strlen(TEST_STRING_ADD_FULL) ); TEST_ASSERT_NOT_NULL(cmd); TEST_ASSERT_EQUAL(ROUTER_COMMAND_ADD, cmd->type); TEST_ASSERT_NOT_NULL(cmd->data); TEST_ASSERT_EQUAL_STRING("dtn://next-hop.dtn/", cmd->data->eid); TEST_ASSERT_EQUAL_STRING("mtcp:127.0.0.1:5555", cmd->data->cla_addr); const struct endpoint_list *const e = cmd->data->endpoints; TEST_ASSERT_NOT_NULL(e); TEST_ASSERT_EQUAL_STRING("ipn:1.0", e->eid); TEST_ASSERT_NOT_NULL(e->next); TEST_ASSERT_EQUAL_STRING("dtn://abc.dtn/", e->next->eid); TEST_ASSERT_NULL(e->next->next); const struct contact_list *const c = cmd->data->contacts; TEST_ASSERT_NOT_NULL(c); TEST_ASSERT_EQUAL_UINT64(1234 * 1000ULL, c->data->from_ms); TEST_ASSERT_EQUAL_UINT64(5678 * 1000ULL, c->data->to_ms); TEST_ASSERT_EQUAL_UINT32(1234U, c->data->bitrate_bytes_per_s); const struct endpoint_list *const ce = c->data->contact_endpoints; TEST_ASSERT_EQUAL_STRING("ipn:2.0", ce->eid); TEST_ASSERT_EQUAL_STRING("ipn:3.0", ce->next->eid); TEST_ASSERT_NULL(ce->next->next); TEST_ASSERT_NOT_NULL(c->next); TEST_ASSERT_EQUAL_UINT64(5679 * 1000ULL, c->next->data->from_ms); TEST_ASSERT_EQUAL_UINT64(6789 * 1000ULL, c->next->data->to_ms); TEST_ASSERT_EQUAL_UINT32(5555U, c->next->data->bitrate_bytes_per_s); TEST_ASSERT_NULL(c->next->data->contact_endpoints); TEST_ASSERT_NULL(c->next->next); free_node(cmd->data); free(cmd); } TEST(json_config, parse_simple) { struct router_command *cmd; cmd = parse_json_command( TEST_STRING_SIMPLE_UPDATE, strlen(TEST_STRING_SIMPLE_UPDATE) ); TEST_ASSERT_NOT_NULL(cmd); TEST_ASSERT_EQUAL(ROUTER_COMMAND_UPDATE, cmd->type); TEST_ASSERT_NOT_NULL(cmd->data); TEST_ASSERT_EQUAL_STRING("dtn://test/", cmd->data->eid); TEST_ASSERT_EQUAL_STRING("mtcp:127.0.0.1:5555", cmd->data->cla_addr); TEST_ASSERT_NULL(cmd->data->endpoints); TEST_ASSERT_NULL(cmd->data->contacts); free_node(cmd->data); free(cmd); cmd = parse_json_command( TEST_STRING_SIMPLE_QUERY, // also support incl null terminator strlen(TEST_STRING_SIMPLE_QUERY) + 1 ); TEST_ASSERT_NOT_NULL(cmd); TEST_ASSERT_EQUAL(ROUTER_COMMAND_QUERY, cmd->type); TEST_ASSERT_NOT_NULL(cmd->data); TEST_ASSERT_EQUAL_STRING("dtn://test/", cmd->data->eid); TEST_ASSERT_NULL(cmd->data->cla_addr); TEST_ASSERT_NULL(cmd->data->endpoints); TEST_ASSERT_NULL(cmd->data->contacts); free_node(cmd->data); free(cmd); } TEST(json_config, parse_invalid_command_type) { struct router_command *cmd = parse_json_command( TEST_STRING_INVALID_CMD, strlen(TEST_STRING_INVALID_CMD) ); TEST_ASSERT_NULL(cmd); } TEST(json_config, parse_delete) { struct router_command *cmd = parse_json_command( TEST_STRING_DELETE, strlen(TEST_STRING_DELETE) ); TEST_ASSERT_NOT_NULL(cmd); TEST_ASSERT_EQUAL(ROUTER_COMMAND_DELETE, cmd->type); TEST_ASSERT_EQUAL_STRING("ipn:2.0", cmd->data->eid); free_node(cmd->data); free(cmd); } TEST(json_config, parse_invalid_types_and_overflows) { struct router_command *cmd; cmd = parse_json_command( TEST_STRING_BAD_EIDS_TYPE, strlen(TEST_STRING_BAD_EIDS_TYPE) ); TEST_ASSERT_NULL(cmd); cmd = parse_json_command( TEST_STRING_BAD_CONTACTS_TYPE, strlen(TEST_STRING_BAD_CONTACTS_TYPE) ); TEST_ASSERT_NULL(cmd); cmd = parse_json_command( TEST_STRING_NEG_START, strlen(TEST_STRING_NEG_START) ); TEST_ASSERT_NULL(cmd); cmd = parse_json_command( TEST_STRING_OVERFLOW_START, strlen(TEST_STRING_OVERFLOW_START) ); TEST_ASSERT_NULL(cmd); cmd = parse_json_command( TEST_STRING_NO_OVERFLOW_START, strlen(TEST_STRING_NO_OVERFLOW_START) ); TEST_ASSERT_NOT_NULL(cmd); free_node(cmd->data); free(cmd); cmd = parse_json_command( TEST_STRING_OVERFLOW_RATE, strlen(TEST_STRING_OVERFLOW_RATE) ); TEST_ASSERT_NULL(cmd); cmd = parse_json_command( TEST_STRING_NO_OVERFLOW_RATE, strlen(TEST_STRING_NO_OVERFLOW_RATE) ); TEST_ASSERT_NOT_NULL(cmd); free_node(cmd->data); free(cmd); } TEST(json_config, parse_invalid_eids) { struct router_command *cmd; cmd = parse_json_command( TEST_STRING_BAD_NODE_EID, strlen(TEST_STRING_BAD_NODE_EID) ); TEST_ASSERT_NULL(cmd); cmd = parse_json_command( TEST_STRING_BAD_EID_IN_LIST, strlen(TEST_STRING_BAD_EID_IN_LIST) ); TEST_ASSERT_NULL(cmd); cmd = parse_json_command( TEST_STRING_BAD_EID_TYPE_IN_LIST, strlen(TEST_STRING_BAD_EID_TYPE_IN_LIST) ); TEST_ASSERT_NULL(cmd); cmd = parse_json_command( TEST_STRING_BAD_EID_TEST_VALID_COUNTEREXAMPLE, strlen(TEST_STRING_BAD_EID_TEST_VALID_COUNTEREXAMPLE) ); TEST_ASSERT_NOT_NULL(cmd); free_node(cmd->data); free(cmd); cmd = parse_json_command( TEST_STRING_BAD_EID_IN_CONTACT_LIST, strlen(TEST_STRING_BAD_EID_IN_CONTACT_LIST) ); TEST_ASSERT_NULL(cmd); cmd = parse_json_command( TEST_STRING_BAD_EID_TYPE_IN_CONTACT_LIST, strlen(TEST_STRING_BAD_EID_TYPE_IN_CONTACT_LIST) ); TEST_ASSERT_NULL(cmd); } // Normally those internal functions are not exported, but we'd like to test // them separately. struct endpoint_list *parse_json_eid_list(json_t *const jp); struct contact_list *parse_json_contact_list(json_t *const jp, struct node *node); TEST(json_config, direct_parse_json_eid_list) { json_t *const arr = json_array(); json_array_append_new(arr, json_string("ipn:9.9")); json_array_append_new(arr, json_string("dtn://xyz/")); struct endpoint_list *list = parse_json_eid_list(arr); TEST_ASSERT_NOT_NULL(list); TEST_ASSERT_EQUAL_STRING("ipn:9.9", list->eid); TEST_ASSERT_EQUAL_STRING("dtn://xyz/", list->next->eid); while (list) list = endpoint_list_free(list); json_decref(arr); // invalid element json_t *const arr2 = json_array(); json_array_append_new(arr2, json_integer(42)); list = parse_json_eid_list(arr2); TEST_ASSERT_NULL(list); json_decref(arr2); } TEST(json_config, direct_parse_json_contact_list) { struct node *const node = node_create(NULL); TEST_ASSERT_NOT_NULL(node); // valid contact w/o reachable_eids json_t *const arr = json_array(); json_t *const o = json_object(); json_object_set_new(o, "start", json_integer(10)); json_object_set_new(o, "end", json_integer(20)); json_object_set_new(o, "data_rate", json_integer(100)); json_array_append_new(arr, o); struct contact_list *cl = parse_json_contact_list(arr, node); TEST_ASSERT_NOT_NULL(cl); TEST_ASSERT_EQUAL_UINT64(10 * 1000ULL, cl->data->from_ms); TEST_ASSERT_EQUAL_UINT64(20 * 1000ULL, cl->data->to_ms); TEST_ASSERT_EQUAL_UINT32(100U, cl->data->bitrate_bytes_per_s); TEST_ASSERT_NULL(cl->data->contact_endpoints); while (cl) cl = contact_list_free(cl); json_decref(arr); // invalid reachable_eids type json_t *const arr2 = json_array(); json_t *const o2 = json_object(); json_object_set_new(o2, "start", json_integer(1)); json_object_set_new(o2, "end", json_integer(2)); json_object_set_new(o2, "data_rate", json_integer(3)); json_object_set_new(o2, "reachable_eids", json_string("oops")); json_array_append_new(arr2, o2); cl = parse_json_contact_list(arr2, node); TEST_ASSERT_NULL(cl); json_decref(arr2); free_node(node); } TEST_GROUP_RUNNER(json_config) { RUN_TEST_CASE(json_config, parse_valid_full); RUN_TEST_CASE(json_config, parse_simple); RUN_TEST_CASE(json_config, parse_invalid_command_type); RUN_TEST_CASE(json_config, parse_delete); RUN_TEST_CASE(json_config, parse_invalid_types_and_overflows); RUN_TEST_CASE(json_config, parse_invalid_eids); RUN_TEST_CASE(json_config, direct_parse_json_eid_list); RUN_TEST_CASE(json_config, direct_parse_json_contact_list); } #endif // DISABLE_JSON