#include #include #include "asn1fix_internal.h" #include "asn1fix_cws.h" static int _asn1f_parse_class_object_data(arg_t *, asn1p_expr_t *eclass, struct asn1p_ioc_row_s *row, asn1p_wsyntx_t *syntax, const uint8_t *buf, const uint8_t *bend, int optional_mode, const uint8_t **newpos, int counter); static int _asn1f_assign_cell_value(arg_t *arg, struct asn1p_ioc_cell_s *cell, const uint8_t *buf, const uint8_t *bend, int counter); static asn1p_wsyntx_chunk_t *asn1f_next_literal_chunk(asn1p_wsyntx_t *syntax, asn1p_wsyntx_chunk_t *chunk, const uint8_t *buf); int asn1f_check_class_object(arg_t *arg) { asn1p_expr_t *expr = arg->expr; asn1p_expr_t *eclass; asn1p_ioc_row_t *row; int ret; if(expr->meta_type != AMT_VALUESET || expr->expr_type != A1TC_REFERENCE || !expr->value || expr->value->type != ATV_UNPARSED) { return 0; } eclass = asn1f_find_terminal_type(arg, expr); if(!eclass || eclass->meta_type != AMT_OBJECTCLASS || eclass->expr_type != A1TC_CLASSDEF) { return 0; } if(!eclass->with_syntax) { DEBUG("Can't process classes without %s just yet", "WITH SYNTAX"); return 0; } row = asn1p_ioc_row_new(eclass); assert(row); ret = _asn1f_parse_class_object_data(arg, eclass, row, eclass->with_syntax, expr->value->value.string.buf + 1, expr->value->value.string.buf + expr->value->value.string.size - 1, 0, 0, 0); asn1p_ioc_row_delete(row); return ret; } static int _asn1f_is_ioc_row_duplicate(asn1p_ioc_table_t *it, asn1p_ioc_row_t *row) { if(!it) { return 0; } for(size_t i = 0; i < it->rows; i++) { switch(asn1p_ioc_row_match(it->row[i], row)) { default: case -1: return -1; case 1: continue; case 0: return 1; /* Duplicate! */ } } return 0; } struct parse_object_key { arg_t *arg; asn1p_expr_t *expr; /* InformationObjectSet */ asn1p_expr_t *eclass; /* CLASS */ int sequence; /* Sequence counter */ }; /* * Add to the IoC table if the row is unique. */ static int _asn1f_add_unique_row(arg_t *arg, asn1p_expr_t *expr, asn1p_ioc_row_t *row) { assert(expr->ioc_table); /* Look for duplicates */ switch(_asn1f_is_ioc_row_duplicate(expr->ioc_table, row)) { case -1: DEBUG("Found Information Object Duplicate in %s", expr->Identifier, expr->_lineno); return -1; case 0: /* Not a duplicate */ break; case 1: /* Proper duplicate detected; ignore */ asn1p_ioc_row_delete(row); return 0; } asn1p_ioc_table_add(expr->ioc_table, row); return 0; } /* * Given a single blob of unparsed Information Object specification, parse it * into a given InformationObjectSet. */ static int _asn1f_parse_object_cb(const uint8_t *buf, size_t size, void *keyp) { struct parse_object_key *key = keyp; arg_t *arg = key->arg; asn1p_expr_t *expr = key->expr; asn1p_expr_t *eclass = key->eclass; asn1p_ioc_row_t *row; int ret; key->sequence++; row = asn1p_ioc_row_new(eclass); assert(row); ret = _asn1f_parse_class_object_data(arg, eclass, row, eclass->with_syntax, buf, buf + size, 0, 0, key->sequence); if(ret) { LOG(ret, "Cannot parse %s of CLASS %s found at line %d", expr->Identifier, eclass->Identifier, expr->_lineno); asn1p_ioc_row_delete(row); return ret; } /* Add object to a CLASS. */ if(_asn1f_add_unique_row(arg, eclass, row) != 0) return -1; /* * Add a copy of the object to the Information Object Set. */ row = asn1p_ioc_row_new(eclass); assert(row); ret = _asn1f_parse_class_object_data(arg, eclass, row, eclass->with_syntax, buf, buf + size, 0, 0, key->sequence); assert(ret == 0); if(_asn1f_add_unique_row(arg, expr, row) != 0) return -1; return 0; } static int _asn1f_foreach_unparsed_union(const asn1p_constraint_t *ct_union, int (*process)(const uint8_t *buf, size_t size, void *key), void *key) { assert(ct_union->type == ACT_CA_UNI); for(size_t j = 0; j < ct_union->el_count; j++) { const asn1p_constraint_t *ct2 = ct_union->elements[j]; if(ct2->type == ACT_EL_VALUE && ct2->value->type == ATV_UNPARSED) { if(process && process(ct2->value->value.string.buf + 1, ct2->value->value.string.size - 2, key) != 0) { return -1; } continue; } return -1; } return 0; } static int _asn1f_foreach_unparsed(arg_t *arg, const asn1p_constraint_t *ct, int (*process)(const uint8_t *buf, size_t size, void *key), void *keyp) { struct parse_object_key *key = keyp; if(!ct) return -1; switch(ct->type) { default: DEBUG("Constraint is of unknown type %d for CWS", ct->type); return -1; case ACT_EL_EXT: /* ... */ if(key) { key->expr->ioc_table->extensible = 1; } return 0; case ACT_CA_UNI: /* | */ return _asn1f_foreach_unparsed_union(ct, process, keyp); case ACT_CA_CSV: /* , */ break; case ACT_EL_VALUE: return 0; } for(size_t i = 0; i < ct->el_count; i++) { const asn1p_constraint_t *ct1 = ct->elements[i]; if(_asn1f_foreach_unparsed(arg, ct1, process, keyp) != 0) { return -1; } } return 0; } static int _asn1f_constraint_looks_like_object_set(arg_t *arg, const asn1p_constraint_t *ct) { return 0 == _asn1f_foreach_unparsed(arg, ct, NULL, NULL); } int asn1f_parse_class_object(arg_t *arg) { asn1p_expr_t *expr = arg->expr; asn1p_expr_t *eclass; enum { FROM_VALUE, FROM_CONSTRAINT, } source = FROM_VALUE; if(expr->meta_type == AMT_VALUE && expr->expr_type == A1TC_REFERENCE && expr->value && expr->value->type == ATV_UNPARSED) { source = FROM_VALUE; } else if(expr->meta_type != AMT_VALUESET || expr->expr_type != A1TC_REFERENCE) { return 0; } else if(expr->value && expr->value->type == ATV_UNPARSED) { source = FROM_VALUE; } else if(!expr->value) { if(_asn1f_constraint_looks_like_object_set(arg, expr->constraints)) { source = FROM_CONSTRAINT; } else { return 0; } } else { return 0; } /* * Find the governing class. */ eclass = asn1f_find_terminal_type(arg, expr); if(!eclass || eclass->meta_type != AMT_OBJECTCLASS || eclass->expr_type != A1TC_CLASSDEF) { return 0; } DEBUG("Value %s of CLASS %s found at line %d", expr->Identifier, eclass->Identifier, expr->_lineno); if(!eclass->with_syntax) { DEBUG("Can't process classes without %s just yet", "WITH SYNTAX"); return 0; } struct parse_object_key key = { .arg = arg, .expr = expr, .eclass = eclass, .sequence = 0 }; if(!expr->ioc_table) { expr->ioc_table = asn1p_ioc_table_new(); } if(!eclass->ioc_table) { eclass->ioc_table = asn1p_ioc_table_new(); } switch(source) { case FROM_VALUE: if(_asn1f_parse_object_cb(expr->value->value.string.buf + 1, expr->value->value.string.size - 2, &key) != 0) { return -1; } break; case FROM_CONSTRAINT: if(_asn1f_foreach_unparsed(arg, expr->constraints, _asn1f_parse_object_cb, &key) != 0) { return -1; } } return 0; } #define SKIPSPACES for(; buf < bend && isspace(*buf); buf++) static int _asn1f_parse_class_object_data(arg_t *arg, asn1p_expr_t *eclass, struct asn1p_ioc_row_s *row, asn1p_wsyntx_t *syntax, const uint8_t *buf, const uint8_t *bend, int optional_mode, const uint8_t **newpos, int counter) { struct asn1p_wsyntx_chunk_s *chunk; int ret; TQ_FOR(chunk, (&syntax->chunks), next) { switch(chunk->type) { case WC_LITERAL: { int token_len = strlen(chunk->content.token); SKIPSPACES; if(bend - buf < token_len || memcmp(buf, chunk->content.token, token_len)) { if(!optional_mode) { FATAL("While parsing object class value %s at line %d: Expected: \"%s\", found: \"%s\"", arg->expr->Identifier, arg->expr->_lineno, chunk->content.token, buf); } if(newpos) *newpos = buf; return -1; } buf += token_len; } break; case WC_WHITESPACE: break; /* Ignore whitespace */ case WC_FIELD: { struct asn1p_ioc_cell_s *cell; asn1p_wsyntx_chunk_t *next_literal; const uint8_t *buf_old = buf; const uint8_t *p = 0; SKIPSPACES; next_literal = asn1f_next_literal_chunk(syntax, chunk, buf); if(!next_literal) { p += (bend - p); } else { p = (uint8_t *)strstr((const char *)buf, (const char *)next_literal->content.token); if(!p) { if (!optional_mode) FATAL("Next literal \"%s\" not found !", next_literal->content.token); if(newpos) *newpos = buf_old; return -1; } } cell = asn1p_ioc_row_cell_fetch(row, chunk->content.token); if(cell == NULL) { FATAL("Field reference %s found in WITH SYNAX {} clause does not match actual field in Object Class %s", chunk->content.token, eclass->Identifier, eclass->_lineno); if(newpos) *newpos = buf; return -1; } DEBUG("Reference %s satisfied by %s (%d)", chunk->content.token, buf, p - buf); ret = _asn1f_assign_cell_value(arg, cell, buf, p, counter); if(ret) return ret; buf = p; if(newpos) *newpos = buf; } break; case WC_OPTIONALGROUP: { const uint8_t *np = 0; SKIPSPACES; ret = _asn1f_parse_class_object_data(arg, eclass, row, chunk->content.syntax, buf, bend, 1, &np, counter); if(newpos) *newpos = np; if(ret && np != buf) return ret; buf = np; } break; } } if(newpos) *newpos = buf; return 0; } static int _asn1f_assign_cell_value(arg_t *arg, struct asn1p_ioc_cell_s *cell, const uint8_t *buf, const uint8_t *bend, int counter) { asn1p_expr_t *expr = (asn1p_expr_t *)NULL; char *mivr; /* Most Immediate Value Representation */ int new_ref = 1; asn1p_t *asn; asn1p_expr_t *type_expr = (asn1p_expr_t *)NULL; int i, ret = 0, psize; char *pp; if((bend - buf) <= 0) { FATAL("Assignment warning: empty string is being assigned into %s for %s at line %d", cell->field->Identifier, arg->expr->Identifier, arg->expr->_lineno); return -1; } mivr = malloc(bend - buf + 1); assert(mivr); memcpy(mivr, buf, bend - buf); mivr[bend - buf] = '\0'; /* remove trailing space */ for (i = bend - buf - 1; (i > 0) && isspace(mivr[i]); i--) mivr[i] = '\0'; /* This value 100 should be larger than following formatting string */ psize = bend - buf + 100; pp = calloc(1, psize); if(pp == NULL) { free(mivr); return -1; } if(cell->field->expr_type == A1TC_CLASSFIELD_TFS) { ret = snprintf(pp, psize, "M DEFINITIONS ::=\nBEGIN\n" "V ::= %s\n" "END\n", mivr ); } else if(cell->field->expr_type == A1TC_CLASSFIELD_FTVFS) { type_expr = TQ_FIRST(&(cell->field->members)); ret = snprintf(pp, psize, "M DEFINITIONS ::=\nBEGIN\n" "v %s ::= %s\n" "END\n", type_expr->reference ? type_expr->reference->components[0].name : _asn1p_expr_type2string(type_expr->expr_type), mivr ); } else { WARNING("asn1c only be able to parse TypeFieldSpec and FixedTypeValueFieldSpec. Failed when parsing %s at line %d\n", mivr, arg->expr->_lineno); free(mivr); free(pp); return -1; } DEBUG("ASN.1:\n\n%s\n", pp); assert(ret < psize); psize = ret; asn = asn1p_parse_buffer(pp, psize, arg->expr->module->source_file_name, arg->expr->_lineno, A1P_NOFLAGS); free(pp); if(asn == NULL) { FATAL("Cannot parse Setting token %s " "at line %d", mivr, arg->expr->_lineno ); free(mivr); return -1; } else { asn1p_module_t *mod = TQ_FIRST(&(asn->modules)); assert(mod); /* This member removal is safe with respect to members hash since the * entire asn module will be deleted down below. */ expr = TQ_REMOVE(&(mod->members), next); assert(expr); expr->parent_expr = NULL; asn1p_expr_set_source(expr, arg->expr->module, arg->expr->_lineno); expr->_type_unique_index = counter; DEBUG("Parsed identifier %s, mivr [%s], reference [%s] value [%s]", expr->Identifier, mivr, asn1p_ref_string(expr->reference), asn1f_printable_value(expr->value)); free(expr->Identifier); if(expr->value) { expr->Identifier = strdup(asn1f_printable_value(expr->value)); } else if (expr->reference) { expr->Identifier = strdup(expr->reference->components[expr->reference->comp_count - 1].name); } else { expr->Identifier = mivr; } asn1p_delete(asn); } if(expr->reference && !asn1f_lookup_symbol(arg, expr->rhs_pspecs, expr->reference)) { asn1p_ref_free(expr->reference); new_ref = 0; expr->reference = type_expr->reference; if (asn1f_value_resolve(arg, expr, 0)) { expr->reference = 0; asn1p_expr_free(expr); FATAL("Cannot find %s referenced by %s at line %d", mivr, arg->expr->Identifier, arg->expr->_lineno); free(mivr); return -1; } } DEBUG("Field %s assignment of %s got %s", cell->field->Identifier, mivr, expr->Identifier); cell->value = expr; cell->new_ref = new_ref; if(expr->Identifier != mivr) { free(mivr); } return 0; } static asn1p_wsyntx_chunk_t * asn1f_next_literal_chunk(asn1p_wsyntx_t *syntax, asn1p_wsyntx_chunk_t *chunk, const uint8_t *buf) { asn1p_wsyntx_chunk_t *next_chunk; next_chunk = TQ_NEXT(chunk, next); do { if(next_chunk == (struct asn1p_wsyntx_chunk_s *)0) { if(!syntax->parent) break; next_chunk = TQ_NEXT(syntax->parent, next); } else if(next_chunk->type == WC_LITERAL) { if(strstr((const char *)buf, (char *)next_chunk->content.token)) break; if(!syntax->parent) break; next_chunk = TQ_NEXT(syntax->parent, next); } else if(next_chunk->type == WC_WHITESPACE) { next_chunk = TQ_NEXT(next_chunk, next); } else if(next_chunk->type == WC_OPTIONALGROUP) { syntax = next_chunk->content.syntax; next_chunk = TQ_FIRST(((&next_chunk->content.syntax->chunks))); } else break; } while (next_chunk); return next_chunk; }