aboutsummaryrefslogtreecommitdiffstats
path: root/libasn1fix/asn1fix_bitstring.c
diff options
context:
space:
mode:
Diffstat (limited to 'libasn1fix/asn1fix_bitstring.c')
-rw-r--r--libasn1fix/asn1fix_bitstring.c230
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;
+}