// 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/eid.h" #include "jansson.h" #include #include struct endpoint_list *parse_json_eid_list(json_t *const jp) { struct endpoint_list *first = NULL, *last = NULL, *next; for (size_t i = 0; i < json_array_size(jp); i++) { next = malloc(sizeof(struct endpoint_list)); if (!next) goto fail; json_t *const e = json_array_get(jp, i); if (!json_is_string(e)) { free(next); goto fail; } next->eid = strdup(json_string_value(e)); if (!next->eid) { free(next); goto fail; } next->next = NULL; if (last) { last->next = next; last = next; } else { first = last = next; } } return first; fail: while (first) first = endpoint_list_free(first); return NULL; } struct contact_list *parse_json_contact_list(json_t *const jp, struct node *node) { struct contact_list *first = NULL, *last = NULL, *next; for (size_t i = 0; i < json_array_size(jp); i++) { next = malloc(sizeof(struct contact_list)); if (!next) goto fail; json_t *const e = json_array_get(jp, i); next->data = contact_create(node); if (!json_is_object(e) || !next->data) { free(next); goto fail; } next->next = NULL; json_t *const c_start_j = json_object_get(e, "start"); json_t *const c_end_j = json_object_get(e, "end"); json_t *const c_drate_j = json_object_get(e, "data_rate"); json_t *const c_eids_j = json_object_get(e, "reachable_eids"); if (!json_is_integer(c_start_j) || !json_is_integer(c_end_j) || !json_is_integer(c_drate_j) || (c_eids_j && !json_is_array(c_eids_j))) { free_contact(next->data); free(next); goto fail; } const json_int_t c_start = json_integer_value(c_start_j); const json_int_t c_end = json_integer_value(c_end_j); const json_int_t c_drate = json_integer_value(c_drate_j); const uint64_t c_start_u64 = (uint64_t)c_start; const uint64_t c_end_u64 = (uint64_t)c_end; const uint64_t c_drate_u64 = (uint64_t)c_drate; if (c_start < 0 || c_end < 0 || c_drate < 0 || c_start_u64 >= (UINT64_MAX / 1000) || c_end_u64 >= (UINT64_MAX / 1000) || c_drate_u64 > UINT32_MAX) { free_contact(next->data); free(next); goto fail; } next->data->from_ms = c_start_u64 * 1000; next->data->to_ms = c_end_u64 * 1000; next->data->bitrate_bytes_per_s = c_drate_u64; // NOTE we checked above that it is a valid array if it exists. if (c_eids_j) { next->data->contact_endpoints = parse_json_eid_list(c_eids_j); // parsing the contact EID list failed if (!next->data->contact_endpoints && json_array_size(c_eids_j)) { free_contact(next->data); free(next); goto fail; } } if (last) { last->next = next; last = next; } else { first = last = next; } } return first; fail: while (first) first = contact_list_free(first); return NULL; } bool command_is_valid(const struct router_command *const cmd) { ASSERT(cmd && cmd->data); if (!eidstr_is_valid(cmd->data->eid)) return false; struct endpoint_list *elist = cmd->data->endpoints; while (elist) { if (!eidstr_is_valid(elist->eid)) return false; elist = elist->next; } struct contact_list *clist = cmd->data->contacts; while (clist) { ASSERT(clist->data); elist = clist->data->contact_endpoints; while (elist) { if (!eidstr_is_valid(elist->eid)) return false; elist = elist->next; } clist = clist->next; } // NOTE: The contact start/end times and potential overlaps are checked // via node_prepare_and_verify in the router agent after parsing. return true; } struct router_command *parse_json_command( const char *const buf, size_t len) { // Some basic validity checks and compatibility with buffers including // the null terminator also in the length (Jansson does not support it). if (len < 2 || buf[0] != '{') return NULL; if (buf[len - 1] == '\0') len--; if (buf[len - 1] != '}') return NULL; json_t *const jp = json_loadb(buf, len, 0, NULL); if (!jp) return NULL; struct router_command *const cmd = malloc(sizeof(struct router_command)); if (!cmd) goto fail_cmd; cmd->data = node_create(NULL); if (!cmd->data) goto fail_node; if (!json_is_object(jp)) goto fail; json_t *const cmd_type = json_object_get(jp, "command"); if (!json_is_string(cmd_type)) goto fail; if (!strcmp(json_string_value(cmd_type), "ADD")) cmd->type = ROUTER_COMMAND_ADD; else if (!strcmp(json_string_value(cmd_type), "UPDATE")) cmd->type = ROUTER_COMMAND_UPDATE; else if (!strcmp(json_string_value(cmd_type), "DELETE")) cmd->type = ROUTER_COMMAND_DELETE; else if (!strcmp(json_string_value(cmd_type), "QUERY")) cmd->type = ROUTER_COMMAND_QUERY; else goto fail; json_t *const cmd_node_id = json_object_get(jp, "node_id"); // mandatory if (!json_is_string(cmd_node_id)) goto fail; cmd->data->eid = strdup(json_string_value(cmd_node_id)); json_t *const cmd_cla_addr = json_object_get(jp, "cla_addr"); // optional if (json_is_string(cmd_cla_addr)) cmd->data->cla_addr = strdup(json_string_value(cmd_cla_addr)); else if (cmd_cla_addr) // invalid type goto fail; json_t *const cmd_eid_list = json_object_get(jp, "reachable_eids"); if (json_is_array(cmd_eid_list)) { cmd->data->endpoints = parse_json_eid_list(cmd_eid_list); // parsing the EID list failed if (!cmd->data->endpoints && json_array_size(cmd_eid_list)) goto fail; } else if (cmd_eid_list) { // invalid type goto fail; } json_t *const cmd_contact_list = json_object_get(jp, "contact_list"); if (json_is_array(cmd_contact_list)) { cmd->data->contacts = parse_json_contact_list( cmd_contact_list, cmd->data ); // parsing the contact list failed if (!cmd->data->contacts && json_array_size(cmd_contact_list)) goto fail; } else if (cmd_contact_list) { // invalid type goto fail; } if (!command_is_valid(cmd)) goto fail; json_decref(jp); return cmd; fail: free_node(cmd->data); fail_node: free(cmd); fail_cmd: json_decref(jp); return NULL; } #endif // DISABLE_JSON