#include "asn1fix_internal.h" #include "asn1fix.h" /* Print everything to stderr */ static void _default_error_logger(int _severity, const char *fmt, ...); /* * Internal check functions. */ static int asn1f_fix_module__phase_1(arg_t *arg); static int asn1f_fix_module__phase_2(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_resolve_constraints(arg_t *arg); /* For subtype constraints */ static int asn1f_check_constraints(arg_t *arg); /* For subtype constraints */ static int asn1f_check_duplicate(arg_t *arg); static int asn1f_apply_unique_index(arg_t *arg); static int phase_1_1(arg_t *arg, int prm2); arg_t a1f_replace_me_with_proper_interface_arg; /* * 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; int ret; /* * 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; } /* Allow SIZE() constraint for INTEGER and other types */ if(flags & A1F_EXTENDED_SizeConstraint) { arg.flags |= A1F_EXTENDED_SizeConstraint; flags &= ~A1F_EXTENDED_SizeConstraint; if(arg.debug) { arg.debug(-1, "Extended SizeConstraint support enabled"); } } a1f_replace_me_with_proper_interface_arg = arg; /* * Check that we haven't missed an unknown flag. */ if(flags) { errno = EINVAL; return -1; } /* * Process each module in the list. * PHASE I. */ TQ_FOR(arg.mod, &(asn->modules), mod_next) { ret = asn1f_fix_module__phase_1(&arg); /* * These lines are used for illustration purposes. * RET2RVAL() is used everywhere else. */ if(ret == -1) fatals++; if(ret == 1) warnings++; } /* PHASE II. */ TQ_FOR(arg.mod, &(asn->modules), mod_next) { ret = asn1f_fix_module__phase_2(&arg); if(ret == -1) fatals++; if(ret == 1) warnings++; } memset(&a1f_replace_me_with_proper_interface_arg, 0, sizeof(arg_t)); /* * Compute a return value. */ return fatals?-1:warnings?1:0; } /* * Check the internals of a single module. */ static int asn1f_fix_module__phase_1(arg_t *arg) { asn1p_expr_t *expr; int rvalue = 0; int ret; asn1p_module_t *omod; /* * Check that we don't have a similarly named module. */ TQ_FOR(omod, &arg->asn->modules, mod_next) { int sameNames; if(omod == arg->mod) break; sameNames = strcmp(omod->ModuleName, arg->mod->ModuleName)?0:1; if(omod->module_oid && arg->mod->module_oid) { /* Compare only the OID. */ if(asn1p_oid_compare(omod->module_oid, arg->mod->module_oid) == 0) { FATAL("ASN.1 module %s in %s " "has the same OBJECT IDENTIFIER" " as module %s", omod->ModuleName, omod->source_file_name, arg->mod->ModuleName ); RET2RVAL(-1, rvalue); } else if(sameNames) { WARNING("ASN.1 module %s is defined more than once, with different OIDs", omod->ModuleName); RET2RVAL(1, rvalue); } } else if(sameNames) { FATAL("ASN.1 module %s is defined more than once", omod->ModuleName); RET2RVAL(-1, rvalue); } } switch((arg->mod->module_flags & MSF_MASK_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->ModuleName); RET2RVAL(-1, rvalue); } switch((arg->mod->module_flags & MSF_MASK_INSTRUCTIONS)) { case MSF_NOFLAGS: /* * arg->mod->module_flags |= MSF_TAG_INSTRUCTIONS; */ break; case MSF_unk_INSTRUCTIONS: WARNING("Module %s defined with unrecognized " "encoding reference", arg->mod->ModuleName); RET2RVAL(1, rvalue); /* Fall through */ case MSF_TAG_INSTRUCTIONS: case MSF_XER_INSTRUCTIONS: break; default: FATAL("Module %s defined with ambiguous encoding reference", arg->mod->ModuleName); RET2RVAL(-1, rvalue); } /* * Do various non-recursive transformations. */ TQ_FOR(expr, &(arg->mod->members), next) { arg->expr = expr; ret = phase_1_1(arg, 0); RET2RVAL(ret, rvalue); /* * Make sure everybody's behaving well. */ assert(arg->expr == expr); } TQ_FOR(expr, &(arg->mod->members), next) { arg->expr = expr; ret = phase_1_1(arg, 1); RET2RVAL(ret, rvalue); assert(arg->expr == expr); } /* * 5. Automatic tagging */ TQ_FOR(expr, &(arg->mod->members), next) { 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) { 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) { 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_module__phase_2(arg_t *arg) { asn1p_expr_t *expr; int rvalue = 0; int ret; TQ_FOR(expr, &(arg->mod->members), next) { arg->expr = expr; /* * Dereference DEFAULT values. */ ret = asn1f_recurse_expr(arg, asn1f_fix_dereference_defaults); RET2RVAL(ret, rvalue); /* * Check semantic validity of constraints. */ ret = asn1f_recurse_expr(arg, asn1f_check_constraints); RET2RVAL(ret, rvalue); /* * Uniquely tag each inner type. */ asn1f_apply_unique_index(0); ret = asn1f_recurse_expr(arg, asn1f_apply_unique_index); RET2RVAL(ret, rvalue); assert(arg->expr == expr); } return rvalue; } static int phase_1_1(arg_t *arg, int prm2) { asn1p_expr_t *expr = arg->expr; int rvalue = 0; int ret; if(expr->lhs_params && expr->spec_index == -1) { int i; if(!prm2) /* Do not process the parameterized type just yet */ return 0; for(i = 0; i < expr->specializations.pspecs_count; i++) { arg->expr = expr->specializations.pspec[i].my_clone; ret = phase_1_1(arg, 0); RET2RVAL(ret, rvalue); } arg->expr = expr; /* revert */ return rvalue; } else if(prm2) { return 0; /* Already done! */ } /* Check whether this type is a duplicate */ if(!expr->lhs_params) { ret = asn1f_check_duplicate(arg); RET2RVAL(ret, rvalue); } DEBUG("=== Now processing \"%s\" (%d/0x%x) at line %d ===", expr->Identifier, expr->meta_type, expr->expr_type, 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.5.4 */ ret = asn1f_recurse_expr(arg, asn1f_fix_dereference_types); RET2RVAL(ret, rvalue); /* * Fix tagging of top-level types. */ ret = asn1f_fix_constr_tag(arg, 1); RET2RVAL(ret, rvalue); /* * 2.[234] Process SEQUENCE/SET/CHOICE types. */ ret = asn1f_recurse_expr(arg, asn1f_fix_constructed); RET2RVAL(ret, rvalue); /* * 2.5.5 */ ret = asn1f_recurse_expr(arg, asn1f_fix_dereference_values); RET2RVAL(ret, rvalue); /* * Parse class objects and fill up the object class with data. */ ret = asn1f_parse_class_object(arg); RET2RVAL(ret, rvalue); /* * Resolve references in constraints. */ ret = asn1f_recurse_expr(arg, asn1f_resolve_constraints); RET2RVAL(ret, rvalue); /* * 6. INTEGER value processed at 2.5.4. */ 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); RET2RVAL(ret, rvalue); /* Fix extensibility */ ret = asn1f_fix_constr_ext(arg); RET2RVAL(ret, rvalue); /* Fix tagging */ ret = asn1f_fix_constr_tag(arg, 0); RET2RVAL(ret, rvalue); /* Import COMPONENTS OF stuff */ ret = asn1f_pull_components_of(arg); RET2RVAL(ret, rvalue); return rvalue; } static int asn1f_resolve_constraints(arg_t *arg) { asn1p_expr_t *top_parent; asn1p_expr_type_e etype; int rvalue = 0; int ret; top_parent = asn1f_find_terminal_type(arg, arg->expr); if(top_parent) etype = top_parent->expr_type; else etype = A1TC_INVALID; DEBUG("(%s)", arg->expr->Identifier); ret = asn1constraint_resolve(arg, arg->expr->constraints, etype, 0); RET2RVAL(ret, rvalue); return rvalue; } static int asn1f_check_constraints(arg_t *arg) { static enum asn1p_constraint_type_e test_types[] = { ACT_EL_RANGE, ACT_CT_SIZE, ACT_CT_FROM }; asn1p_expr_t *top_parent; asn1cnst_range_t *range; asn1p_expr_type_e etype; unsigned int i; int rvalue = 0; int ret; DEBUG("(%s{%d/%d})", arg->expr->Identifier, arg->expr->meta_type, arg->expr->expr_type); top_parent = asn1f_find_terminal_type(arg, arg->expr); if(!top_parent) return 0; etype = top_parent->expr_type; ret = asn1constraint_pullup(arg); RET2RVAL(ret, rvalue); for(i = 0; i < sizeof(test_types)/sizeof(test_types[0]); i++) { range = asn1constraint_compute_PER_range( etype, arg->expr->combined_constraints, test_types[i], 0, 0, CPR_noflags /* ignore -fbless-SIZE */); if(!range && errno == EPERM) { FATAL("This error happened for \"%s\" (meta %d) " "at line %d", arg->expr->Identifier, arg->expr->meta_type, arg->expr->_lineno); return -1; } asn1constraint_range_free(range); } return rvalue; } static int asn1f_check_duplicate(arg_t *arg) { arg_t tmparg = *arg; int rvalue = 0; /* * This is a linear scan in search of a similar type. * The linear scan is just fine for the task, no need to over-optimize. */ TQ_FOR(tmparg.mod, &arg->asn->modules, mod_next) { int critical = 1; /* FATAL */ if((arg->mod->_tags & MT_STANDARD_MODULE) != (tmparg.mod->_tags & MT_STANDARD_MODULE)) { /* Ignore clashes with standard module */ critical = 0; /* WARNING */ } TQ_FOR(tmparg.expr, &(tmparg.mod->members), next) { int diff_files; /* different files */ assert(tmparg.expr->Identifier); assert(arg->expr->Identifier); if(arg->expr->spec_index != -1) continue; if(tmparg.expr == arg->expr) break; if(strcmp(tmparg.expr->Identifier, arg->expr->Identifier)) continue; diff_files = strcmp(arg->mod->source_file_name, tmparg.mod->source_file_name) ? 1 : 0; LOG(critical, "ASN.1 expression \"%s\" at line %d of module %s\n" "clashes with expression \"%s\" at line %d of module %s" "%s%s%s.\n" "Rename or remove either instance " "to resolve the conflict", arg->expr->Identifier, arg->expr->_lineno, arg->mod->ModuleName, tmparg.expr->Identifier, tmparg.expr->_lineno, tmparg.mod->ModuleName, diff_files ? " (" : "", diff_files ? tmparg.mod->source_file_name : "", diff_files ? ")" : ""); if(critical) return -1; RET2RVAL(1, rvalue); } if(tmparg.mod == arg->mod) break; } return rvalue; } static int asn1f_apply_unique_index(arg_t *arg) { static int unique_index; if(!arg) { unique_index = 0; return 0; } arg->expr->_type_unique_index = ++unique_index; return 0; } /* * 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"); }