diff options
Diffstat (limited to 'libasn1fix/asn1fix.c')
-rw-r--r-- | libasn1fix/asn1fix.c | 354 |
1 files changed, 354 insertions, 0 deletions
diff --git a/libasn1fix/asn1fix.c b/libasn1fix/asn1fix.c new file mode 100644 index 00000000..af110999 --- /dev/null +++ b/libasn1fix/asn1fix.c @@ -0,0 +1,354 @@ +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <stdarg.h> + +#include "asn1fix.h" +#include "asn1fix_internal.h" + +/* Print everything to stderr */ +static void _default_error_logger(int _severity, const char *fmt, ...); + +/* + * Internal check functions. + */ +static int asn1f_fix_module(arg_t *arg); +static int asn1f_fix_simple(arg_t *arg); /* For INTEGER/ENUMERATED */ +static int asn1f_fix_constructed(arg_t *arg); /* For SEQUENCE/SET/CHOICE */ +static int asn1f_fix_constraints(arg_t *arg); /* For subtype constraints */ + + +/* + * Scan every module defined here in search for inconsistences. + */ +int +asn1f_process(asn1p_t *asn, enum asn1f_flags flags, + error_logger_f error_logger) { + arg_t arg; + int fatals = 0; + int warnings = 0; + + /* + * Check validity of arguments. + */ + if(asn == NULL) { + errno = EINVAL; + return -1; + } + + /* + * If errors handler is not specified, default to internal one. + */ + if(error_logger == 0) { + error_logger = _default_error_logger; + } + + memset(&arg, 0, sizeof(arg)); + arg.asn = asn; + arg.eh = error_logger; + + if(flags & A1F_DEBUG) { + arg.debug = arg.eh; + arg.debug(-1, "Called %s() with flags %d", __func__, flags); + flags &= ~A1F_DEBUG; + } + + /* + * Check that we haven't missed an unknown flag. + */ + if(flags) { + errno = EINVAL; + return -1; + } + + /* + * Process each module in the list. + */ + TQ_FOR(arg.mod, &(asn->modules), mod_next) { + int ret = asn1f_fix_module(&arg); + /* + * These lines are used for illustration purposes. + * RET2RVAL() is used everywhere else. + */ + if(ret == -1) fatals++; + if(ret == 1) warnings++; + } + + /* + * Compute a return value. + */ + return fatals?-1:warnings?1:0; +} + +/* + * Check the internals of a single module. + */ +static int +asn1f_fix_module(arg_t *arg) { + asn1p_expr_t *expr; + int rvalue = 0; + + switch((arg->mod->module_flags + & (MSF_EXPLICIT_TAGS | MSF_IMPLICIT_TAGS | MSF_AUTOMATIC_TAGS))) { + case MSF_NOFLAGS: + case MSF_EXPLICIT_TAGS: + case MSF_IMPLICIT_TAGS: + case MSF_AUTOMATIC_TAGS: + break; + default: + FATAL("Module %s defined with ambiguous global tagging mode", + arg->mod->Identifier); + RET2RVAL(-1, rvalue); + } + + /* + * Do various non-recursive transformations. + * Order is not important. + */ + TQ_FOR(expr, &(arg->mod->members), next) { + int ret; + arg->expr = expr; + + if(expr->meta_type == AMT_PARAMTYPE) + /* Do not process the parametrized type just yet */ + continue; + + DEBUG("=== Now processing \"%s\" at line %d ===", + expr->Identifier, expr->_lineno); + assert(expr->meta_type != AMT_INVALID); + + /* + * 2.1 Pre-process simple types (ENUMERATED, INTEGER, etc). + */ + ret = asn1f_recurse_expr(arg, asn1f_fix_simple); + RET2RVAL(ret, rvalue); + + /* + * 2.[234] Process SEQUENCE/SET/CHOICE types. + */ + ret = asn1f_recurse_expr(arg, asn1f_fix_constructed); + RET2RVAL(ret, rvalue); + + /* + * 2.5.4 + */ + ret = asn1f_recurse_expr(arg, asn1f_fix_dereference_types); + RET2RVAL(ret, rvalue); + + /* + * 2.5.5 + */ + ret = asn1f_recurse_expr(arg, asn1f_fix_dereference_values); + RET2RVAL(ret, rvalue); + + /* + * Resolve references in constraints. + */ + ret = asn1f_recurse_expr(arg, asn1f_fix_constraints); + RET2RVAL(ret, rvalue); + + /* + * 6. INTEGER value processed at 2.5.4. + */ + + /* + * Make sure everybody's behaving well. + */ + assert(arg->expr == expr); + } + + /* + * 5. Automatic tagging + */ + TQ_FOR(expr, &(arg->mod->members), next) { + int ret; + + arg->expr = expr; + + ret = asn1f_recurse_expr(arg, asn1f_fix_constr_autotag); + RET2RVAL(ret, rvalue); + + assert(arg->expr == expr); + } + + /* + * 8. fix BIT STRING + * 9. fix spaces in cstrings + */ + TQ_FOR(expr, &(arg->mod->members), next) { + int ret; + arg->expr = expr; + + ret = asn1f_recurse_expr(arg, asn1f_fix_bit_string); + RET2RVAL(ret, rvalue); + + ret = asn1f_recurse_expr(arg, asn1f_fix_cstring); + RET2RVAL(ret, rvalue); + + assert(arg->expr == expr); + } + + /* + * ... Check for tags distinctness. + */ + TQ_FOR(expr, &(arg->mod->members), next) { + int ret; + arg->expr = expr; + + ret = asn1f_recurse_expr(arg, asn1f_check_constr_tags_distinct); + RET2RVAL(ret, rvalue); + + assert(arg->expr == expr); + } + + return rvalue; +} + + +static int +asn1f_fix_simple(arg_t *arg) { + int rvalue = 0; + int ret; + + ret = asn1f_fix_enum(arg); + RET2RVAL(ret, rvalue); + + ret = asn1f_fix_integer(arg); + RET2RVAL(ret, rvalue); + + return rvalue; +} + +static int +asn1f_fix_constructed(arg_t *arg) { + int rvalue = 0; + int ret; + + switch(arg->expr->expr_type) { + case ASN_CONSTR_SEQUENCE: + case ASN_CONSTR_SET: + case ASN_CONSTR_CHOICE: + break; + default: + return 0; + } + + /* Check identifier distinctness */ + ret = asn1f_check_unique_expr(arg, NULL); + RET2RVAL(ret, rvalue); + + /* Fix extensibility */ + ret = asn1f_fix_constr_ext(arg); + RET2RVAL(ret, rvalue); + + /* Fix tagging */ + ret = asn1f_fix_constr_tag(arg); + RET2RVAL(ret, rvalue); + + return rvalue; +} + +static int +_constraint_value_resolve(arg_t *arg, asn1p_value_t **value) { + asn1p_expr_t expr; + asn1p_expr_t *tmp_expr; + asn1p_module_t *tmp_mod; + asn1p_module_t *mod_r = NULL; + int rvalue = 0; + int ret; + + tmp_expr = asn1f_lookup_symbol(arg, (*value)->value.reference, &mod_r); + if(tmp_expr == NULL) { + FATAL("Cannot find symbol %s " + "used in %s subtype constraint at line %d", + asn1f_printable_reference((*value)->value.reference), + arg->expr->Identifier, arg->expr->_lineno); + assert((*value)->type == ATV_REFERENCED); + return -1; + } + + memset(&expr, 0, sizeof(expr)); + expr.meta_type = tmp_expr->meta_type; + expr.expr_type = tmp_expr->expr_type; + expr.Identifier = tmp_expr->Identifier; + expr.value = *value; + tmp_expr = arg->expr; + tmp_mod = arg->mod; + arg->expr = &expr; + arg->mod = mod_r; + ret = asn1f_fix_dereference_values(arg); + RET2RVAL(ret, rvalue); + arg->expr = tmp_expr; + arg->mod = tmp_mod; + assert(expr.value); + *value = expr.value; + + return rvalue; +} + +static int +_resolve_constraints(arg_t *arg, asn1p_constraint_t *ct) { + int rvalue = 0; + int ret; + int el; + + /* Don't touch information object classes */ + if(ct->type == ACT_CT_WCOMP + || ct->type == ACT_CT_WCOMPS + || ct->type == ACT_CA_CRC) + return 0; + + if(ct->value && ct->value->type == ATV_REFERENCED) { + ret = _constraint_value_resolve(arg, &ct->value); + RET2RVAL(ret, rvalue); + } + if(ct->range_start && ct->range_start->type == ATV_REFERENCED) { + ret = _constraint_value_resolve(arg, &ct->range_start); + RET2RVAL(ret, rvalue); + } + if(ct->range_stop && ct->range_stop->type == ATV_REFERENCED) { + ret = _constraint_value_resolve(arg, &ct->range_stop); + RET2RVAL(ret, rvalue); + } + + for(el = 0; el < ct->el_count; el++) { + ret = _resolve_constraints(arg, ct->elements[el]); + RET2RVAL(ret, rvalue); + } + + return rvalue; +} + +static int +asn1f_fix_constraints(arg_t *arg) { + int rvalue = 0; + int ret; + + if(arg->expr->constraints) { + ret = _resolve_constraints(arg, arg->expr->constraints); + RET2RVAL(ret, rvalue); + } + + return rvalue; +} + +/* + * Print everything to stderr + */ +static void +_default_error_logger(int _severity, const char *fmt, ...) { + va_list ap; + char *pfx = ""; + + switch(_severity) { + case -1: pfx = "DEBUG: "; break; + case 0: pfx = "WARNING: "; break; + case 1: pfx = "FATAL: "; break; + } + + fprintf(stderr, "%s", pfx); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); +} |