/*-
* Copyright (c) 2003, 2004, 2005, 2006 Lev Walkin <vlm@lionet.info>.
* All rights reserved.
* Redistribution and modifications are permitted subject to BSD license.
*/
#include <asn_internal.h>
#include <OCTET_STRING.h>
#include <BIT_STRING.h> /* for .bits_unused member */
#include <errno.h>
/*
* OCTET STRING basic type description.
*/
static ber_tlv_tag_t asn_DEF_OCTET_STRING_tags[] = {
(ASN_TAG_CLASS_UNIVERSAL | (4 << 2))
};
static asn_OCTET_STRING_specifics_t asn_DEF_OCTET_STRING_specs = {
sizeof(OCTET_STRING_t),
offsetof(OCTET_STRING_t, _asn_ctx),
ASN_OSUBV_STR
};
static asn_per_constraints_t asn_DEF_OCTET_STRING_constraints = {
{ APC_CONSTRAINED, 8, 8, 0, 255 },
{ APC_SEMI_CONSTRAINED, -1, -1, 0, 0 },
0, 0
};
asn_TYPE_descriptor_t asn_DEF_OCTET_STRING = {
"OCTET STRING", /* Canonical name */
"OCTET_STRING", /* XML tag name */
OCTET_STRING_free,
OCTET_STRING_print, /* non-ascii stuff, generally */
asn_generic_no_constraint,
OCTET_STRING_decode_ber,
OCTET_STRING_encode_der,
OCTET_STRING_decode_xer_hex,
OCTET_STRING_encode_xer,
OCTET_STRING_decode_uper, /* Unaligned PER decoder */
OCTET_STRING_encode_uper, /* Unaligned PER encoder */
0, /* Use generic outmost tag fetcher */
asn_DEF_OCTET_STRING_tags,
sizeof(asn_DEF_OCTET_STRING_tags)
/ sizeof(asn_DEF_OCTET_STRING_tags[0]),
asn_DEF_OCTET_STRING_tags, /* Same as above */
sizeof(asn_DEF_OCTET_STRING_tags)
/ sizeof(asn_DEF_OCTET_STRING_tags[0]),
0, /* No PER visible constraints */
0, 0, /* No members */
&asn_DEF_OCTET_STRING_specs
};
#undef _CH_PHASE
#undef NEXT_PHASE
#undef PREV_PHASE
#define _CH_PHASE(ctx, inc) do { \
if(ctx->phase == 0) \
ctx->context = 0; \
ctx->phase += inc; \
} while(0)
#define NEXT_PHASE(ctx) _CH_PHASE(ctx, +1)
#define PREV_PHASE(ctx) _CH_PHASE(ctx, -1)
#undef ADVANCE
#define ADVANCE(num_bytes) do { \
size_t num = (num_bytes); \
buf_ptr = ((const char *)buf_ptr) + num; \
size -= num; \
consumed_myself += num; \
} while(0)
#undef RETURN
#define RETURN(_code) do { \
asn_dec_rval_t tmprval; \
tmprval.code = _code; \
tmprval.consumed = consumed_myself; \
return tmprval; \
} while(0)
#undef APPEND
#define APPEND(bufptr, bufsize) do { \
size_t _bs = (bufsize); /* Append size */ \
size_t _ns = ctx->context; /* Allocated now */ \
size_t _es = st->size + _bs; /* Expected size */ \
/* int is really a typeof(st->size): */ \
if((int)_es < 0) RETURN(RC_FAIL); \
if(_ns <= _es) { \
void *ptr; \
/* Be nice and round to the memory allocator */ \
do { _ns = _ns ? _ns << 1 : 16; } \
while(_ns <= _es); \
/* int is really a typeof(st->size): */ \
if((int)_ns < 0) RETURN(RC_FAIL); \
ptr = REALLOC(st->buf, _ns); \
if(ptr) { \
st->buf = (uint8_t *)ptr; \
ctx->context = _ns; \
} else { \
RETURN(RC_FAIL); \
} \
ASN_DEBUG("Reallocating into %ld", (long)_ns); \
} \
memcpy(st->buf + st->size, bufptr, _bs); \
/* Convenient nul-termination */ \
st->buf[_es] = '\0'; \
st->size = _es; \
} while(0)
/*
* The main reason why ASN.1 is still alive is that too much time and effort
* is necessary for learning it more or less adequately, thus creating a gut
* necessity to demonstrate that aquired skill everywhere afterwards.
* No, I am not going to explain what the following stuff is.
*/
struct _stack_el {
ber_tlv_len_t left; /* What's left to read (or -1) */
ber_tlv_len_t got; /* What was actually processed */
int cont_level; /* Depth of subcontainment */
int want_nulls; /* Want null "end of content" octets? */
int bits_chopped; /* Flag in BIT STRING mode */
ber_tlv_tag_t tag; /* For debugging purposes */
struct _stack_el *prev;
struct _stack_el *next;
};
struct _stack {
struct _stack_el *tail;
struct _stack_el *cur_ptr;
};
static struct _stack_el *
OS__add_stack_el(struct _stack *st) {
struct _stack_el *nel;
/*
* Reuse the old stack frame or allocate a new one.
*/
if(st->cur_ptr && st->cur_ptr->next) {
nel = st->cur_ptr->next;
nel->bits_chopped = 0;
nel->got = 0;
/* Retain the nel->cont_level, it's correct. */
} else {
nel = (struct _stack_el *)CALLOC(1, sizeof(struct _stack_el));
if(nel == NULL)
return NULL;
if(st->tail) {
/* Increase a subcontainment depth */
nel->cont_level = st->tail->cont_level + 1;
st->tail->next = nel;
}
nel->prev = st->tail;
st->tail = nel;
}
st->cur_ptr = nel;
return nel;
}
static struct _stack *
_new_stack() {
return (struct _stack *)CALLOC(1, sizeof(struct _stack));
}
/*
* Decode OCTET STRING type.
*/
asn_dec_rval_t
OCTET_STRING_decode_ber(asn_codec_ctx_t *opt_codec_ctx,
asn_TYPE_descriptor_t *td,
void **sptr, const void *buf_ptr, size_t size, int tag_mode) {
asn_OCTET_STRING_specifics_t *specs = td->specifics
? (asn_OCTET_STRING_specifics_t *)td->specifics
: &asn_DEF_OCTET_STRING_specs;
BIT_STRING_t *st = (BIT_STRING_t *)*sptr;
asn_dec_rval_t rval;
asn_struct_ctx_t *ctx;
ssize_t consumed_myself = 0;
struct _stack *stck; /* Expectations stack structure */
struct _stack_el *sel = 0; /* Stack element */
int tlv_constr;
enum asn_OS_Subvariant type_variant = specs->subvariant;
ASN_DEBUG("Decoding %s as %s (frame %ld)",
td->name,
(type_variant == ASN_OSUBV_STR) ?
"OCTET STRING" : "OS-SpecialCase",
(long)size);
/*
* Create the string if does not exist.
*/
if(st == NULL) {
st = (BIT_STRING_t *)(*sptr = CALLOC(1, specs->struct_size));
if(st == NULL) RETURN(RC_FAIL);
}
/* Restore parsing context */
ctx = (asn_struct_ctx_t *)((char *)st + specs->ctx_offset);
switch(ctx->phase) {
case 0:
/*
* Check tags.
*/
rval = ber_check_tags(opt_codec_ctx, td, ctx,
buf_ptr, size, tag_mode, -1,
&ctx->left, &tlv_constr);
if(rval.code != RC_OK)
return rval;
if(tlv_constr) {
/*
* Complex operation, requires stack of expectations.
*/
ctx->ptr = _new_stack();
if(ctx->ptr) {
stck = (struct _stack *)ctx->ptr;
} else {
RETURN(RC_FAIL);
}
} else {
/*
* Jump into stackless primitive decoding.
*/
_CH_PHASE(ctx, 3);
if(type_variant == ASN_OSUBV_ANY && tag_mode != 1)
APPEND(buf_ptr, rval.consumed);
ADVANCE(rval.consumed);
goto phase3;
}
NEXT_PHASE(ctx);
/* Fall through */
case 1:
phase1:
/*
* Fill the stack with expectations.
*/
stck = (struct _stack *)ctx->ptr;
sel = stck->cur_ptr;
do {
ber_tlv_tag_t tlv_tag;
ber_tlv_len_t tlv_len;
ber_tlv_tag_t expected_tag;
ssize_t tl, ll, tlvl;
/* This one works even if (sel->left == -1) */
ssize_t Left = ((!sel||(size_t)sel->left >= size)
?(ssize_t)size:sel->left);
ASN_DEBUG("%p, s->l=%ld, s->wn=%ld, s->g=%ld\n", sel,
(long)(sel?sel->left:0),
(long)(sel?sel->want_nulls:0),
(long)(sel?sel->got:0)
);
if(sel && sel->left <= 0 && sel->want_nulls == 0) {
if(sel->prev) {
struct _stack_el *prev = sel->prev;
if(prev->left != -1) {
if(prev->left < sel->got)
RETURN(RC_FAIL);
prev->left -= sel->got;
}
prev->got += sel->got;
sel = stck->cur_ptr = prev;
if(!sel) break;
tlv_constr = 1;
continue;
} else {
sel = stck->cur_ptr = 0;
break; /* Nothing to wait */
}
}
tl = ber_fetch_tag(buf_ptr, Left, &tlv_tag);
ASN_DEBUG("fetch tag(size=%ld,L=%ld), %sstack, left=%ld, wn=%ld, tl=%ld",
(long)size, (long)Left, sel?"":"!",
(long)(sel?sel->left:0),
(long)(sel?sel->want_nulls:0),
(long)tl);
switch(tl) {
case -1: RETURN(RC_FAIL);
case 0: RETURN(RC_WMORE);
}
tlv_constr = BER_TLV_CONSTRUCTED(buf_ptr);
ll = ber_fetch_length(tlv_constr,
(const char *)buf_ptr + tl,Left - tl,&tlv_len);
ASN_DEBUG("Got tag=%s, tc=%d, left=%ld, tl=%ld, len=%ld, ll=%ld",
ber_tlv_tag_string(tlv_tag), tlv_constr,
(long)Left, (long)tl, (long)tlv_len, (long)ll);
switch(ll) {
case -1: RETURN(RC_FAIL);
case 0: RETURN(R