ud3tn/components/bundle6/parser.c
Felix Walter 26e91ad69e Remove config.h: Move all definitions to individual headers
This moves all definitions from config.h to individual header files and
makes them configurable (i.e., does not define when already defined).

Signed-off-by: Felix Walter <felix.walter@d3tn.com>
2023-09-29 14:43:21 +02:00

716 lines
19 KiB
C

// SPDX-License-Identifier: BSD-3-Clause OR Apache-2.0
#include "bundle6/parser.h"
#include "ud3tn/common.h"
#include <inttypes.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
struct parser *bundle6_parser_init(
struct bundle6_parser *state,
void (*send_callback)(struct bundle *, void *), void *param)
{
state->basedata = malloc(sizeof(struct parser));
if (state->basedata == NULL)
return NULL;
state->send_callback = send_callback;
state->send_param = param;
state->bundle = NULL;
state->dict = NULL;
state->basedata->status = PARSER_STATUS_ERROR;
if (bundle6_parser_reset(state) != UD3TN_OK)
return NULL;
return state->basedata;
}
enum ud3tn_result bundle6_parser_reset(struct bundle6_parser *state)
{
if (state->basedata->status == PARSER_STATUS_GOOD &&
state->current_stage == PARSER_STAGE_VERSION)
return UD3TN_OK;
state->basedata->status = PARSER_STATUS_GOOD;
state->basedata->flags = PARSER_FLAG_NONE;
state->error = PARSER_ERROR_NONE;
state->current_stage = PARSER_STAGE_VERSION;
state->next_stage = PARSER_STAGE_VERSION;
state->current_block = PARSER_BLOCK_PRIMARY;
sdnv_reset(&state->sdnv_state);
state->primary_bytes_remaining = 0;
state->cur_bytes_remaining = 0;
state->current_index = 0;
state->current_size = sizeof(struct bundle);
state->last_block = 0;
if (state->bundle != NULL)
bundle_reset(state->bundle);
else
state->bundle = bundle_init();
if (state->bundle == NULL)
return UD3TN_FAIL;
if (state->dict != NULL) {
free(state->dict);
state->dict = NULL;
}
state->current_block_entry = &state->bundle->blocks;
return UD3TN_OK;
}
enum ud3tn_result bundle6_parser_deinit(struct bundle6_parser *state)
{
free(state->basedata);
if (state->bundle != NULL)
bundle_free(state->bundle);
if (state->dict != NULL)
free(state->dict);
return UD3TN_OK;
}
static inline bool bundle6_parser_length_known(struct bundle6_parser *state)
{
return state->current_stage > PARSER_STAGE_BLOCK_LENGTH;
}
static inline bool bundle6_parser_data_done(struct bundle6_parser *state)
{
return state->cur_bytes_remaining == 0;
}
static inline bool bundle6_parser_is_last_block(struct bundle6_parser *state)
{
return state->last_block;
}
static inline void bundle6_parser_begin_block(
struct bundle6_parser *state, uint8_t type)
{
struct bundle_block *new = bundle_block_create(type);
if (new == NULL) {
state->basedata->status = PARSER_STATUS_ERROR;
return;
}
if (type == BUNDLE_BLOCK_TYPE_PAYLOAD) {
state->bundle->payload_block = new;
state->current_block = PARSER_BLOCK_PAYLOAD;
} else {
state->current_block = PARSER_BLOCK_EXTENSION;
}
*state->current_block_entry = bundle_block_entry_create(new);
if (*state->current_block_entry == NULL) {
bundle_block_free(new);
state->basedata->status = PARSER_STATUS_ERROR;
return;
}
}
static inline void bundle6_parser_end_block(struct bundle6_parser *state)
{
state->current_block_entry = &(*state->current_block_entry)->next;
}
static char *bundle6_read_eid(const char *dict, const size_t dict_len,
struct bundle6_eid_reference ref)
{
if (ref.scheme_offset >= dict_len || ref.ssp_offset >= dict_len)
return NULL;
size_t scheme_len = strlen(&dict[ref.scheme_offset]);
size_t ssp_len = strlen(&dict[ref.ssp_offset]);
char *eid = malloc(scheme_len + ssp_len + 2);
if (eid == NULL)
return NULL;
if (ref.scheme_offset + scheme_len > dict_len)
goto fail;
if (strchr(&dict[ref.scheme_offset], ':') != NULL)
goto fail; // Scheme may not contain colons
memcpy(eid, &dict[ref.scheme_offset], scheme_len);
eid[scheme_len] = ':';
if (ref.ssp_offset + ssp_len > dict_len)
goto fail;
memcpy(&eid[scheme_len + 1], &dict[ref.ssp_offset], ssp_len);
eid[scheme_len + ssp_len + 1] = 0;
return eid;
fail:
free(eid);
return NULL;
}
static inline size_t decimal_digits(uint64_t number)
{
size_t digits = 0;
if (number == 0)
return 1;
while (number != 0) {
digits++;
number /= 10;
}
return digits;
}
static char *bundle6_create_eid_cbhe(const struct bundle6_eid_reference ref)
{
if (ref.scheme_offset == 0 || ref.ssp_offset == 0)
return strdup("dtn:none");
// "ipn:" + nodenum + "." + servicenum + "\0"
const size_t length = 4
+ decimal_digits(ref.scheme_offset)
+ 1
+ decimal_digits(ref.ssp_offset)
+ 1;
char *const eid = malloc(length);
if (eid == NULL)
return NULL;
snprintf(eid, length, "ipn:%" PRIu64 ".%" PRIu64,
ref.scheme_offset, ref.ssp_offset);
return eid;
}
static bool bundle_is_valid(struct bundle *const bundle)
{
return bundle->payload_block != NULL;
}
static inline void bundle6_parser_send_bundle(struct bundle6_parser *state)
{
struct bundle *ptr = state->bundle;
state->bundle = NULL;
if (state->send_callback == NULL || !bundle_is_valid(ptr))
bundle_free(ptr);
else
state->send_callback(ptr, state->send_param);
}
static inline void bundle6_parser_next(struct bundle6_parser *state)
{
switch (state->next_stage) {
case PARSER_STAGE_PROC_FLAGS:
sdnv_reset(&state->sdnv_state);
break;
case PARSER_STAGE_BLOCK_LENGTH:
sdnv_reset(&state->sdnv_state);
break;
case PARSER_STAGE_DESTINATION_EID_SCHEME:
state->primary_bytes_remaining =
state->bundle->primary_block_length;
sdnv_reset(&state->sdnv_state);
break;
case PARSER_STAGE_DESTINATION_EID_SSP:
sdnv_reset(&state->sdnv_state);
break;
case PARSER_STAGE_SOURCE_EID_SCHEME:
sdnv_reset(&state->sdnv_state);
break;
case PARSER_STAGE_SOURCE_EID_SSP:
sdnv_reset(&state->sdnv_state);
break;
case PARSER_STAGE_REPORT_EID_SCHEME:
sdnv_reset(&state->sdnv_state);
break;
case PARSER_STAGE_REPORT_EID_SSP:
sdnv_reset(&state->sdnv_state);
break;
case PARSER_STAGE_CUSTODIAN_EID_SCHEME:
sdnv_reset(&state->sdnv_state);
break;
case PARSER_STAGE_CUSTODIAN_EID_SSP:
sdnv_reset(&state->sdnv_state);
break;
case PARSER_STAGE_TIMESTAMP:
sdnv_reset(&state->sdnv_state);
break;
case PARSER_STAGE_SEQUENCE_NUM:
state->bundle->creation_timestamp_ms *= 1000; // s -> ms
sdnv_reset(&state->sdnv_state);
break;
case PARSER_STAGE_LIFETIME:
sdnv_reset(&state->sdnv_state);
break;
case PARSER_STAGE_DICT_LENGTH:
state->bundle->lifetime_ms *= 1000; // s -> ms
sdnv_reset(&state->sdnv_state);
break;
case PARSER_STAGE_DICTIONARY:
state->current_index = 0;
// CBHE (RFC 6260)
if (state->dict_length == 0) {
state->bundle->source = bundle6_create_eid_cbhe(
state->source_eidref
);
// destination
state->bundle->destination = bundle6_create_eid_cbhe(
state->destination_eidref
);
// report-to
state->bundle->report_to = bundle6_create_eid_cbhe(
state->report_to_eidref
);
// custodian
state->bundle->current_custodian =
bundle6_create_eid_cbhe(
state->custodian_eidref
);
if (state->bundle->source == NULL ||
state->bundle->destination == NULL ||
state->bundle->report_to == NULL ||
state->bundle->current_custodian == NULL
) {
state->basedata->status = PARSER_STATUS_ERROR;
break;
}
// CBHE = no dict -> skip through!
if (bundle_is_fragmented(state->bundle)) {
state->next_stage =
PARSER_STAGE_FRAGMENT_OFFSET;
} else {
state->next_stage =
PARSER_STAGE_BLOCK_TYPE;
// see PARSER_STAGE_BLOCK_TYPE
state->current_block = PARSER_BLOCK_GENERIC;
}
break;
}
state->cur_bytes_remaining = state->dict_length;
state->current_size += state->cur_bytes_remaining + 1;
if (state->current_size > BUNDLE_MAX_SIZE) {
state->basedata->status = PARSER_STATUS_ERROR;
} else {
// We ensure that the dict is zero-terminated
state->dict = malloc(state->cur_bytes_remaining + 1);
if (state->dict == NULL)
state->basedata->status = PARSER_STATUS_ERROR;
else
state->dict[state->cur_bytes_remaining] = 0;
}
break;
case PARSER_STAGE_FRAGMENT_OFFSET:
sdnv_reset(&state->sdnv_state);
break;
case PARSER_STAGE_ADU_LENGTH:
sdnv_reset(&state->sdnv_state);
break;
case PARSER_STAGE_BLOCK_TYPE:
state->current_block = PARSER_BLOCK_GENERIC;
break;
case PARSER_STAGE_BLOCK_FLAGS:
sdnv_reset(&state->sdnv_state);
break;
case PARSER_STAGE_BLOCK_EID_REF_CNT:
sdnv_reset(&state->sdnv_state);
break;
case PARSER_STAGE_BLOCK_EID_REF_SCH:
sdnv_reset(&state->sdnv_state);
break;
case PARSER_STAGE_BLOCK_EID_REF_SSP:
sdnv_reset(&state->sdnv_state);
break;
case PARSER_STAGE_BLOCK_DATA_LENGTH:
sdnv_reset(&state->sdnv_state);
break;
case PARSER_STAGE_BLOCK_DATA:
state->current_index = 0;
state->cur_bytes_remaining = 0;
state->basedata->next_bytes =
(*state->current_block_entry)->data->length;
state->current_size += state->basedata->next_bytes;
if (state->current_size > BUNDLE_MAX_SIZE) {
state->basedata->status = PARSER_STATUS_ERROR;
} else {
state->basedata->next_buffer =
malloc(state->basedata->next_bytes);
if (state->basedata->next_buffer == NULL) {
state->basedata->status = PARSER_STATUS_ERROR;
} else {
(*state->current_block_entry)->data->data =
state->basedata->next_buffer;
state->basedata->flags |= PARSER_FLAG_BULK_READ;
}
}
break;
default:
break;
}
state->current_stage = state->next_stage;
if (state->current_stage == PARSER_STAGE_DONE) {
state->basedata->status = PARSER_STATUS_DONE;
bundle6_parser_send_bundle(state);
}
}
/**
* Checks if the SDNV parser is done or has encountered an error
*
* In the former case, sets the parser to continue with the next stage,
* in the latter case, "forwards" the error to the parser.
*/
static inline int bundle6_parser_wait_for_sdnv(
struct bundle6_parser *state, enum bundle6_parser_stage next_stage)
{
switch (state->sdnv_state.status) {
case SDNV_IN_PROGRESS:
/* intentional, do nothing - unless
* a termination state is reached
*/
break;
case SDNV_DONE:
state->next_stage = next_stage;
return 1;
case SDNV_ERROR:
state->basedata->status = PARSER_STATUS_ERROR;
state->error = PARSER_ERROR_SDNV_FAILURE;
break;
}
return 0;
}
static void bundle6_parser_read_byte(struct bundle6_parser *state,
uint8_t byte)
{
if (state->current_block == PARSER_BLOCK_PRIMARY) {
if (bundle6_parser_length_known(state) &&
state->primary_bytes_remaining == 0) {
state->basedata->status = PARSER_STATUS_ERROR;
state->error = PARSER_ERROR_BLOCK_LENGTH_EXHAUSTED;
return;
}
if (state->primary_bytes_remaining != 0)
state->primary_bytes_remaining--;
}
switch (state->current_stage) {
case PARSER_STAGE_VERSION:
state->bundle->protocol_version = byte;
state->next_stage = PARSER_STAGE_PROC_FLAGS;
break;
case PARSER_STAGE_PROC_FLAGS:
ASSERT(sizeof(state->bundle->proc_flags) == sizeof(uint32_t));
sdnv_read_u32(&state->sdnv_state,
(uint32_t *)&state->bundle->proc_flags, byte);
bundle6_parser_wait_for_sdnv(
state, PARSER_STAGE_BLOCK_LENGTH);
break;
case PARSER_STAGE_BLOCK_LENGTH:
sdnv_read_u16(&state->sdnv_state,
&state->bundle->primary_block_length, byte);
bundle6_parser_wait_for_sdnv(
state, PARSER_STAGE_DESTINATION_EID_SCHEME);
break;
case PARSER_STAGE_DESTINATION_EID_SCHEME:
sdnv_read_u64(&state->sdnv_state,
&state->destination_eidref.scheme_offset, byte);
bundle6_parser_wait_for_sdnv(
state, PARSER_STAGE_DESTINATION_EID_SSP);
break;
case PARSER_STAGE_DESTINATION_EID_SSP:
sdnv_read_u64(&state->sdnv_state,
&state->destination_eidref.ssp_offset, byte);
bundle6_parser_wait_for_sdnv(
state, PARSER_STAGE_SOURCE_EID_SCHEME);
break;
case PARSER_STAGE_SOURCE_EID_SCHEME:
sdnv_read_u64(&state->sdnv_state,
&state->source_eidref.scheme_offset, byte);
bundle6_parser_wait_for_sdnv(
state, PARSER_STAGE_SOURCE_EID_SSP);
break;
case PARSER_STAGE_SOURCE_EID_SSP:
sdnv_read_u64(&state->sdnv_state,
&state->source_eidref.ssp_offset, byte);
bundle6_parser_wait_for_sdnv(
state, PARSER_STAGE_REPORT_EID_SCHEME);
break;
case PARSER_STAGE_REPORT_EID_SCHEME:
sdnv_read_u64(&state->sdnv_state,
&state->report_to_eidref.scheme_offset, byte);
bundle6_parser_wait_for_sdnv(
state, PARSER_STAGE_REPORT_EID_SSP);
break;
case PARSER_STAGE_REPORT_EID_SSP:
sdnv_read_u64(&state->sdnv_state,
&state->report_to_eidref.ssp_offset, byte);
bundle6_parser_wait_for_sdnv(
state, PARSER_STAGE_CUSTODIAN_EID_SCHEME);
break;
case PARSER_STAGE_CUSTODIAN_EID_SCHEME:
sdnv_read_u64(&state->sdnv_state,
&state->custodian_eidref.scheme_offset, byte);
bundle6_parser_wait_for_sdnv(
state, PARSER_STAGE_CUSTODIAN_EID_SSP);
break;
case PARSER_STAGE_CUSTODIAN_EID_SSP:
sdnv_read_u64(&state->sdnv_state,
&state->custodian_eidref.ssp_offset, byte);
bundle6_parser_wait_for_sdnv(
state, PARSER_STAGE_TIMESTAMP);
break;
case PARSER_STAGE_TIMESTAMP:
sdnv_read_u64(
&state->sdnv_state,
&state->bundle->creation_timestamp_ms,
byte);
bundle6_parser_wait_for_sdnv(
state, PARSER_STAGE_SEQUENCE_NUM);
break;
case PARSER_STAGE_SEQUENCE_NUM:
sdnv_read_u64(
&state->sdnv_state,
&state->bundle->sequence_number,
byte);
bundle6_parser_wait_for_sdnv(
state, PARSER_STAGE_LIFETIME);
break;
case PARSER_STAGE_LIFETIME:
sdnv_read_u64(
&state->sdnv_state,
&state->bundle->lifetime_ms,
byte);
bundle6_parser_wait_for_sdnv(
state, PARSER_STAGE_DICT_LENGTH);
break;
case PARSER_STAGE_DICT_LENGTH:
sdnv_read_u16(&state->sdnv_state, &state->dict_length,
byte);
bundle6_parser_wait_for_sdnv(
state, PARSER_STAGE_DICTIONARY);
break;
case PARSER_STAGE_DICTIONARY:
--state->cur_bytes_remaining;
((uint8_t *)state->dict)[state->current_index++] = byte;
if (bundle6_parser_data_done(state)) {
((uint8_t *)state->dict)[state->current_index++] = 0;
// Allocate EID references
//
// source
state->bundle->source = bundle6_read_eid(
state->dict, state->dict_length,
state->source_eidref
);
// destination
state->bundle->destination = bundle6_read_eid(
state->dict, state->dict_length,
state->destination_eidref
);
// report-to
state->bundle->report_to = bundle6_read_eid(
state->dict, state->dict_length,
state->report_to_eidref
);
// custodian
state->bundle->current_custodian = bundle6_read_eid(
state->dict, state->dict_length,
state->custodian_eidref
);
if (state->bundle->source == NULL ||
state->bundle->destination == NULL ||
state->bundle->report_to == NULL ||
state->bundle->current_custodian == NULL) {
state->basedata->status = PARSER_STATUS_ERROR;
break;
}
if (bundle_is_fragmented(state->bundle))
state->next_stage =
PARSER_STAGE_FRAGMENT_OFFSET;
else
state->next_stage =
PARSER_STAGE_BLOCK_TYPE;
}
break;
case PARSER_STAGE_FRAGMENT_OFFSET:
sdnv_read_u32(
&state->sdnv_state,
&state->bundle->fragment_offset,
byte);
bundle6_parser_wait_for_sdnv(
state, PARSER_STAGE_ADU_LENGTH);
break;
case PARSER_STAGE_ADU_LENGTH:
sdnv_read_u32(
&state->sdnv_state,
&state->bundle->total_adu_length,
byte);
bundle6_parser_wait_for_sdnv(
state, PARSER_STAGE_BLOCK_TYPE);
break;
case PARSER_STAGE_BLOCK_TYPE:
state->next_stage = PARSER_STAGE_BLOCK_FLAGS;
bundle6_parser_begin_block(state, byte);
break;
case PARSER_STAGE_BLOCK_FLAGS:
sdnv_read_u8(
&state->sdnv_state,
(uint8_t *)&(*state->current_block_entry)->data->flags,
byte
);
if (bundle6_parser_wait_for_sdnv(state,
PARSER_STAGE_BLOCK_DATA_LENGTH)) {
enum bundle_block_flags flags =
(*state->current_block_entry)->data->flags;
state->last_block = HAS_FLAG(
flags,
BUNDLE_V6_BLOCK_FLAG_LAST_BLOCK
);
if (HAS_FLAG(flags, BUNDLE_V6_BLOCK_FLAG_HAS_EID_REF_FIELD)) {
state->next_stage =
PARSER_STAGE_BLOCK_EID_REF_CNT;
}
}
break;
case PARSER_STAGE_BLOCK_EID_REF_CNT:
sdnv_read_u32(&state->sdnv_state, &state->current_size, byte);
if (bundle6_parser_wait_for_sdnv(
state, PARSER_STAGE_BLOCK_EID_REF_SCH)) {
state->current_index = 0;
if (state->current_size == 0)
state->next_stage = PARSER_STAGE_BLOCK_DATA_LENGTH;
}
break;
case PARSER_STAGE_BLOCK_EID_REF_SCH:
sdnv_read_u64(&state->sdnv_state,
&state->cur_eidref.scheme_offset, byte);
bundle6_parser_wait_for_sdnv(
state, PARSER_STAGE_BLOCK_EID_REF_SSP);
break;
case PARSER_STAGE_BLOCK_EID_REF_SSP:
sdnv_read_u64(&state->sdnv_state,
&state->cur_eidref.ssp_offset, byte);
if (bundle6_parser_wait_for_sdnv(
state, PARSER_STAGE_BLOCK_EID_REF_SCH)) {
struct endpoint_list *entry = malloc(
sizeof(struct endpoint_list));
if (entry == NULL) {
state->basedata->status = PARSER_STATUS_ERROR;
break;
}
if (state->dict_length == 0) {
// CBHE
entry->eid = bundle6_create_eid_cbhe(
state->cur_eidref
);
} else {
// read from dict
entry->eid = bundle6_read_eid(
state->dict, state->dict_length,
state->cur_eidref);
}
entry->next = (*state->current_block_entry)
->data->eid_refs;
(*state->current_block_entry)
->data->eid_refs = entry;
state->current_index++;
if (entry->eid == NULL)
state->basedata->status = PARSER_STATUS_ERROR;
else if (state->current_index == state->current_size)
state->next_stage =
PARSER_STAGE_BLOCK_DATA_LENGTH;
}
break;
case PARSER_STAGE_BLOCK_DATA_LENGTH:
sdnv_read_u32(
&state->sdnv_state,
&(*state->current_block_entry)
->data->length,
byte);
bundle6_parser_wait_for_sdnv(
state, PARSER_STAGE_BLOCK_DATA);
break;
case PARSER_STAGE_BLOCK_DATA:
if (state->cur_bytes_remaining) {
state->basedata->status = PARSER_STATUS_ERROR;
} else {
/*
* We do not parse byte as it is, only passed for
* triggering this logic and always has the value zero.
* See also inputProcessor.c
*/
bundle6_parser_end_block(state);
if (bundle6_parser_is_last_block(state))
state->next_stage = PARSER_STAGE_DONE;
else
state->next_stage = PARSER_STAGE_BLOCK_TYPE;
}
break;
default:
state->basedata->status = PARSER_STATUS_ERROR;
break;
}
if (state->current_stage != state->next_stage &&
state->basedata->status != PARSER_STATUS_ERROR)
bundle6_parser_next(state);
}
size_t bundle6_parser_read(struct bundle6_parser *parser,
const uint8_t *buffer, size_t length)
{
/*
* Special case:
* Bulk read operation was performed.
* This function is called with an empty buffer.
*/
if (buffer == NULL) {
/* Feed zero byte */
bundle6_parser_read_byte(parser, '\0');
return 0;
}
size_t i = 0;
while (i < length && parser->basedata->status == PARSER_STATUS_GOOD) {
if (HAS_FLAG(parser->basedata->flags, PARSER_FLAG_BULK_READ)) {
/*
* Input buffer does not contain enough data,
* let the outer function handle the bulk read
* operation
*/
if (i + parser->basedata->next_bytes > length)
break;
/* Perform bulk read operation directly */
memcpy(parser->basedata->next_buffer,
buffer + i,
parser->basedata->next_bytes);
/* Disables bulk read mode again */
parser->basedata->flags &= ~PARSER_FLAG_BULK_READ;
/* Feed zero byte */
bundle6_parser_read_byte(parser, '\0');
i += parser->basedata->next_bytes;
} else {
bundle6_parser_read_byte(parser, buffer[i]);
i++;
}
}
return i;
}