ud3tn/components/spp/spp_parser.c
Felix Walter 915051b278 CLA: TCPSPP: Drop SPPs with unknown APID before parsing timecode
We do not need to parse the timecode to drop SPP frames that do not
belong to us. This makes the SPP parser return on every change of its
internal state (as it is called again by the CLA methods, this is a
totally valid mode of operation) and check the APID early in the CLA
methods.

By that we also support receiving SPPs that use different formats to
other APIDs (e.g., without any timecode in case we are expecting a
timecode).

Signed-off-by: Felix Walter <felix.walter@d3tn.com>
2022-11-09 09:30:45 +01:00

204 lines
4.7 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// SPDX-License-Identifier: BSD-3-Clause OR Apache-2.0
#include "spp/spp_parser.h"
#include "spp/spp_internal.h"
#include "ud3tn/parser.h"
#include <assert.h>
struct parser *spp_parser_init(struct spp_parser *parser,
const struct spp_context_t *ctx)
{
parser->ctx = ctx;
spp_parser_reset(parser);
return &parser->base;
}
bool spp_parse_byte(struct spp_parser *parser,
const uint8_t byte)
{
switch (parser->state) {
case SPP_PARSER_STATE_PH_P1_MSB:
{
if (!spp_check_first_byte(byte)) {
spp_parser_reset(parser);
parser->base.status = PARSER_STATUS_ERROR;
// ?!
return true;
}
parser->bufw = byte << 8;
parser->state = SPP_PARSER_STATE_PH_P1_LSB;
return true;
}
case SPP_PARSER_STATE_PH_P1_LSB:
{
parser->bufw |= byte;
spp_parse_ph_p1(parser->bufw, &parser->header);
parser->state = SPP_PARSER_STATE_PH_P2_MSB;
return true;
}
case SPP_PARSER_STATE_PH_P2_MSB:
{
parser->bufw = byte << 8;
parser->state = SPP_PARSER_STATE_PH_P2_LSB;
return true;
}
case SPP_PARSER_STATE_PH_P2_LSB:
{
parser->bufw |= byte;
spp_parse_ph_p2(parser->bufw, &parser->header);
parser->state = SPP_PARSER_STATE_PH_LEN_MSB;
return true;
}
case SPP_PARSER_STATE_PH_LEN_MSB:
{
parser->bufw = byte << 8;
parser->state = SPP_PARSER_STATE_PH_LEN_LSB;
return true;
}
case SPP_PARSER_STATE_PH_LEN_LSB:
{
parser->bufw |= byte;
spp_parse_ph_len(parser->bufw, &parser->header);
parser->data_length = parser->header.data_length;
parser->data_length -= parser->ctx->ancillary_data_len;
if (!parser->header.has_secondary_header) {
// TODO: init subparser if implemented here
parser->state = SPP_PARSER_STATE_DATA_SUBPARSER;
return false;
}
if (parser->ctx->timecode != NULL) {
parser->state = SPP_PARSER_STATE_SH_TIMECODE_SUBPARSER;
spp_tc_parser_init(parser->ctx->timecode,
&parser->tc_parser);
break;
}
if (parser->ctx->ancillary_data_len > 0) {
// TODO: init subparser if implemented here
parser->state = SPP_PARSER_STATE_SH_ANCILLARY_SUBPARSER;
break;
}
parser->state = SPP_PARSER_STATE_DATA_SUBPARSER;
return false;
}
case SPP_PARSER_STATE_SH_TIMECODE_SUBPARSER:
{
parser->data_length -= 1;
switch (spp_tc_parser_feed(&parser->tc_parser, byte)) {
case SPP_TC_PARSER_GOOD:
{
return true;
}
case SPP_TC_PARSER_ERROR:
{
parser->base.status = PARSER_STATUS_ERROR;
return false;
}
case SPP_TC_PARSER_DONE:
{
break;
}
}
parser->dtn_timestamp = spp_tc_get_dtn_timestamp(
&parser->tc_parser);
if (parser->ctx->ancillary_data_len > 0) {
// TODO: init subparser if implemented here
parser->state = SPP_PARSER_STATE_SH_ANCILLARY_SUBPARSER;
break;
}
parser->state = SPP_PARSER_STATE_DATA_SUBPARSER;
return false;
}
case SPP_PARSER_STATE_SH_ANCILLARY_SUBPARSER:
{
// NOTE: Has to be implemented externally
assert(false);
break;
}
case SPP_PARSER_STATE_DATA_SUBPARSER:
{
// NOTE: Has to be implemented externally
assert(false);
break;
}
}
return true;
}
size_t spp_parser_read(struct spp_parser *parser,
const uint8_t *buffer,
size_t length)
{
const uint8_t *const end = buffer + length;
const uint8_t *cur = buffer;
const enum spp_parser_state prev_state = parser->state;
for (; cur != end; ++cur) {
// give control back to the caller on every state change
if (parser->state != prev_state)
break;
if (!spp_parse_byte(parser, *cur)) {
/* need to increase by one here to ensure that read
* count is correct.
*/
cur += 1;
break;
}
}
return cur - buffer;
}
void spp_parser_reset(struct spp_parser *parser)
{
parser->base.status = PARSER_STATUS_GOOD;
parser->base.next_bytes = 0;
parser->base.flags = PARSER_FLAG_NONE;
parser->state = SPP_PARSER_STATE_PH_P1_MSB;
}
bool spp_parser_get_meta(const struct spp_parser *parser,
struct spp_meta_t *dest)
{
if (parser->state < SPP_PARSER_STATE_SH_ANCILLARY_SUBPARSER) {
/* metadata is only ready after the timecode has been parsed
* as time is part of the metadata.
*/
return false;
}
dest->apid = parser->header.apid;
dest->is_request = parser->header.is_request;
dest->segment_number = parser->header.segment_number;
dest->segment_status = parser->header.segment_status;
dest->dtn_timestamp = parser->dtn_timestamp;
dest->dtn_counter = 0;
return true;
}
bool spp_parser_get_data_length(const struct spp_parser *parser,
size_t *dest)
{
if (parser->state < SPP_PARSER_STATE_SH_ANCILLARY_SUBPARSER) {
/* metadata is only ready after the timecode has been parsed
* since timecode may be variable length, we need to parse it
* first to be sure when its over.
*/
return false;
}
*dest = parser->data_length;
return true;
}