mirror of
https://gitlab.com/d3tn/ud3tn.git
synced 2026-08-13 12:33:27 +02:00
This adds the JSON parser logic as an alternative to the custom legacy format and also adds necessary tests. Signed-off-by: Felix Walter <felix.walter@d3tn.com>
503 lines
12 KiB
C
503 lines
12 KiB
C
// 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 <jansson.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
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
|