Commit 0cb0fc52 authored by Lev Walkin's avatar Lev Walkin

improved xml handling

parent cb3aabee
...@@ -12,16 +12,38 @@ ...@@ -12,16 +12,38 @@
.SH NAME .SH NAME
enber \- Convert the unber XML output back into BER enber \- Convert the unber XML output back into BER
.SH SYNOPSIS .SH SYNOPSIS
enber [\fB-\fR] \fIinfile\fR... enber [\fB-n\fR] [\fB-\fR] \fIinfile\fR...
.SH DESCRIPTION .SH DESCRIPTION
enber takes the XML-encoded files produced by \fIunber\fR\|(1) and converts enber takes the XML-formatted files produced by \fIunber\fR\|(1) and converts
them back into the BER format. them back into the BER format.
A single dash represents the standard input. A single dash represents the standard input.
.SH OPTIONS
.TP
\fB\-n\fR
Disable input validation. By default, enber performs some basic validity checks
against the XML input. This option disables such checks.
.SH EXAMPLES .SH EXAMPLES
Decode the BER sequence and immediately encode it back Decode the BER sequence and immediately encode it back
.Vb .Vb
\& unber \fB-p\fR \fIfilename.ber\fR | enber \fB-\fR > \fIreconstructed.ber\fR \& unber \fB-p\fR \fIfilename.ber\fR | enber \fB-\fR > \fIreconstructed.ber\fR
.Ve .Ve
.SH FOOTNOTES
This program does not attempt to perform full-fledged XML parsing. It is merely
compatible with \fIunber\fR\|(1)'s output.
In particular, the XML comments are not supported.
The following characters after the initial white space indicate
the start of a comment: "\fB#\fR" (a hash) and "\fB--\fR" (two dashes).
The whole comment line is ignored completely. Empty lines are ignored as well.
.P
The following example demostrates the use of comments:
.Vb
\& <C T="[1]" TL="2" V="2">
\& <I T="[1]" TL="2" V="Indefinite">
\& -- </I>
\& # Do not terminate:
\& # the absence of end-of-content octets is intentional!
\& </C>
.Ve
.SH SEE ALSO .SH SEE ALSO
.TP .TP
\&\fIunber\fR\|(1), \&\fIasn1c\fR\|(1) \&\fIunber\fR\|(1), \&\fIasn1c\fR\|(1)
......
...@@ -45,7 +45,7 @@ ...@@ -45,7 +45,7 @@
#include <ber_tlv_tag.c> #include <ber_tlv_tag.c>
#include <ber_tlv_length.c> #include <ber_tlv_length.c>
static void usage(const char *av0); /* Print the Usage screen and exit */ static void usage(const char *av0, int);/* Print the Usage screen and exit */
static int process(const char *fname); /* Perform the BER decoding */ static int process(const char *fname); /* Perform the BER decoding */
static int process_line(const char *fname, char *line, int lineno); static int process_line(const char *fname, char *line, int lineno);
...@@ -53,6 +53,8 @@ static int process_line(const char *fname, char *line, int lineno); ...@@ -53,6 +53,8 @@ static int process_line(const char *fname, char *line, int lineno);
#define COPYRIGHT \ #define COPYRIGHT \
"Copyright (c) 2004 Lev Walkin <vlm@lionet.info>\n" "Copyright (c) 2004 Lev Walkin <vlm@lionet.info>\n"
static int no_validation; /* -n */
int int
main(int ac, char **av) { main(int ac, char **av) {
int ch; /* Command line character */ int ch; /* Command line character */
...@@ -61,15 +63,20 @@ main(int ac, char **av) { ...@@ -61,15 +63,20 @@ main(int ac, char **av) {
/* /*
* Process command-line options. * Process command-line options.
*/ */
while((ch = getopt(ac, av, "hv")) != -1) while((ch = getopt(ac, av, "nhv")) != -1)
switch(ch) { switch(ch) {
case 'n':
no_validation++;
break;
case 'v': case 'v':
fprintf(stderr, "ASN.1 BER Decoder, v" VERSION "\n" COPYRIGHT); usage(av[0], 1);
fprintf(stderr, "Convert unber(1)'s output back into BER, "
"v" VERSION "\n" COPYRIGHT);
exit(0); exit(0);
break; break;
case 'h': case 'h':
default: default:
usage(av[0]); usage(av[0], 0);
} }
/* /*
...@@ -101,11 +108,15 @@ main(int ac, char **av) { ...@@ -101,11 +108,15 @@ main(int ac, char **av) {
* Print the usage screen and exit(EX_USAGE). * Print the usage screen and exit(EX_USAGE).
*/ */
static void static void
usage(const char *av0) { usage(const char *av0, int copyright_only) {
fprintf(stderr, fprintf(stderr,
"Convertor of under(1) output back into BER, v" VERSION "\n" COPYRIGHT "Convert unber(1)'s output back into BER, "
"Usage: %s [-] [file ...]\n" "v" VERSION "\n" COPYRIGHT);
, av0); if(copyright_only) exit(0);
fprintf(stderr,
"Usage: %s [-n] [-] [file ...]\n"
"Options:\n"
" -n Disable XML input validation\n", av0);
exit(EX_USAGE); exit(EX_USAGE);
} }
...@@ -184,9 +195,18 @@ process_line(const char *fname, char *line, int lineno) { ...@@ -184,9 +195,18 @@ process_line(const char *fname, char *line, int lineno) {
(void)fname; (void)fname;
/* Find a tag opening angle bracket */ /* Find a tag opening angle bracket */
for(; *line == ' '; line++); for(; *line == ' ' || *line == '\t'; line++);
op = line; op = line;
if(*op != '<') { switch(*op) {
case '<': /* That's what we want! A tag opening */
break;
case '-': /* This is a comment (dash-dash) */
if(op[1] == *op)
case '\r':
case '\n':
case '#': /* This is a comment */
return 0;
default:
fprintf(stderr, "%s: Missing '<' after whitespace\n", fname); fprintf(stderr, "%s: Missing '<' after whitespace\n", fname);
exit(EX_DATAERR); exit(EX_DATAERR);
} }
...@@ -240,10 +260,10 @@ process_line(const char *fname, char *line, int lineno) { ...@@ -240,10 +260,10 @@ process_line(const char *fname, char *line, int lineno) {
tcl_pos = strstr(op, "T=\"["); tcl_pos = strstr(op, "T=\"[");
tl_pos = strstr(op, "TL=\""); tl_pos = strstr(op, "TL=\"");
v_pos = strstr(op, "V=\""); v_pos = strstr(op, "V=\"");
if(!tcl_pos || !tl_pos || !v_pos) { if(!tcl_pos || !tl_pos || (!v_pos && constr != 2)) {
fprintf(stderr, fprintf(stderr,
"%s: Mandatory attribute %s is not found at line %d\n", "%s: Mandatory attribute %s is not found at line %d\n",
fname, (!tcl_pos)?"T":(v_pos?"V":"TCL"), lineno); fname, (!tcl_pos)?"T":((!v_pos)?"V":"TL"), lineno);
exit(EX_DATAERR); exit(EX_DATAERR);
} }
errno = 0; errno = 0;
...@@ -368,11 +388,12 @@ process_line(const char *fname, char *line, int lineno) { ...@@ -368,11 +388,12 @@ process_line(const char *fname, char *line, int lineno) {
fputc(v, stdout); fputc(v, stdout);
} }
if(len != tlv_len) { if(len != tlv_len) {
if(no_validation) fprintf(stderr, "Warning: ");
fprintf(stderr, fprintf(stderr,
"%s: Could not encode value of %d chars " "%s: Could not encode value of %d chars "
"at line %d in %d bytes\n", "at line %d in %d bytes\n",
fname, len, lineno, tlv_len); fname, len, lineno, tlv_len);
exit(EX_DATAERR); if(!no_validation) exit(EX_DATAERR);
} }
} }
......
...@@ -29,7 +29,8 @@ By default, unber continues decoding until the end of file (input stream). ...@@ -29,7 +29,8 @@ By default, unber continues decoding until the end of file (input stream).
Use the specified number of spaces for output indentation. Default is 4 spaces. Use the specified number of spaces for output indentation. Default is 4 spaces.
.TP .TP
\fB\-p\fR \fB\-p\fR
Do \fInot\fR attempt pretty-printing of known ASN.1 types (strings, INTEGER, BOOLEAN, etc). Do \fInot\fR attempt pretty-printing of known ASN.1 types (OBJECT IDENTIFIER, INTEGER, BOOLEAN, etc). By default, some ASN.1 types are converted into
the text representation. This option is required for \&\fIenber\fR\|(1).
.TP .TP
\fB\-t\fR \fIdata-string\fR \fB\-t\fR \fIdata-string\fR
Interpret the data-string as a sequence of hexadecimal values representing Interpret the data-string as a sequence of hexadecimal values representing
...@@ -62,7 +63,7 @@ Likely name of the underlying ASN.1 type (for UNIVERSAL tags) ...@@ -62,7 +63,7 @@ Likely name of the underlying ASN.1 type (for UNIVERSAL tags)
[\fIF\fR] [\fIF\fR]
Indicates that the value was reformatted (pretty-printed) Indicates that the value was reformatted (pretty-printed)
.P .P
Example: Sample XML output:
.Vb .Vb
\& <I T="[UNIVERSAL 16]" TL="2" V="Indefinite" A="SEQUENCE"> \& <I T="[UNIVERSAL 16]" TL="2" V="Indefinite" A="SEQUENCE">
\& <P T="[UNIVERSAL 19]" TL="2" V="2" A="PrintableString">US</P> \& <P T="[UNIVERSAL 19]" TL="2" V="2" A="PrintableString">US</P>
...@@ -84,10 +85,19 @@ Decode the binary stream taken from the standard input: ...@@ -84,10 +85,19 @@ Decode the binary stream taken from the standard input:
.Vb .Vb
\& cat \fI...\fR | unber \fB-\fR \& cat \fI...\fR | unber \fB-\fR
.Ve .Ve
Decode the binary stream into the same stream (see \fIenber\fR\|(1)): Decode the binary stream into the same stream (see \&\fIenber\fR\|(1)):
.Vb .Vb
\& cat \fI...\fR | unber \fB-p\fR \fB-\fR | enber \fB-\fR > filename.ber\fI\fR \& cat \fI...\fR | unber \fB-p\fR \fB-\fR | enber \fB-\fR > filename.ber\fI\fR
.Ve .Ve
.SH FOOTNOTES
The constructed XML output is not necessarily well-formed.
.P
When indefinite length encoding is being used, the BER sequence, which is not
terminated with the end-of-content octets, will cause the terminating \fB</I>\fR
XML tag to disappear.
Thus, the invalid BER framing directly causes invalid XML output.
.P
The \&\fIenber\fR\|(1) utility understands this convention correctly.
.SH SEE ALSO .SH SEE ALSO
.TP .TP
\&\fIenber\fR\|(1), \&\fIasn1c\fR\|(1) \&\fIenber\fR\|(1), \&\fIasn1c\fR\|(1)
......
...@@ -158,7 +158,7 @@ typedef enum pd_code { ...@@ -158,7 +158,7 @@ typedef enum pd_code {
PD_FINISHED = 0, PD_FINISHED = 0,
PD_EOF = 1, PD_EOF = 1,
} pd_code_e; } pd_code_e;
static pd_code_e process_deeper(const char *fname, FILE *fp, int level, ssize_t limit); static pd_code_e process_deeper(const char *fname, FILE *fp, int level, ssize_t limit, ssize_t *decoded, int expect_eoc);
static void print_TL(int fin, int level, int constr, ssize_t tlen, ber_tlv_tag_t, ber_tlv_len_t); static void print_TL(int fin, int level, int constr, ssize_t tlen, ber_tlv_tag_t, ber_tlv_len_t);
static int print_V(const char *fname, FILE *fp, ber_tlv_tag_t, ber_tlv_len_t); static int print_V(const char *fname, FILE *fp, ber_tlv_tag_t, ber_tlv_len_t);
...@@ -169,6 +169,7 @@ static int ...@@ -169,6 +169,7 @@ static int
process(const char *fname) { process(const char *fname) {
FILE *fp; FILE *fp;
pd_code_e pdc; pd_code_e pdc;
ssize_t decoded = 0;
if(strcmp(fname, "-")) { if(strcmp(fname, "-")) {
fp = fopen(fname, "r"); fp = fopen(fname, "r");
...@@ -184,7 +185,7 @@ process(const char *fname) { ...@@ -184,7 +185,7 @@ process(const char *fname) {
* Fetch out BER-encoded data until EOF or error. * Fetch out BER-encoded data until EOF or error.
*/ */
do { do {
pdc = process_deeper(fname, fp, 0, -1); pdc = process_deeper(fname, fp, 0, -1, &decoded, 0);
} while(pdc == PD_FINISHED && !single_type_decoding); } while(pdc == PD_FINISHED && !single_type_decoding);
if(fp != stdin) if(fp != stdin)
...@@ -198,7 +199,7 @@ process(const char *fname) { ...@@ -198,7 +199,7 @@ process(const char *fname) {
/* /*
* Process the TLV recursively. * Process the TLV recursively.
*/ */
static pd_code_e process_deeper(const char *fname, FILE *fp, int level, ssize_t limit) { static pd_code_e process_deeper(const char *fname, FILE *fp, int level, ssize_t limit, ssize_t *decoded, int expect_eoc) {
unsigned char tagbuf[32]; unsigned char tagbuf[32];
ssize_t tblen = 0; ssize_t tblen = 0;
pd_code_e pdc = PD_FINISHED; pd_code_e pdc = PD_FINISHED;
...@@ -266,52 +267,57 @@ static pd_code_e process_deeper(const char *fname, FILE *fp, int level, ssize_t ...@@ -266,52 +267,57 @@ static pd_code_e process_deeper(const char *fname, FILE *fp, int level, ssize_t
continue; continue;
} }
if(tagbuf[0] == '\0' && tagbuf[1] == '\0') {
/* End of content octets */
return PD_FINISHED;
}
/* Make sure the T & L decoders took exactly the whole buffer */ /* Make sure the T & L decoders took exactly the whole buffer */
assert((t_len + l_len) == tblen); assert((t_len + l_len) == tblen);
if(!expect_eoc || tagbuf[0] || tagbuf[1])
print_TL(0, level, constr, tblen, tlv_tag, tlv_len); print_TL(0, level, constr, tblen, tlv_tag, tlv_len);
if(limit != -1) { if(limit != -1) {
/* If limit is set, account for the TL sequence */ /* If limit is set, account for the TL sequence */
limit -= (t_len + l_len); limit -= (t_len + l_len);
assert(limit >= 0); assert(limit >= 0);
}
if(limit != -1) {
if(tlv_len > limit) { if(tlv_len > limit) {
fprintf(stderr, fprintf(stderr,
"%s: Structure advertizes length (%ld) " "%s: Structure advertizes length (%ld) "
"greater than of a parent container (%ld)\n", "greater than of a parent container (%ld)\n",
fname, (long)tlv_len, (long)limit); fname, (long)tlv_len, (long)limit);
return PD_FAILED; return PD_FAILED;
} else if(tlv_len != -1) {
/* Account for the V */
limit -= tlv_len;
} }
} }
*decoded += t_len + l_len;
if(expect_eoc && tagbuf[0] == '\0' && tagbuf[1] == '\0') {
/* End of content octets */
print_TL(1, level - 1, 1, 2, 0, -1);
return PD_FINISHED;
}
if(constr) { if(constr) {
ssize_t dec = 0;
/* /*
* This is a constructed type. Process recursively. * This is a constructed type. Process recursively.
*/ */
/* Get the new subframe limit from the structure tags */
if(tlv_len == -1)
tlv_len = limit;
printf(">\n"); /* Close the opening tag */ printf(">\n"); /* Close the opening tag */
pdc = process_deeper(fname, fp, level + 1, tlv_len); pdc = process_deeper(fname, fp, level + 1,
tlv_len == -1 ? limit : tlv_len,
&dec, tlv_len == -1);
if(pdc == PD_FAILED) return pdc; if(pdc == PD_FAILED) return pdc;
if(limit != -1) limit -= dec;
*decoded += dec;
if(tlv_len == -1) {
tblen = 0;
continue;
}
} else { } else {
assert(tlv_len >= 0); assert(tlv_len >= 0);
if(print_V(fname, fp, tlv_tag, tlv_len)) if(print_V(fname, fp, tlv_tag, tlv_len))
return PD_FAILED; return PD_FAILED;
limit -= tlv_len;
*decoded += tlv_len;
} }
print_TL(1, level, constr, tblen, tlv_tag, tlv_len); print_TL(1, level, constr, tblen, tlv_tag, tlv_len);
...@@ -339,9 +345,9 @@ print_TL(int fin, int level, int constr, ssize_t tlen, ber_tlv_tag_t tlv_tag, be ...@@ -339,9 +345,9 @@ print_TL(int fin, int level, int constr, ssize_t tlen, ber_tlv_tag_t tlv_tag, be
ber_tlv_tag_fwrite(tlv_tag, stdout); ber_tlv_tag_fwrite(tlv_tag, stdout);
printf("\""); printf("\"");
if(!fin) { if(!fin || tlv_len == -1)
printf(" TL=\"%ld\"", (long)tlen); printf(" TL=\"%ld\"", (long)tlen);
if(!fin) {
if(tlv_len == -1) if(tlv_len == -1)
printf(" V=\"Indefinite\""); printf(" V=\"Indefinite\"");
else else
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment