ud3tn/components/bundle7/eid.c
Maximilian Nitsch 611b67fb0e refactor: cleanup includes
Removes unused includes detected by the clangd LSP.

Signed-off-by: Maximilian Nitsch <maximilian.nitsch@d3tn.com>
2026-05-20 16:16:35 +02:00

414 lines
9.8 KiB
C

// SPDX-License-Identifier: BSD-3-Clause OR Apache-2.0
#include "bundle7/bundle7.h"
#include "bundle7/eid.h"
#include "ud3tn/bundle.h"
#include "ud3tn/eid.h"
#include "cbor.h"
#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// --------------------------------
// Endpoint Identifier (EID) Parser
// --------------------------------
static CborError eid_parse_dtn(CborValue *it, char **eid);
static CborError eid_parse_ipn(CborValue *it, struct ipn_eid *eid);
CborError bundle7_eid_parse_cbor(CborValue *it, struct eid *eid)
{
CborValue recursed;
CborError err;
size_t length;
uint64_t schema;
if (!cbor_value_is_array(it) || !cbor_value_is_length_known(it))
return CborErrorIllegalType;
// Array must contain 2 items
if (cbor_value_get_array_length(it, &length) != CborNoError)
return CborErrorIllegalType;
if (length != 2)
return CborErrorIllegalType;
// Enter EID array
err = cbor_value_enter_container(it, &recursed);
if (err)
return err;
// EID schema (first element)
if (!cbor_value_is_unsigned_integer(&recursed))
return CborErrorIllegalType;
cbor_value_get_uint64(&recursed, &schema);
// Second item (schema specific)
err = cbor_value_advance_fixed(&recursed);
if (err)
return err;
char *dtn_eid;
struct ipn_eid ipn_eid;
// Call schema specific parsing functions
switch (schema) {
case BUNDLE_V7_EID_SCHEMA_DTN:
err = eid_parse_dtn(&recursed, &dtn_eid);
break;
case BUNDLE_V7_EID_SCHEMA_IPN:
err = eid_parse_ipn(&recursed, &ipn_eid);
break;
// unknown schema
default:
return CborErrorIllegalType;
}
// SSP parsing returned error
if (err)
return err;
assert(cbor_value_at_end(&recursed));
// Leave EID array
err = cbor_value_leave_container(it, &recursed);
// Validation/post-processing
if (schema == BUNDLE_V7_EID_SCHEMA_DTN) {
if (err) {
free(dtn_eid);
return err;
} else if (dtn_eid != NULL && !eidstr_dtn_is_valid(dtn_eid)) {
free(dtn_eid);
return CborErrorImproperValue;
}
eid->scheme = EID_SCHEME_DTN;
eid->dtn_eid = dtn_eid;
return CborNoError;
}
if (err)
return err;
// NOTE: We could consider normalizing ipn:0.0 or ipn:0.x to dtn:none here.
// We do not do this (for now) to ensure that the primary block stays unchanged.
// This explicitly includes the case where an ipn EID has a zero node number but nonzero
// service number, which is considered equal to the Null Endpoint.
eid->scheme = EID_SCHEME_IPN;
eid->ipn_eid = ipn_eid;
return CborNoError;
}
CborError eid_parse_dtn(CborValue *it, char **eid)
{
CborError err;
size_t length;
uint64_t num;
// Special case:
//
// null-EID ("dtn:none")
//
if (cbor_value_is_unsigned_integer(it)) {
cbor_value_get_uint64(it, &num);
// The only allowed value here is zero
if (num != 0)
return CborErrorIllegalType;
// Go to end of array
cbor_value_advance_fixed(it);
// Allocate output buffer
*eid = NULL;
return CborNoError;
}
// The SSP of the "dtn" schema is a CBOR text string
if (!cbor_value_is_text_string(it))
return CborErrorIllegalType;
// Determine SSP length. We do not check if the length is known because
// this is already done in cbor_value_get_string_length(). If the
// length is unknown a CborErrorUnknownLength will be returned
err = cbor_value_get_string_length(it, &length);
if (err)
return err;
// Remaining bytes are not sufficient
if ((size_t)(it->parser->source.end - it->source.ptr) < length)
return CborErrorUnexpectedEOF;
// The number of bytes written to the buffer will be written to the
// length variable
length += 1; // '\0' termination
// Allocate output buffer for "dtn:" prefix + SSP
*eid = malloc(4 + length);
if (*eid == NULL)
return CborErrorOutOfMemory;
// Copy prefix
memcpy(*eid, "dtn:", 4);
// Copy SSP + '\0' and advance iterator to next element after SSP.
err = cbor_value_copy_text_string(it, *eid + 4, &length, it);
if (err) {
free(*eid);
// Ensure that the pointer is not used afterwards.
// Otherwise a parser reset might try to double-free it.
*eid = NULL;
return err;
}
return CborNoError;
}
static CborError eid_parse_ipn(CborValue *it, struct ipn_eid *eid)
{
CborValue recursed;
CborError err;
uint64_t nodenum, servicenum;
uint64_t allocatorid_tmp;
size_t array_length;
// Enter array
if (!cbor_value_is_array(it) || !cbor_value_is_length_known(it))
return CborErrorIllegalType;
// Get array length to determine EID format
if (cbor_value_get_array_length(it, &array_length) != CborNoError)
return CborErrorIllegalType;
err = cbor_value_enter_container(it, &recursed);
if (err)
return err;
if (array_length == 2) {
// FQNN
if (!cbor_value_is_unsigned_integer(&recursed))
return CborErrorIllegalType;
cbor_value_get_uint64(&recursed, &nodenum);
} else if (array_length == 3) {
// Allocator ID
if (!cbor_value_is_unsigned_integer(&recursed))
return CborErrorIllegalType;
cbor_value_get_uint64(&recursed, &allocatorid_tmp);
// Advance to node number
err = cbor_value_advance_fixed(&recursed);
if (err)
return err;
// Assigned node number
if (!cbor_value_is_unsigned_integer(&recursed))
return CborErrorIllegalType;
cbor_value_get_uint64(&recursed, &nodenum);
if (allocatorid_tmp > UINT32_MAX || nodenum > UINT32_MAX)
return CborErrorIllegalType;
nodenum = ipn_eid_get_fqnn(allocatorid_tmp, nodenum);
} else {
// Invalid array length
return CborErrorIllegalType;
}
// Advance to service number
err = cbor_value_advance_fixed(&recursed);
if (err)
return err;
// Service number
// --------------
if (!cbor_value_is_unsigned_integer(&recursed))
return CborErrorIllegalType;
cbor_value_get_uint64(&recursed, &servicenum);
err = cbor_value_advance_fixed(&recursed);
if (err)
return err;
// Expect end of array
if (!cbor_value_at_end(&recursed))
return CborErrorIllegalType;
// Leave array
err = cbor_value_leave_container(it, &recursed);
if (err)
return err;
eid->fully_qualified_node_number = nodenum;
eid->service_number = servicenum;
return CborNoError;
}
struct eid bundle7_eid_parse(const uint8_t *buffer, size_t length)
{
struct eid eid;
CborValue it;
CborParser parser;
CborError err;
err = cbor_parser_init(buffer, length, 0, &parser, &it);
if (err != CborNoError)
return eid_get_invalid();
err = bundle7_eid_parse_cbor(&it, &eid);
if (err != CborNoError)
return eid_get_invalid();
return eid;
}
// ------------------------------------
// Endpoint Identifier (EID) Serializer
// ------------------------------------
//static const uint8_t _dtn_none[3] = {
// 0x82, 0x01, 0x00
//};
CborError serialize_dtn_none(CborEncoder *encoder)
{
CborEncoder recursed;
cbor_encoder_create_array(encoder, &recursed, 2);
cbor_encode_uint(&recursed, BUNDLE_V7_EID_SCHEMA_DTN);
cbor_encode_uint(&recursed, 0);
cbor_encoder_close_container(encoder, &recursed);
return CborNoError;
}
CborError serialize_dtn(const char *eid, CborEncoder *encoder)
{
CborEncoder recursed;
size_t ssp_length = strlen(eid) - 4;
// EID container
cbor_encoder_create_array(encoder, &recursed, 2);
// Schema
cbor_encode_uint(&recursed, BUNDLE_V7_EID_SCHEMA_DTN);
// SSP
cbor_encode_text_string(&recursed, eid + 4, ssp_length);
// EID container
cbor_encoder_close_container(encoder, &recursed);
return CborNoError;
}
CborError serialize_ipn(const struct ipn_eid eid, CborEncoder *encoder)
{
CborEncoder inner, ssp;
// EID container
cbor_encoder_create_array(encoder, &inner, 2);
// Schema
cbor_encode_uint(&inner, BUNDLE_V7_EID_SCHEMA_IPN);
// SSP
if (ipn_eid_allocator(eid) == 0 || EID_IPN_FORCE_2_ELEMENT) {
cbor_encoder_create_array(&inner, &ssp, 2);
cbor_encode_uint(&ssp, eid.fully_qualified_node_number);
cbor_encode_uint(&ssp, eid.service_number);
cbor_encoder_close_container(&inner, &ssp);
} else {
cbor_encoder_create_array(&inner, &ssp, 3);
cbor_encode_uint(&ssp, ipn_eid_allocator(eid));
cbor_encode_uint(&ssp, ipn_eid_node(eid));
cbor_encode_uint(&ssp, eid.service_number);
cbor_encoder_close_container(&inner, &ssp);
}
// EID container
cbor_encoder_close_container(encoder, &inner);
return CborNoError;
}
uint8_t *bundle7_eid_serialize_alloc(const struct eid eid, size_t *length)
{
const size_t buffer_size = bundle7_eid_sizeof(eid);
if (buffer_size == 0)
return NULL;
uint8_t *const buffer = malloc(buffer_size);
// We could not allocate enough memory
if (buffer == NULL)
return NULL;
int written = bundle7_eid_serialize(eid, buffer, buffer_size);
if (written <= 0) {
free(buffer);
return NULL;
}
// Shorten buffer to correct length
uint8_t *compress = realloc(buffer, written);
// Something went south during realloc, clear buffer and abort
if (compress == NULL) {
// False positive in some versions of cppcheck. It is correct
// to free here as `realloc` failed and does not free.
// cppcheck-suppress doubleFree
free(buffer);
return NULL;
}
*length = (size_t)written;
return compress;
}
CborError bundle7_eid_serialize_cbor(const struct eid eid, CborEncoder *encoder)
{
// Special case: null-EID
if (eid.scheme == EID_SCHEME_DTN && eid.dtn_eid == NULL)
return serialize_dtn_none(encoder);
// DTN (allows empty SSPs)
else if (eid.scheme == EID_SCHEME_DTN)
return serialize_dtn(eid.dtn_eid, encoder);
// IPN
else if (eid.scheme == EID_SCHEME_IPN)
return serialize_ipn(eid.ipn_eid, encoder);
// Unknown schema
else
return CborErrorIllegalType;
}
int bundle7_eid_serialize(const struct eid eid, uint8_t *buffer, size_t buffer_size)
{
CborEncoder encoder;
CborError err;
cbor_encoder_init(&encoder, buffer, buffer_size, 0);
err = bundle7_eid_serialize_cbor(eid, &encoder);
// A non-recoverable error occurred
if (err != CborNoError && err != CborErrorOutOfMemory)
return -1;
// Out of memory (buffer too small)
if (cbor_encoder_get_extra_bytes_needed(&encoder) > 0)
return 0;
return cbor_encoder_get_buffer_size(&encoder, buffer);
}