mirror of
https://gitlab.com/d3tn/ud3tn.git
synced 2026-08-13 12:33:27 +02:00
Removes unused includes detected by the clangd LSP. Signed-off-by: Maximilian Nitsch <maximilian.nitsch@d3tn.com>
910 lines
24 KiB
C
910 lines
24 KiB
C
// SPDX-License-Identifier: BSD-3-Clause OR Apache-2.0
|
|
#include "bundle7/eid.h"
|
|
#include "bundle7/parser.h"
|
|
#include "bundle7/timestamp.h"
|
|
|
|
#include "ud3tn/common.h"
|
|
|
|
#include "compilersupport_p.h" // Private TinyCBOR header, used for endianness
|
|
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
|
|
/**
|
|
* Shortcut access to current bundle block
|
|
*/
|
|
#define BLOCK(state) ((*((state)->current_block_entry))->data)
|
|
|
|
|
|
/**
|
|
* Shortcut to transition into error state
|
|
*/
|
|
#define FAIL(state) ((state)->basedata->status = PARSER_STATUS_ERROR)
|
|
|
|
|
|
// -----------------------------
|
|
// Cyclic Redundancy Check (CRC)
|
|
// -----------------------------
|
|
|
|
static void crc_verify(struct bundle7_parser *state, uint32_t checksum,
|
|
uint32_t expected)
|
|
{
|
|
if (checksum != expected)
|
|
state->basedata->flags |= PARSER_FLAG_CRC_INVALID;
|
|
}
|
|
|
|
|
|
// --------------------
|
|
// Bundle start and end
|
|
// --------------------
|
|
|
|
static CborError bundle_start(struct bundle7_parser *, CborValue *);
|
|
static CborError bundle_end(struct bundle7_parser *, CborValue *);
|
|
|
|
|
|
// -------------
|
|
// Primary block
|
|
// -------------
|
|
|
|
static CborError primary_block_start(struct bundle7_parser *, CborValue *);
|
|
static CborError protocol_version(struct bundle7_parser *, CborValue *);
|
|
static CborError primary_proc_flags(struct bundle7_parser *, CborValue *);
|
|
static CborError crc_type_field(struct bundle7_parser *, CborValue *);
|
|
static CborError destination_eid(struct bundle7_parser *, CborValue *);
|
|
static CborError source_eid(struct bundle7_parser *, CborValue *);
|
|
static CborError report_to_eid(struct bundle7_parser *, CborValue *);
|
|
static CborError creation_timestamp(struct bundle7_parser *, CborValue *);
|
|
static CborError lifetime(struct bundle7_parser *, CborValue *);
|
|
static CborError fragment_offset(struct bundle7_parser *, CborValue *);
|
|
static CborError total_adu_length(struct bundle7_parser *, CborValue *);
|
|
static CborError primary_block_crc(struct bundle7_parser *, CborValue *);
|
|
|
|
|
|
// ----------------
|
|
// Extension Blocks
|
|
// ----------------
|
|
|
|
static CborError block_start(struct bundle7_parser *, CborValue *);
|
|
static CborError block_type(struct bundle7_parser *, CborValue *);
|
|
static CborError block_number(struct bundle7_parser *, CborValue *);
|
|
static CborError block_proc_flags(struct bundle7_parser *, CborValue *);
|
|
static CborError block_crc_type(struct bundle7_parser *, CborValue *);
|
|
static CborError block_crc(struct bundle7_parser *, CborValue *);
|
|
static CborError block_data(struct bundle7_parser *, CborValue *);
|
|
static CborError block_end(struct bundle7_parser *, CborValue *);
|
|
|
|
|
|
// --------------------
|
|
// Bundle start and end
|
|
// --------------------
|
|
|
|
CborError bundle_start(struct bundle7_parser *state, CborValue *it)
|
|
{
|
|
if (!cbor_value_is_array(it) || cbor_value_is_length_known(it))
|
|
return CborErrorIllegalType;
|
|
|
|
state->next = primary_block_start;
|
|
return cbor_value_enter_container(it, it);
|
|
}
|
|
|
|
|
|
CborError bundle_end(struct bundle7_parser *state, CborValue *it)
|
|
{
|
|
struct bundle *bundle;
|
|
|
|
if (*it->source.ptr != 0xff)
|
|
return CborErrorIllegalType;
|
|
it->source.ptr++;
|
|
|
|
// Transition into "Done" state
|
|
// NOTE that it is expected that a transition to "DONE" also occurs if
|
|
// the CRC is invalid. In this case, however, the "send" callback
|
|
// is not called. The caller may check for this case by examining the
|
|
// parser flags for `PARSER_FLAG_CRC_INVALID` when the `status` is
|
|
// `PARSER_STATUS_DONE`.
|
|
state->basedata->status = PARSER_STATUS_DONE;
|
|
|
|
// Clear bundle reference
|
|
bundle = state->bundle;
|
|
state->bundle = NULL;
|
|
|
|
// Call "send" callback if set and all CRCs passed, otherwise discard
|
|
// parsed bundle silently
|
|
if (state->send_callback == NULL ||
|
|
state->basedata->flags & PARSER_FLAG_CRC_INVALID)
|
|
bundle_free(bundle);
|
|
else
|
|
state->send_callback(bundle, state->send_param);
|
|
|
|
return CborNoError;
|
|
}
|
|
|
|
|
|
// -------------
|
|
// Primary Block
|
|
// -------------
|
|
|
|
CborError primary_block_start(struct bundle7_parser *state, CborValue *it)
|
|
{
|
|
if (!cbor_value_is_array(it) || !cbor_value_is_length_known(it))
|
|
return CborErrorIllegalType;
|
|
|
|
size_t primary_array_len = 0;
|
|
|
|
if (cbor_value_get_array_length(it, &primary_array_len) != CborNoError)
|
|
return CborErrorIllegalType;
|
|
|
|
// RFC 9171, 4.3.1 - array length check
|
|
if (primary_array_len < 8 || primary_array_len > 11)
|
|
return CborErrorIllegalType;
|
|
|
|
state->primary_block_array_length = primary_array_len;
|
|
|
|
// Primary block CRC
|
|
state->flags |= BUNDLE_V7_PARSER_CRC_FEED;
|
|
|
|
crc_init(&state->crc16, CRC16_X25);
|
|
crc_init(&state->crc32, CRC32);
|
|
|
|
state->next = protocol_version;
|
|
return cbor_value_enter_container(it, it);
|
|
}
|
|
|
|
|
|
CborError protocol_version(struct bundle7_parser *state, CborValue *it)
|
|
{
|
|
uint64_t version;
|
|
|
|
if (!cbor_value_is_unsigned_integer(it))
|
|
return CborErrorIllegalType;
|
|
|
|
cbor_value_get_uint64(it, &version);
|
|
|
|
// Validate that it is indeed BPv7. This parser can only handle RFC9171-conformant bundles.
|
|
if (version != 7)
|
|
return CborErrorImproperValue;
|
|
|
|
state->bundle->protocol_version = 7;
|
|
|
|
state->next = primary_proc_flags;
|
|
return cbor_value_advance_fixed(it);
|
|
}
|
|
|
|
|
|
CborError primary_proc_flags(struct bundle7_parser *state, CborValue *it)
|
|
{
|
|
uint64_t flags;
|
|
|
|
if (!cbor_value_is_unsigned_integer(it))
|
|
return CborErrorIllegalType;
|
|
|
|
cbor_value_get_uint64(it, &flags);
|
|
state->bundle->proc_flags = flags;
|
|
state->next = crc_type_field;
|
|
return cbor_value_advance_fixed(it);
|
|
}
|
|
|
|
|
|
CborError crc_type_field(struct bundle7_parser *state, CborValue *it)
|
|
{
|
|
uint64_t type;
|
|
|
|
if (!cbor_value_is_unsigned_integer(it))
|
|
return CborErrorIllegalType;
|
|
|
|
cbor_value_get_uint64(it, &type);
|
|
|
|
// Validate CRC type
|
|
if (type > BUNDLE_CRC_TYPE_32)
|
|
return CborErrorIllegalType;
|
|
|
|
state->bundle->crc_type = type;
|
|
|
|
const bool has_crc = state->bundle->crc_type != BUNDLE_CRC_TYPE_NONE;
|
|
|
|
// Deactivate CRC feeding if CRC is not enabled
|
|
if (!has_crc)
|
|
state->flags &= ~BUNDLE_V7_PARSER_CRC_FEED;
|
|
|
|
// RFC 9171, 4.3.1 - array length check
|
|
const bool is_fragment = HAS_FLAG(state->bundle->proc_flags, BUNDLE_FLAG_IS_FRAGMENT);
|
|
const bool is_valid_primary_block_array_length = (
|
|
(!is_fragment && !has_crc && state->primary_block_array_length == 8) ||
|
|
(!is_fragment && has_crc && state->primary_block_array_length == 9) ||
|
|
(is_fragment && !has_crc && state->primary_block_array_length == 10) ||
|
|
(is_fragment && has_crc && state->primary_block_array_length == 11)
|
|
);
|
|
|
|
if (!is_valid_primary_block_array_length)
|
|
return CborErrorIllegalType;
|
|
|
|
state->next = destination_eid;
|
|
return cbor_value_advance_fixed(it);
|
|
}
|
|
|
|
|
|
CborError parse_eid(struct bundle7_parser *state, CborValue *it, struct eid *eid,
|
|
CborError (*next)(struct bundle7_parser *, CborValue *))
|
|
{
|
|
CborError err = bundle7_eid_parse_cbor(it, eid);
|
|
|
|
if (err)
|
|
return err;
|
|
|
|
state->next = next;
|
|
return CborNoError;
|
|
}
|
|
|
|
|
|
CborError destination_eid(struct bundle7_parser *state, CborValue *it)
|
|
{
|
|
return parse_eid(state, it, &state->bundle->destination, source_eid);
|
|
}
|
|
|
|
|
|
CborError source_eid(struct bundle7_parser *state, CborValue *it)
|
|
{
|
|
return parse_eid(state, it, &state->bundle->source, report_to_eid);
|
|
}
|
|
|
|
|
|
CborError report_to_eid(struct bundle7_parser *state, CborValue *it)
|
|
{
|
|
return parse_eid(state, it, &state->bundle->report_to,
|
|
creation_timestamp);
|
|
}
|
|
|
|
|
|
CborError creation_timestamp(struct bundle7_parser *state, CborValue *it)
|
|
{
|
|
CborError err;
|
|
|
|
err = bundle7_timestamp_parse(it,
|
|
&state->bundle->creation_timestamp_ms,
|
|
&state->bundle->sequence_number);
|
|
if (err)
|
|
return err;
|
|
|
|
state->next = lifetime;
|
|
return CborNoError;
|
|
}
|
|
|
|
|
|
CborError lifetime(struct bundle7_parser *state, CborValue *it)
|
|
{
|
|
uint64_t value;
|
|
|
|
if (!cbor_value_is_unsigned_integer(it))
|
|
return CborErrorIllegalType;
|
|
|
|
cbor_value_get_uint64(it, &value);
|
|
state->bundle->lifetime_ms = value;
|
|
|
|
const uint8_t *last_ptr = it->source.ptr;
|
|
CborError err = cbor_value_advance_fixed(it);
|
|
|
|
if (bundle_is_fragmented(state->bundle)) {
|
|
state->next = fragment_offset;
|
|
} else if (state->bundle->crc_type != BUNDLE_CRC_TYPE_NONE) {
|
|
state->next = primary_block_crc;
|
|
} else {
|
|
state->next = block_start;
|
|
|
|
// -1 Byte infinite array header
|
|
state->bundle->primary_block_length = state->bundle_size - 1 +
|
|
(it->source.ptr - last_ptr);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
CborError fragment_offset(struct bundle7_parser *state, CborValue *it)
|
|
{
|
|
uint64_t offset;
|
|
|
|
if (!cbor_value_is_unsigned_integer(it))
|
|
return CborErrorIllegalType;
|
|
|
|
cbor_value_get_uint64(it, &offset);
|
|
state->bundle->fragment_offset = offset;
|
|
|
|
state->next = total_adu_length;
|
|
return cbor_value_advance_fixed(it);
|
|
}
|
|
|
|
|
|
CborError total_adu_length(struct bundle7_parser *state, CborValue *it)
|
|
{
|
|
uint64_t length;
|
|
|
|
if (!cbor_value_is_unsigned_integer(it))
|
|
return CborErrorIllegalType;
|
|
|
|
cbor_value_get_uint64(it, &length);
|
|
state->bundle->total_adu_length = length;
|
|
|
|
const uint8_t *last_ptr = it->source.ptr;
|
|
CborError err = cbor_value_advance_fixed(it);
|
|
|
|
if (state->bundle->crc_type != BUNDLE_CRC_TYPE_NONE) {
|
|
state->next = primary_block_crc;
|
|
} else {
|
|
state->next = block_start;
|
|
|
|
// -1 Byte infinite array header
|
|
state->bundle->primary_block_length = state->bundle_size - 1 +
|
|
(it->source.ptr - last_ptr);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
CborError primary_block_crc(struct bundle7_parser *state, CborValue *it)
|
|
{
|
|
union crc crc;
|
|
size_t len = sizeof(crc);
|
|
CborError err;
|
|
|
|
if (!cbor_value_is_byte_string(it))
|
|
return CborErrorIllegalType;
|
|
|
|
err = cbor_value_copy_byte_string(it, crc.bytes, &len, it);
|
|
if (err)
|
|
return err;
|
|
|
|
// Deactivate CRC feeding for the CRC field itself
|
|
state->flags &= ~BUNDLE_V7_PARSER_CRC_FEED;
|
|
|
|
if (state->bundle->crc_type == BUNDLE_CRC_TYPE_16) {
|
|
// Ensure correct CRC 16 length
|
|
if (len != 2)
|
|
return CborErrorIllegalType;
|
|
|
|
// CRC field is populated with zero
|
|
state->crc16.feed(&state->crc16, 0x42); // CBOR 2-b array header
|
|
state->crc16.feed(&state->crc16, 0x00);
|
|
state->crc16.feed(&state->crc16, 0x00);
|
|
state->crc16.feed_eof(&state->crc16);
|
|
|
|
// Swap from network byte order to native order and clear all
|
|
// higher bits
|
|
state->bundle->crc.checksum = cbor_ntohs(crc.checksum & 0xffff);
|
|
|
|
crc_verify(state,
|
|
state->crc16.checksum,
|
|
state->bundle->crc.checksum);
|
|
|
|
// -1 Byte infinite array header + 3 Bytes CRC field
|
|
state->bundle->primary_block_length = state->bundle_size - 1 + 3;
|
|
} else {
|
|
// Ensure correct CRC 32 length
|
|
if (len != 4)
|
|
return CborErrorIllegalType;
|
|
|
|
// CRC field is populated with zero
|
|
state->crc32.feed(&state->crc32, 0x44); // CBOR 4-b array header
|
|
state->crc32.feed(&state->crc32, 0x00);
|
|
state->crc32.feed(&state->crc32, 0x00);
|
|
state->crc32.feed(&state->crc32, 0x00);
|
|
state->crc32.feed(&state->crc32, 0x00);
|
|
state->crc32.feed_eof(&state->crc32);
|
|
|
|
// Swap from network byte order to native order
|
|
state->bundle->crc.checksum = cbor_ntohl(crc.checksum);
|
|
|
|
crc_verify(state,
|
|
state->crc32.checksum,
|
|
state->bundle->crc.checksum);
|
|
|
|
// -1 Byte infinite array header + 5 Bytes CRC field
|
|
state->bundle->primary_block_length = state->bundle_size - 1 + 5;
|
|
}
|
|
|
|
state->next = block_start;
|
|
|
|
return CborNoError;
|
|
}
|
|
|
|
|
|
// ----------------
|
|
// Extension Blocks
|
|
// ----------------
|
|
|
|
CborError block_start(struct bundle7_parser *state, CborValue *it)
|
|
{
|
|
if (!cbor_value_is_array(it) || !cbor_value_is_length_known(it))
|
|
return CborErrorIllegalType;
|
|
|
|
size_t block_array_len = 0;
|
|
|
|
if (cbor_value_get_array_length(it, &block_array_len) != CborNoError)
|
|
return CborErrorIllegalType;
|
|
|
|
// RFC 9171, 4.3.2 - array length check
|
|
if (block_array_len < 5 || block_array_len > 6)
|
|
return CborErrorIllegalType;
|
|
|
|
state->current_block_array_length = block_array_len;
|
|
|
|
// Reset CRC streams
|
|
crc_init(&state->crc16, CRC16_X25);
|
|
crc_init(&state->crc32, CRC32);
|
|
|
|
// Enable CRC feeding again
|
|
state->flags |= BUNDLE_V7_PARSER_CRC_FEED;
|
|
|
|
state->next = block_type;
|
|
return cbor_value_enter_container(it, it);
|
|
}
|
|
|
|
|
|
CborError block_type(struct bundle7_parser *state, CborValue *it)
|
|
{
|
|
uint64_t type;
|
|
|
|
if (!cbor_value_is_unsigned_integer(it))
|
|
return CborErrorIllegalType;
|
|
|
|
cbor_value_get_uint64(it, &type);
|
|
|
|
// Invalid block type
|
|
if (type > (uint64_t)BUNDLE_BLOCK_TYPE_MAX)
|
|
return CborErrorImproperValue;
|
|
|
|
if (*state->current_block_entry == NULL) {
|
|
// Create bundle block
|
|
struct bundle_block *block = bundle_block_create(type);
|
|
|
|
if (block == NULL)
|
|
return CborErrorOutOfMemory;
|
|
|
|
// Create bundle block list entry
|
|
*state->current_block_entry = bundle_block_entry_create(block);
|
|
|
|
if (*state->current_block_entry == NULL) {
|
|
bundle_block_free(block);
|
|
return CborErrorOutOfMemory;
|
|
}
|
|
} else {
|
|
// Was already created but the last operation failed with a
|
|
// too-short chunk (CborErrorUnexpectedEOF).
|
|
ASSERT(
|
|
(*state->current_block_entry)->data->type ==
|
|
(enum bundle_block_type)type
|
|
);
|
|
}
|
|
|
|
state->next = block_number;
|
|
return cbor_value_advance_fixed(it);
|
|
}
|
|
|
|
|
|
CborError block_number(struct bundle7_parser *state, CborValue *it)
|
|
{
|
|
uint64_t number;
|
|
|
|
if (!cbor_value_is_unsigned_integer(it))
|
|
return CborErrorIllegalType;
|
|
|
|
cbor_value_get_uint64(it, &number);
|
|
|
|
// We do not support larger block numbers atm.!
|
|
if (number > UINT8_MAX)
|
|
return CborErrorImproperValue;
|
|
|
|
BLOCK(state)->number = number;
|
|
|
|
state->next = block_proc_flags;
|
|
return cbor_value_advance_fixed(it);
|
|
}
|
|
|
|
|
|
CborError block_proc_flags(struct bundle7_parser *state, CborValue *it)
|
|
{
|
|
uint64_t flags;
|
|
|
|
if (!cbor_value_is_unsigned_integer(it))
|
|
return CborErrorIllegalType;
|
|
|
|
cbor_value_get_uint64(it, &flags);
|
|
BLOCK(state)->flags = flags;
|
|
|
|
state->next = block_crc_type;
|
|
return cbor_value_advance_fixed(it);
|
|
}
|
|
|
|
|
|
CborError block_crc_type(struct bundle7_parser *state, CborValue *it)
|
|
{
|
|
uint64_t crc_type;
|
|
|
|
if (!cbor_value_is_unsigned_integer(it))
|
|
return CborErrorIllegalType;
|
|
|
|
cbor_value_get_uint64(it, &crc_type);
|
|
BLOCK(state)->crc_type = crc_type;
|
|
|
|
state->next = block_data;
|
|
|
|
const bool has_crc = crc_type != BUNDLE_CRC_TYPE_NONE;
|
|
|
|
// Deactivate CRC feeding
|
|
if (!has_crc)
|
|
state->flags &= ~BUNDLE_V7_PARSER_CRC_FEED;
|
|
|
|
// RFC 9171, 4.3.2 - array length check
|
|
const bool is_valid_current_block_array_length = (
|
|
(!has_crc && state->current_block_array_length == 5) ||
|
|
(has_crc && state->current_block_array_length == 6)
|
|
);
|
|
|
|
if (!is_valid_current_block_array_length)
|
|
return CborErrorIllegalType;
|
|
|
|
return cbor_value_advance_fixed(it);
|
|
}
|
|
|
|
|
|
CborError block_crc(struct bundle7_parser *state, CborValue *it)
|
|
{
|
|
union crc crc;
|
|
size_t len = sizeof(crc);
|
|
CborError err;
|
|
|
|
if (!cbor_value_is_byte_string(it))
|
|
return CborErrorIllegalType;
|
|
|
|
// Deactivate CRC feeding for the CRC field itself
|
|
state->flags &= ~BUNDLE_V7_PARSER_CRC_FEED;
|
|
|
|
err = cbor_value_copy_byte_string(it, crc.bytes, &len, it);
|
|
if (err)
|
|
return err;
|
|
|
|
// Feed CRC with raw CBOR block data
|
|
if (BLOCK(state)->crc_type == BUNDLE_CRC_TYPE_16) {
|
|
// Ensure correct CRC 16 checksum length
|
|
if (len != 2)
|
|
return CborErrorIllegalType;
|
|
|
|
crc_feed_bytes(&state->crc16,
|
|
BLOCK(state)->data,
|
|
BLOCK(state)->length);
|
|
|
|
// CRC field is populated with zero
|
|
state->crc16.feed(&state->crc16, 0x42); // CBOR byte string(2)
|
|
state->crc16.feed(&state->crc16, 0x00);
|
|
state->crc16.feed(&state->crc16, 0x00);
|
|
state->crc16.feed_eof(&state->crc16);
|
|
|
|
// Swap from network byte order to native order and clear all
|
|
// higher bits
|
|
BLOCK(state)->crc.checksum = cbor_ntohs(crc.checksum & 0xffff);
|
|
|
|
crc_verify(state,
|
|
state->crc16.checksum,
|
|
BLOCK(state)->crc.checksum);
|
|
} else {
|
|
// Ensure correct CRC 32 checksum length
|
|
if (len != 4)
|
|
return CborErrorIllegalType;
|
|
|
|
crc_feed_bytes(&state->crc32,
|
|
BLOCK(state)->data,
|
|
BLOCK(state)->length);
|
|
|
|
// CRC field is populated with zero
|
|
state->crc32.feed(&state->crc32, 0x44); // CBOR byte string(4)
|
|
state->crc32.feed(&state->crc32, 0x00);
|
|
state->crc32.feed(&state->crc32, 0x00);
|
|
state->crc32.feed(&state->crc32, 0x00);
|
|
state->crc32.feed(&state->crc32, 0x00);
|
|
state->crc32.feed_eof(&state->crc32);
|
|
|
|
// Swap from network byte order to native order
|
|
BLOCK(state)->crc.checksum = cbor_ntohl(crc.checksum);
|
|
|
|
crc_verify(state,
|
|
state->crc32.checksum,
|
|
BLOCK(state)->crc.checksum);
|
|
}
|
|
block_end(state, it);
|
|
|
|
return CborNoError;
|
|
}
|
|
|
|
|
|
CborError block_data(struct bundle7_parser *state, CborValue *it)
|
|
{
|
|
size_t length;
|
|
CborError err;
|
|
|
|
if (!cbor_value_is_byte_string(it))
|
|
return CborErrorIllegalType;
|
|
|
|
err = cbor_value_get_string_length(it, &length);
|
|
// CBOR length may be unknown (which is not allowed by the BPbis spec)
|
|
if (err)
|
|
return err;
|
|
|
|
// Activate CRC feeding again
|
|
state->flags |= BUNDLE_V7_PARSER_CRC_FEED;
|
|
|
|
BLOCK(state)->length = length;
|
|
|
|
// Block-specific data
|
|
// -------------------
|
|
//
|
|
BLOCK(state)->data = malloc(length);
|
|
if (BLOCK(state)->data == NULL)
|
|
return CborErrorOutOfMemory;
|
|
|
|
// Enable "bulk read" mode
|
|
state->basedata->next_buffer = BLOCK(state)->data;
|
|
state->basedata->next_bytes = length;
|
|
state->basedata->flags |= PARSER_FLAG_BULK_READ;
|
|
state->bundle_size += length;
|
|
|
|
if (BLOCK(state)->crc_type != BUNDLE_CRC_TYPE_NONE)
|
|
state->next = block_crc;
|
|
else
|
|
state->next = block_end;
|
|
|
|
// The CBOR byte string length is represented in the same manner as a
|
|
// CBOR uint. We thus change the type to uint to allow cleanly
|
|
// advancing the iterator to just the start of the block payload.
|
|
it->type = CborIntegerType;
|
|
// NOTE: This may return an error depending on the following data
|
|
// which it tries to preparse. It is already ensured that the header
|
|
// and length can safely be read and advanced over by the result of
|
|
// cbor_value_get_string_length. As we have requested a BULK_READ,
|
|
// control will be handed over to the bulk_read handler and afterwards
|
|
// we re-initialize the parser.
|
|
cbor_value_advance_fixed(it);
|
|
return CborNoError;
|
|
}
|
|
|
|
|
|
/**
|
|
* Bulk read operation handler
|
|
*
|
|
* This function gets called with an null-pointer instead of an iterator after
|
|
* the "bulk read" operation was performed and the bulk buffer was filled.
|
|
*
|
|
* @return This function never fails and always returns CborNoError
|
|
*/
|
|
CborError block_end(struct bundle7_parser *state, CborValue *it)
|
|
{
|
|
(void)it;
|
|
|
|
// Update parsing callback directly because there is no transition for
|
|
// this callback as it gets called after the bulk read operation with
|
|
// a null pointer instead an CBOR iterator.
|
|
//
|
|
// Set the "next" callback to the same function to prevent undesirable
|
|
// transitions to an old callback of a previous stage
|
|
if (BLOCK(state)->type == BUNDLE_BLOCK_TYPE_PAYLOAD) {
|
|
state->bundle->payload_block = BLOCK(state);
|
|
state->parse = bundle_end;
|
|
state->next = bundle_end;
|
|
} else {
|
|
state->parse = block_start;
|
|
state->next = block_start;
|
|
}
|
|
|
|
// Iterate to next block entry element
|
|
state->current_block_entry = &(*state->current_block_entry)->next;
|
|
|
|
return CborNoError;
|
|
}
|
|
|
|
|
|
// -----------
|
|
// Parser Core
|
|
// -----------
|
|
|
|
struct parser *bundle7_parser_init(struct bundle7_parser *state,
|
|
void (*send_callback)(struct bundle *, void *), void *param)
|
|
{
|
|
state->basedata = malloc(sizeof(struct parser));
|
|
if (state->basedata == NULL)
|
|
return NULL;
|
|
|
|
state->bundle_quota = BUNDLE7_DEFAULT_BUNDLE_QUOTA;
|
|
state->send_callback = send_callback;
|
|
state->send_param = param;
|
|
state->bundle = NULL;
|
|
state->next = NULL; // force reset to do its job
|
|
|
|
// Set to error that the reset handler does not abort
|
|
state->basedata->status = PARSER_STATUS_ERROR;
|
|
|
|
if (bundle7_parser_reset(state) != UD3TN_OK) {
|
|
free(state->basedata);
|
|
return NULL;
|
|
}
|
|
|
|
return state->basedata;
|
|
}
|
|
|
|
|
|
enum ud3tn_result bundle7_parser_reset(struct bundle7_parser *state)
|
|
{
|
|
if (state->basedata->status == PARSER_STATUS_GOOD &&
|
|
state->next == bundle_start)
|
|
return UD3TN_OK;
|
|
|
|
state->basedata->status = PARSER_STATUS_GOOD;
|
|
state->basedata->flags = PARSER_FLAG_NONE;
|
|
|
|
state->parse = bundle_start;
|
|
state->flags = 0;
|
|
state->bundle_size = 0;
|
|
|
|
if (state->bundle != NULL)
|
|
bundle_reset(state->bundle);
|
|
else
|
|
state->bundle = bundle_init();
|
|
|
|
if (state->bundle == NULL)
|
|
return UD3TN_FAIL;
|
|
|
|
state->current_block_entry = &state->bundle->blocks;
|
|
|
|
state->primary_block_array_length = 0;
|
|
state->current_block_array_length = 0;
|
|
|
|
return UD3TN_OK;
|
|
}
|
|
|
|
enum ud3tn_result bundle7_parser_deinit(struct bundle7_parser *state)
|
|
{
|
|
free(state->basedata);
|
|
if (state->bundle != NULL)
|
|
bundle_free(state->bundle);
|
|
|
|
return UD3TN_OK;
|
|
}
|
|
|
|
|
|
size_t bundle7_parser_read(struct bundle7_parser *state,
|
|
const uint8_t *buffer, size_t length)
|
|
{
|
|
CborParser parser;
|
|
CborValue it;
|
|
CborError err;
|
|
size_t parsed = 0;
|
|
bool initialize_parser = true;
|
|
|
|
// Special case:
|
|
// Bulk read operation was performed and this function gets called
|
|
// with an empty buffer to continue processing
|
|
if (buffer == NULL) {
|
|
// If no CRC is to be read, block_end needs to be called now.
|
|
// "block_end" never fails - therefore no error handling
|
|
if (state->parse == block_end) {
|
|
state->parse(state, NULL);
|
|
state->parse = state->next;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
while (parsed < length && state->basedata->status == PARSER_STATUS_GOOD) {
|
|
// For arbitrarily-long data such as block payload data, a
|
|
// "BULK_READ" operation is requested, normally to be handled
|
|
// by the RX task. Though, sometimes we can handle this on our
|
|
// own (if we have enough data in the current buffer). Thus, we
|
|
// first check whether, given the bytes in the buffer, we can
|
|
// perform the bulk read operation on our own.
|
|
// NOTE: At this point the CBOR parser may be in an invalid
|
|
// state because we have to interrupt it before it starts
|
|
// trying to read the bytes from the array by itself. This is
|
|
// not possible as the array might be too short and we want to
|
|
// be able to "stream" the data.
|
|
if (state->basedata->flags & PARSER_FLAG_BULK_READ) {
|
|
if (parsed + state->basedata->next_bytes > length) {
|
|
// Bulk read operation was requested but cannot
|
|
// be performed by us. -> RX task will do it.
|
|
break;
|
|
}
|
|
|
|
memcpy(
|
|
state->basedata->next_buffer,
|
|
buffer + parsed,
|
|
state->basedata->next_bytes
|
|
);
|
|
|
|
parsed += state->basedata->next_bytes;
|
|
|
|
// Disables bulk read mode again
|
|
state->basedata->flags &= ~PARSER_FLAG_BULK_READ;
|
|
|
|
// Process copied data and proceed to next block if
|
|
// there is not CRC to be read.
|
|
// This call to "block_end" is always successful.
|
|
if (state->parse == block_end) {
|
|
state->parse(state, NULL);
|
|
state->parse = state->next;
|
|
}
|
|
|
|
// Re-initialize after the "bulk read".
|
|
initialize_parser = true;
|
|
|
|
// Force checking the loop condition again.
|
|
continue;
|
|
}
|
|
|
|
if (initialize_parser) {
|
|
err = cbor_parser_init(
|
|
buffer + parsed,
|
|
length - parsed,
|
|
0, &parser, &it
|
|
);
|
|
|
|
// CBOR decoding error when preparsing first byte
|
|
if (err) {
|
|
// Insufficient data
|
|
if (err == CborErrorUnexpectedEOF)
|
|
break;
|
|
|
|
// We allow "unexpected" breaks
|
|
//
|
|
// The bundle is an indefinite array and its end
|
|
// is marked by the "break" symbol.
|
|
// Therefore, the buffer could contain a single
|
|
// "break". This happens because we cut the
|
|
// indefinite array by the chunked reading
|
|
// operation.
|
|
if (err != CborErrorUnexpectedBreak) {
|
|
FAIL(state);
|
|
break;
|
|
}
|
|
}
|
|
|
|
initialize_parser = false;
|
|
}
|
|
|
|
// Parsing step
|
|
err = state->parse(state, &it);
|
|
|
|
// Parsing error
|
|
if (err) {
|
|
if (err != CborErrorUnexpectedEOF)
|
|
FAIL(state);
|
|
break;
|
|
}
|
|
|
|
// Transition to next stage
|
|
size_t new_parsed = (cbor_value_get_next_byte(&it) - buffer);
|
|
|
|
state->bundle_size += new_parsed - parsed;
|
|
|
|
// Bundle is larger than allowed
|
|
if (state->bundle_size > state->bundle_quota) {
|
|
FAIL(state);
|
|
break;
|
|
}
|
|
|
|
if (state->flags & BUNDLE_V7_PARSER_CRC_FEED) {
|
|
crc_feed_bytes(&state->crc16,
|
|
buffer + parsed, new_parsed - parsed);
|
|
crc_feed_bytes(&state->crc32,
|
|
buffer + parsed, new_parsed - parsed);
|
|
}
|
|
|
|
state->parse = state->next;
|
|
parsed = new_parsed;
|
|
|
|
// If we have reached the end of the current item/container,
|
|
// we re-initialize the parser as we might want to read more
|
|
// than that. The parser is not fully aware of the bundle
|
|
// CBOR structure because we interrupt the process due to
|
|
// the chunked and bulk read mechanics.
|
|
if (cbor_value_at_end(&it))
|
|
initialize_parser = true;
|
|
}
|
|
|
|
return parsed;
|
|
}
|