mirror of https://gerrit.osmocom.org/asn1c
355 lines
7.1 KiB
C
355 lines
7.1 KiB
C
#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");
|
|
}
|