286 lines
7.8 KiB
C
286 lines
7.8 KiB
C
/*
|
|
* Copyright (c) 2017 Lev Walkin <vlm@lionet.info>.
|
|
* All rights reserved.
|
|
* Redistribution and modifications are permitted subject to BSD license.
|
|
*/
|
|
#ifndef ASN_DISABLE_OER_SUPPORT
|
|
|
|
#include <asn_internal.h>
|
|
#include <constr_SET_OF.h>
|
|
#include <asn_SET_OF.h>
|
|
#include <errno.h>
|
|
|
|
/*
|
|
* This macro "eats" the part of the buffer which is definitely "consumed",
|
|
* i.e. was correctly converted into local representation or rightfully skipped.
|
|
*/
|
|
#undef ADVANCE
|
|
#define ADVANCE(num_bytes) \
|
|
do { \
|
|
size_t num = num_bytes; \
|
|
ptr = ((const char *)ptr) + num; \
|
|
size -= num; \
|
|
consumed_myself += num; \
|
|
} while(0)
|
|
|
|
/*
|
|
* Switch to the next phase of parsing.
|
|
*/
|
|
#undef NEXT_PHASE
|
|
#define NEXT_PHASE(ctx) \
|
|
do { \
|
|
ctx->phase++; \
|
|
ctx->step = 0; \
|
|
} while(0)
|
|
#undef SET_PHASE
|
|
#define SET_PHASE(ctx, value) \
|
|
do { \
|
|
ctx->phase = value; \
|
|
ctx->step = 0; \
|
|
} while(0)
|
|
|
|
/*
|
|
* Return a standardized complex structure.
|
|
*/
|
|
#undef RETURN
|
|
#define RETURN(_code) \
|
|
do { \
|
|
asn_dec_rval_t rval; \
|
|
rval.code = _code; \
|
|
rval.consumed = consumed_myself; \
|
|
return rval; \
|
|
} while(0)
|
|
|
|
/*
|
|
* The SEQUENCE OF and SET OF values utilize a "quantity field".
|
|
* It is is a pointless combination of #8.6 (length determinant, capable
|
|
* of encoding tiny and huge numbers in the shortest possible number of octets)
|
|
* and the variable sized integer. What could have been encoded by #8.6 alone
|
|
* is required to be encoded by #8.6 followed by that number of unsigned octets.
|
|
* This doesn't make too much sense. It seems that the original version of OER
|
|
* standard have been using the unconstrained unsigned integer as a quantity
|
|
* field, and this legacy have gone through ISO/ITU-T standardization process.
|
|
*/
|
|
static ssize_t
|
|
oer_fetch_quantity(const void *ptr, size_t size, size_t *qty_r) {
|
|
const uint8_t *b;
|
|
const uint8_t *bend;
|
|
size_t len = 0;
|
|
size_t qty;
|
|
|
|
ssize_t len_len = oer_fetch_length(ptr, size, &len);
|
|
if(len_len <= 0) {
|
|
*qty_r = 0;
|
|
return len_len;
|
|
}
|
|
|
|
if((len_len + len) > size) {
|
|
*qty_r = 0;
|
|
return 0;
|
|
}
|
|
|
|
b = (const uint8_t *)ptr + len_len;
|
|
bend = b + len;
|
|
|
|
/* Skip the leading 0-bytes */
|
|
for(; b < bend && *b == 0; b++) {
|
|
}
|
|
|
|
if((bend - b) > (ssize_t)sizeof(size_t)) {
|
|
/* Length is not representable by the native size_t type */
|
|
*qty_r = 0;
|
|
return -1;
|
|
}
|
|
|
|
for(qty = 0; b < bend; b++) {
|
|
qty = (qty << 8) + *b;
|
|
}
|
|
|
|
if(qty > RSIZE_MAX) { /* A bit of C11 validation */
|
|
*qty_r = 0;
|
|
return -1;
|
|
}
|
|
|
|
*qty_r = qty;
|
|
assert((size_t)len_len + len == (size_t)(bend - (const uint8_t *)ptr));
|
|
return len_len + len;
|
|
}
|
|
|
|
asn_dec_rval_t
|
|
SET_OF_decode_oer(const asn_codec_ctx_t *opt_codec_ctx,
|
|
const asn_TYPE_descriptor_t *td,
|
|
const asn_oer_constraints_t *constraints, void **struct_ptr,
|
|
const void *ptr, size_t size) {
|
|
const asn_SET_OF_specifics_t *specs = (const asn_SET_OF_specifics_t *)td->specifics;
|
|
asn_dec_rval_t rval = {RC_OK, 0};
|
|
void *st = *struct_ptr; /* Target structure */
|
|
asn_struct_ctx_t *ctx; /* Decoder context */
|
|
size_t consumed_myself = 0; /* Consumed bytes from ptr. */
|
|
|
|
(void)constraints;
|
|
|
|
if(ASN__STACK_OVERFLOW_CHECK(opt_codec_ctx))
|
|
ASN__DECODE_FAILED;
|
|
|
|
/*
|
|
* Create the target structure if it is not present already.
|
|
*/
|
|
if(st == 0) {
|
|
st = *struct_ptr = CALLOC(1, specs->struct_size);
|
|
if(st == 0) {
|
|
RETURN(RC_FAIL);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Restore parsing context.
|
|
*/
|
|
ctx = (asn_struct_ctx_t *)((char *)st + specs->ctx_offset);
|
|
|
|
/*
|
|
* Start to parse where left previously.
|
|
*/
|
|
switch(ctx->phase) {
|
|
case 0: {
|
|
/*
|
|
* Fetch number of elements to decode.
|
|
*/
|
|
size_t length = 0;
|
|
size_t len_size = oer_fetch_quantity(ptr, size, &length);
|
|
switch(len_size) {
|
|
case 0:
|
|
RETURN(RC_WMORE);
|
|
case -1:
|
|
RETURN(RC_FAIL);
|
|
default:
|
|
ADVANCE(len_size);
|
|
ctx->left = length;
|
|
}
|
|
}
|
|
NEXT_PHASE(ctx);
|
|
/* FALL THROUGH */
|
|
case 1: {
|
|
/* Decode components of the extension root */
|
|
asn_TYPE_member_t *elm = td->elements;
|
|
asn_anonymous_set_ *list = _A_SET_FROM_VOID(st);
|
|
const void *base_ptr = ptr;
|
|
ber_tlv_len_t base_ctx_left = ctx->left;
|
|
|
|
assert(td->elements_count == 1);
|
|
|
|
ASN_DEBUG("OER SET OF %s Decoding PHASE 1", td->name);
|
|
|
|
for(; ctx->left > 0; ctx->left--) {
|
|
asn_dec_rval_t rv = elm->type->op->oer_decoder(
|
|
opt_codec_ctx, elm->type,
|
|
elm->encoding_constraints.oer_constraints, &ctx->ptr, ptr,
|
|
size);
|
|
ADVANCE(rv.consumed);
|
|
switch(rv.code) {
|
|
case RC_OK:
|
|
if(ASN_SET_ADD(list, ctx->ptr) != 0) {
|
|
RETURN(RC_FAIL);
|
|
} else {
|
|
ctx->ptr = 0;
|
|
/*
|
|
* This check is to avoid compression bomb with
|
|
* specs like SEQUENCE/SET OF NULL which don't
|
|
* consume data at all.
|
|
*/
|
|
if(rv.consumed == 0 && base_ptr == ptr
|
|
&& (base_ctx_left - ctx->left) > 200) {
|
|
ASN__DECODE_FAILED;
|
|
}
|
|
break;
|
|
}
|
|
case RC_WMORE:
|
|
RETURN(RC_WMORE);
|
|
case RC_FAIL:
|
|
ASN_STRUCT_FREE(*elm->type, ctx->ptr);
|
|
ctx->ptr = 0;
|
|
SET_PHASE(ctx, 3);
|
|
RETURN(RC_FAIL);
|
|
}
|
|
}
|
|
/* Decoded decently. */
|
|
NEXT_PHASE(ctx);
|
|
}
|
|
/* Fall through */
|
|
case 2:
|
|
/* Ignore fully decoded */
|
|
assert(ctx->left == 0);
|
|
RETURN(RC_OK);
|
|
case 3:
|
|
/* Failed to decode. */
|
|
RETURN(RC_FAIL);
|
|
}
|
|
|
|
return rval;
|
|
}
|
|
|
|
static ssize_t
|
|
oer_put_quantity(size_t qty, asn_app_consume_bytes_f *cb, void *app_key) {
|
|
uint8_t buf[1 + sizeof(size_t)];
|
|
uint8_t *b = &buf[sizeof(size_t)]; /* Last addressable */
|
|
size_t encoded;
|
|
|
|
do {
|
|
*b-- = qty;
|
|
qty >>= 8;
|
|
} while(qty);
|
|
|
|
*b = sizeof(buf) - (b-buf) - 1;
|
|
encoded = sizeof(buf) - (b-buf);
|
|
if(cb(b, encoded, app_key) < 0)
|
|
return -1;
|
|
return encoded;
|
|
}
|
|
|
|
/*
|
|
* Encode as Canonical OER.
|
|
*/
|
|
asn_enc_rval_t
|
|
SET_OF_encode_oer(const asn_TYPE_descriptor_t *td,
|
|
const asn_oer_constraints_t *constraints, const void *sptr,
|
|
asn_app_consume_bytes_f *cb, void *app_key) {
|
|
const asn_TYPE_member_t *elm;
|
|
const asn_anonymous_set_ *list;
|
|
size_t computed_size = 0;
|
|
ssize_t qty_len;
|
|
int n;
|
|
|
|
(void)constraints;
|
|
|
|
if(!sptr) ASN__ENCODE_FAILED;
|
|
|
|
elm = td->elements;
|
|
list = _A_CSET_FROM_VOID(sptr);
|
|
|
|
qty_len = oer_put_quantity(list->count, cb, app_key);
|
|
if(qty_len < 0) {
|
|
ASN__ENCODE_FAILED;
|
|
}
|
|
computed_size += qty_len;
|
|
|
|
for(n = 0; n < list->count; n++) {
|
|
void *memb_ptr = list->array[n];
|
|
asn_enc_rval_t er = {0,0,0};
|
|
er = elm->type->op->oer_encoder(
|
|
elm->type, elm->encoding_constraints.oer_constraints, memb_ptr, cb,
|
|
app_key);
|
|
if(er.encoded < 0) {
|
|
return er;
|
|
} else {
|
|
computed_size += er.encoded;
|
|
}
|
|
}
|
|
|
|
{
|
|
asn_enc_rval_t erval = {0,0,0};
|
|
erval.encoded = computed_size;
|
|
ASN__ENCODED_OK(erval);
|
|
}
|
|
}
|
|
|
|
#endif /* ASN_DISABLE_OER_SUPPORT */
|