diff options
Diffstat (limited to 'libasn1fix/asn1fix_bitstring.c')
-rw-r--r-- | libasn1fix/asn1fix_bitstring.c | 230 |
1 files changed, 230 insertions, 0 deletions
diff --git a/libasn1fix/asn1fix_bitstring.c b/libasn1fix/asn1fix_bitstring.c new file mode 100644 index 00000000..0d3961d4 --- /dev/null +++ b/libasn1fix/asn1fix_bitstring.c @@ -0,0 +1,230 @@ +#include "asn1fix_internal.h" + +int asn1f_fix_bit_string_value(arg_t *arg, asn1p_expr_t *ttype); +static void asn1f_BS_remove_trailing_zero_bits(asn1p_value_t *value); +static int asn1f_BS_unparsed_convert(arg_t *arg, asn1p_value_t *value, asn1p_expr_t *ttype); + +int +asn1f_fix_bit_string(arg_t *arg) { + asn1p_expr_t *expr = arg->expr; + int r_value = 0; + int ret; + + if(expr->meta_type == AMT_VALUE) { + asn1p_expr_t *ttype; + + DEBUG("%s(%s) for line %d", __func__, + expr->Identifier, expr->_lineno); + + ttype = asn1f_find_terminal_type(arg, expr, 0); + if(ttype && ttype->expr_type == ASN_BASIC_BIT_STRING) { + ret = asn1f_fix_bit_string_value(arg, ttype); + RET2RVAL(ret, r_value); + } + } + + return r_value; +} + +int +asn1f_fix_bit_string_value(arg_t *arg, asn1p_expr_t *ttype) { + asn1p_expr_t *expr = arg->expr; + int r_value = 0; + + DEBUG("%s(%s) for line %d", __func__, + expr->Identifier, expr->_lineno); + + switch(expr->value->type) { + case ATV_UNPARSED: + /* + * Most definitely we have something like + * value BitStringType1 ::= { a, b, c } + * which could not be parsed by the LALR parser, mostly + * because it requires knowledge about BitStringType1 + * during the parsing. So, here's a little hack: we create + * a buffer containing the full specification of a module, + * which contains some pre-defined INTEGER type with the + * opaque definition "{ a, b, c }" from the bit string. + */ + if(asn1f_BS_unparsed_convert(arg, expr->value, ttype)) { + r_value = -1; + break; + } + /* Fall through: remove trailing zero bits */ + case ATV_BITVECTOR: + asn1f_BS_remove_trailing_zero_bits(expr->value); + break; + default: + break; + } + + return r_value; +} + +static void +asn1f_BS_remove_trailing_zero_bits(asn1p_value_t *value) { + int lmfb = -1; /* Last meaningful byte position */ + int bits; /* Number of bits in the BIT STRING value */ + int b; + + assert(value->type == ATV_BITVECTOR); + + bits = value->value.binary_vector.size_in_bits; + /* + * Figure out the rightmost meaningful byte. + */ + for(b = 0; b < ((bits + 7) >> 3); b++) { + uint8_t uc = value->value.binary_vector.bits[b]; + if(uc && b > lmfb) + lmfb = b; + } + if(lmfb == -1) { + bits = 0; + } else { + uint8_t uc; + uc = value->value.binary_vector.bits[lmfb]; + bits = (lmfb+1) * 8; + /* + * Squeeze the bit string width until the rightmost + * bit is set. + */ + for(; uc && (uc & 1) == 0; uc >>= 1) + bits--; + if(uc == 0) { + bits = lmfb * 8; + } + } + value->value.binary_vector.size_in_bits = bits; +} + +static int +asn1f_BS_unparsed_convert(arg_t *arg, asn1p_value_t *value, asn1p_expr_t *ttype) { + asn1p_t *asn; + asn1p_module_t *mod; + asn1p_expr_t *V; + asn1p_expr_t *bit; + asn1_integer_t aI; + uint8_t *bitbuf; + int bits; + int psize; + char *p; + int ret; + int r_value = 0; + + assert(value->type == ATV_UNPARSED); + + psize = value->value.string.size + 64; + p = malloc(psize); + if(p == NULL) + return -1; + + ret = snprintf(p, psize, + "M DEFINITIONS ::=\nBEGIN\n" + "V ::= INTEGER %s\n" + "END\n", + value->value.string.buf + ); + assert(ret < psize); + psize = ret; + + asn = asn1p_parse_buffer(p, psize, A1P_NOFLAGS); + free(p); + if(asn == NULL) { + FATAL("Cannot parse BIT STRING value %s " + "defined as %s at line %d", + arg->expr->Identifier, + value->value.string.buf, + arg->expr->_lineno + ); + return -1; + } + + mod = TQ_FIRST(&(asn->modules)); + assert(mod); + V = TQ_FIRST(&(mod->members)); + assert(V); + assert(strcmp(V->Identifier, "V") == 0); + assert(TQ_FIRST(&(V->members))); + + /* + * Simple loop just to fetch the maximal bit position + * out of the BIT STRING value defined as NamedBitList. + */ + aI = -1; + TQ_FOR(bit, &(V->members), next) { + asn1p_expr_t *bitdef; + bitdef = asn1f_lookup_child(ttype, bit->Identifier); + if(bitdef && bitdef->value + && bitdef->value->type == ATV_INTEGER) { + if(bitdef->value->value.v_integer > aI) + aI = bitdef->value->value.v_integer; + } + } + + if(aI > 1024 * 1024 * 8) { /* One megabyte */ + FATAL("Unsupportedly large BIT STRING value \"%s\" " + "defined at line %d " + "(larger than 1MByte)", + arg->expr->Identifier, + arg->expr->_lineno + ); + asn1p_free(asn); + return -1; + } + + bits = aI + 1; /* Number of bits is more than a last bit position */ + bitbuf = calloc(1, 1 + ((bits + 7) / 8)); + if(bitbuf == NULL) { + asn1p_free(asn); + return -1; + } + + TQ_FOR(bit, &(V->members), next) { + asn1p_expr_t *bitdef; + int set_bit_pos; + + if(bit->value) { + WARNING("Identifier \"%s\" at line %d " + "must not have a value", + bit->Identifier, bit->_lineno); + RET2RVAL(1, r_value); + } + bitdef = asn1f_lookup_child(ttype, bit->Identifier); + if(bitdef == NULL) { + FATAL("Identifier \"%s\" at line %d is not defined " + "in the \"%s\" type definition at line %d", + bit->Identifier, + bit->_lineno, + ttype->Identifier, + ttype->_lineno + ); + RET2RVAL(-1, r_value); + continue; + } + if(bitdef->value == NULL + || bitdef->value->type != ATV_INTEGER) { + FATAL("Broken identifier " + "\"%s\" at line %d " + "referenced by \"%s\" at line %d", + bitdef->Identifier, + bitdef->_lineno, + arg->expr->Identifier, + arg->expr->_lineno + ); + RET2RVAL(-1, r_value); + continue; + } + + assert(bitdef->value->value.v_integer < bits); + set_bit_pos = bitdef->value->value.v_integer; + bitbuf[set_bit_pos>>3] |= 1 << (7-(set_bit_pos % 8)); + } + + asn1p_free(asn); + free(value->value.string.buf); + value->type = ATV_BITVECTOR; + value->value.binary_vector.bits = bitbuf; + value->value.binary_vector.size_in_bits = bits; + + return r_value; +} |