// SPDX-License-Identifier: GPL-2.0-or-later /* Simplified ASN.1 notation parser * * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com)
*/
#define verbose(fmt, ...) do { if (verbose_opt) printf(fmt, ## __VA_ARGS__); } while (0) #define debug(fmt, ...) do { if (debug_opt) printf(fmt, ## __VA_ARGS__); } while (0)
/* Assume we're going to have half as many tokens as we have * characters
*/
token_list = tokens = calloc((end - buffer) / 2, sizeof(struct token)); if (!tokens) {
perror(NULL); exit(1);
}
tix = 0;
lineno = 0; while (buffer < end) { /* First of all, break out a line */
lineno++;
line = buffer;
nl = memchr(line, '\n', end - buffer); if (!nl) {
buffer = nl = end;
} else {
buffer = nl + 1;
*nl = '\0';
}
/* Remove "--" comments */
p = line;
next_comment: while ((p = memchr(p, '-', nl - p))) { if (p[1] == '-') { /* Found a comment; see if there's a terminator */
q = p + 2; while ((q = memchr(q, '-', nl - q))) { if (q[1] == '-') { /* There is - excise the comment */
q += 2;
memmove(p, q, nl - q); goto next_comment;
}
q++;
}
*p = '\0';
nl = p; break;
} else {
p++;
}
}
p = line; while (p < nl) { /* Skip white space */ while (p < nl && isspace(*p))
*(p++) = 0; if (p >= nl) break;
tokens[tix].line = lineno;
start = p;
/* Handle string tokens */ if (isalpha(*p)) { constchar **dir;
/* Can be a directive, type name or element * name. Find the end of the name.
*/
q = p + 1; while (q < nl && (isalnum(*q) || *q == '-' || *q == '_'))
q++;
tokens[tix].size = q - p;
p = q;
/* If it begins with a lowercase letter then * it's an element name
*/ if (islower(tokens[tix].content[0])) {
tokens[tix++].token_type = TOKEN_ELEMENT_NAME; continue;
}
/* Otherwise we need to search the directive * table
*/
dir = bsearch(&tokens[tix], directives, sizeof(directives) / sizeof(directives[1]), sizeof(directives[1]),
directive_compare); if (dir) {
tokens[tix++].token_type = dir - directives; continue;
}
if (readlen != st.st_size) {
fprintf(stderr, "%s: Short read\n", filename); exit(1);
}
p = strrchr(argv[1], '/');
p = p ? p + 1 : argv[1];
grammar_name = strdup(p); if (!grammar_name) {
perror(NULL); exit(1);
}
p = strchr(grammar_name, '.'); if (p)
*p = '\0';
if (cursor != type[1].name) {
fprintf(stderr, "%s:%d: Parse error at token '%s'\n",
filename, cursor->line, cursor->content); exit(1);
}
} while (type++, !(type->flags & TYPE_STOP_MARKER));
verbose("Extracted %u actions\n", nr_actions);
}
staticstruct element *element_list;
staticstruct element *alloc_elem(void)
{ struct element *e = calloc(1, sizeof(*e)); if (!e) {
perror(NULL); exit(1);
}
e->list_next = element_list;
element_list = e; return e;
}
staticstruct element *parse_compound(struct token **_cursor, struct token *end, int alternates);
/* * Parse one type definition statement
*/ staticstruct element *parse_type(struct token **_cursor, struct token *end, struct token *name)
{ struct element *top, *element; struct action *action, **ppaction; struct token *cursor = *_cursor; struct type **ref; char *p; int labelled = 0, implicit = 0;
top = element = alloc_elem();
element->class = ASN1_UNIV;
element->method = ASN1_PRIM;
element->tag = token_to_tag[cursor->token_type];
element->name = name;
/* Extract the tag value if one given */ if (cursor->token_type == TOKEN_OPEN_SQUARE) {
cursor++; if (cursor >= end) goto overrun_error; switch (cursor->token_type) { case DIRECTIVE_UNIVERSAL:
element->class = ASN1_UNIV;
cursor++; break; case DIRECTIVE_APPLICATION:
element->class = ASN1_APPL;
cursor++; break; case TOKEN_NUMBER:
element->class = ASN1_CONT; break; case DIRECTIVE_PRIVATE:
element->class = ASN1_PRIV;
cursor++; break; default:
fprintf(stderr, "%s:%d: Unrecognised tag class token '%s'\n",
filename, cursor->line, cursor->content); exit(1);
}
if (cursor >= end) goto overrun_error; if (cursor->token_type != TOKEN_NUMBER) {
fprintf(stderr, "%s:%d: Missing tag number '%s'\n",
filename, cursor->line, cursor->content); exit(1);
}
/* Extract the type we're expecting here */
element->type = cursor; switch (cursor->token_type) { case DIRECTIVE_ANY:
element->compound = ANY;
cursor++; break;
case DIRECTIVE_NULL: case DIRECTIVE_BOOLEAN: case DIRECTIVE_ENUMERATED: case DIRECTIVE_INTEGER:
element->compound = NOT_COMPOUND;
cursor++; break;
case DIRECTIVE_EXTERNAL:
element->method = ASN1_CONS;
case DIRECTIVE_BMPString: case DIRECTIVE_GeneralString: case DIRECTIVE_GraphicString: case DIRECTIVE_IA5String: case DIRECTIVE_ISO646String: case DIRECTIVE_NumericString: case DIRECTIVE_PrintableString: case DIRECTIVE_T61String: case DIRECTIVE_TeletexString: case DIRECTIVE_UniversalString: case DIRECTIVE_UTF8String: case DIRECTIVE_VideotexString: case DIRECTIVE_VisibleString: case DIRECTIVE_ObjectDescriptor: case DIRECTIVE_GeneralizedTime: case DIRECTIVE_UTCTime:
element->compound = NOT_COMPOUND;
cursor++; break;
case DIRECTIVE_BIT: case DIRECTIVE_OCTET:
element->compound = NOT_COMPOUND;
cursor++; if (cursor >= end) goto overrun_error; if (cursor->token_type != DIRECTIVE_STRING) goto parse_error;
cursor++; break;
case DIRECTIVE_OBJECT:
element->compound = NOT_COMPOUND;
cursor++; if (cursor >= end) goto overrun_error; if (cursor->token_type != DIRECTIVE_IDENTIFIER) goto parse_error;
cursor++; break;
default:
fprintf(stderr, "%s:%d: Token '%s' does not introduce a type\n",
filename, cursor->line, cursor->content); exit(1);
}
/* Handle elements that are optional */ if (cursor < end && (cursor->token_type == DIRECTIVE_OPTIONAL ||
cursor->token_type == DIRECTIVE_DEFAULT)
) {
cursor++;
top->flags |= ELEMENT_SKIPPABLE;
}
if (cursor < end && cursor->token_type == TOKEN_OPEN_ACTION) {
cursor++; if (cursor >= end) goto overrun_error; if (cursor->token_type != TOKEN_ELEMENT_NAME) {
fprintf(stderr, "%s:%d: Token '%s' is not an action function name\n",
filename, cursor->line, cursor->content); exit(1);
}
/* * Render the grammar into a state machine definition.
*/ staticvoid render(FILE *out, FILE *hdr)
{ struct element *e; struct action *action; struct type *root; int index;
fprintf(hdr, "/*\n");
fprintf(hdr, " * Automatically generated by asn1_compiler. Do not edit\n");
fprintf(hdr, " *\n");
fprintf(hdr, " * ASN.1 parser for %s\n", grammar_name);
fprintf(hdr, " */\n");
fprintf(hdr, "#include \n");
fprintf(hdr, "\n");
fprintf(hdr, "extern const struct asn1_decoder %s_decoder;\n", grammar_name); if (ferror(hdr)) {
perror(headername); exit(1);
}
fprintf(out, "/*\n");
fprintf(out, " * Automatically generated by asn1_compiler. Do not edit\n");
fprintf(out, " *\n");
fprintf(out, " * ASN.1 parser for %s\n", grammar_name);
fprintf(out, " */\n");
fprintf(out, "#include \n");
fprintf(out, "#include \"%s.asn1.h\"\n", grammar_name);
fprintf(out, "\n"); if (ferror(out)) {
perror(outputname); exit(1);
}
/* Tabulate the action functions we might have to call */
fprintf(hdr, "\n");
index = 0; for (action = action_list; action; action = action->next) {
action->index = index++;
fprintf(hdr, "extern int %s(void *, size_t, unsigned char," " const void *, size_t);\n",
action->name);
}
fprintf(hdr, "\n");
/* We do two passes - the first one calculates all the offsets */
verbose("Pass 1\n");
nr_entries = 0;
root = &type_list[0];
render_element(NULL, root->element, NULL);
render_opcode(NULL, "ASN1_OP_COMPLETE,\n");
render_out_of_line_list(NULL);
for (e = element_list; e; e = e->list_next)
e->flags &= ~ELEMENT_RENDERED;
/* And then we actually render */
verbose("Pass 2\n");
fprintf(out, "\n");
fprintf(out, "static const unsigned char %s_machine[] = {\n",
grammar_name);
/* Deal with compound types */ switch (e->compound) { case TYPE_REF:
render_element(out, e->type->type->element, tag); if (e->action)
render_opcode(out, "ASN1_OP_%sACT,\n",
skippable ? "MAYBE_" : ""); break;
case SEQUENCE: if (outofline) { /* Render out-of-line for multiple use or
* skipability */
render_opcode(out, "_jump_target(%u),", e->entry_index); if (e->type_def && e->type_def->name)
render_more(out, "\t\t// --> %s",
e->type_def->name->content);
render_more(out, "\n"); if (!(e->flags & ELEMENT_RENDERED)) {
e->flags |= ELEMENT_RENDERED;
*render_list_p = e;
render_list_p = &e->render_next;
} return;
} else { /* Render inline for single use */
render_depth++; for (ec = e->children; ec; ec = ec->next)
render_element(out, ec, NULL);
render_depth--;
render_opcode(out, "ASN1_OP_END_SEQ%s,\n", act);
} break;
case SEQUENCE_OF: case SET_OF: if (outofline) { /* Render out-of-line for multiple use or
* skipability */
render_opcode(out, "_jump_target(%u),", e->entry_index); if (e->type_def && e->type_def->name)
render_more(out, "\t\t// --> %s",
e->type_def->name->content);
render_more(out, "\n"); if (!(e->flags & ELEMENT_RENDERED)) {
e->flags |= ELEMENT_RENDERED;
*render_list_p = e;
render_list_p = &e->render_next;
} return;
} else { /* Render inline for single use */
entry = nr_entries;
render_depth++;
render_element(out, e->children, NULL);
render_depth--; if (e->compound == SEQUENCE_OF)
render_opcode(out, "ASN1_OP_END_SEQ_OF%s,\n", act); else
render_opcode(out, "ASN1_OP_END_SET_OF%s,\n", act);
render_opcode(out, "_jump_target(%u),\n", entry);
} break;
case SET: /* I can't think of a nice way to do SET support without having * a stack of bitmasks to make sure no element is repeated. * The bitmask has also to be checked that no non-optional * elements are left out whilst not preventing optional * elements from being left out.
*/
fprintf(stderr, "The ASN.1 SET type is not currently supported.\n"); exit(1);
case CHOICE: for (ec = e->children; ec; ec = ec->next)
render_element(out, ec, ec); if (!skippable)
render_opcode(out, "ASN1_OP_COND_FAIL,\n"); if (e->action)
render_opcode(out, "ASN1_OP_ACT,\n"); break;
default: break;
}
if (e->action)
render_opcode(out, "_action(ACT_%s),\n", e->action->name);
}
¤ Dauer der Verarbeitung: 0.25 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.