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

improved xml handling

parent cb3aabee
......@@ -12,16 +12,38 @@
.SH NAME
enber \- Convert the unber XML output back into BER
.SH SYNOPSIS
enber [\fB-\fR] \fIinfile\fR...
enber [\fB-n\fR] [\fB-\fR] \fIinfile\fR...
.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.
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
Decode the BER sequence and immediately encode it back
.Vb
\& unber \fB-p\fR \fIfilename.ber\fR | enber \fB-\fR > \fIreconstructed.ber\fR
.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
.TP
\&\fIunber\fR\|(1), \&\fIasn1c\fR\|(1)
......
......@@ -45,7 +45,7 @@
#include <ber_tlv_tag.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_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 \
"Copyright (c) 2004 Lev Walkin <vlm@lionet.info>\n"
static int no_validation; /* -n */
int
main(int ac, char **av) {
int ch; /* Command line character */
......@@ -61,15 +63,20 @@ main(int ac, char **av) {
/*
* Process command-line options.
*/
while((ch = getopt(ac, av, "hv")) != -1)
while((ch = getopt(ac, av, "nhv")) != -1)
switch(ch) {
case 'n':
no_validation++;
break;
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);
break;
case 'h':
default:
usage(av[0]);
usage(av[0], 0);
}
/*
......@@ -101,11 +108,15 @@ main(int ac, char **av) {
* Print the usage screen and exit(EX_USAGE).
*/
static void
usage(const char *av0) {
usage(const char *av0, int copyright_only) {
fprintf(stderr,
"Convert unber(1)'s output back into BER, "
"v" VERSION "\n" COPYRIGHT);
if(copyright_only) exit(0);
fprintf(stderr,
"Convertor of under(1) output back into BER, v" VERSION "\n" COPYRIGHT
"Usage: %s [-] [file ...]\n"
, av0);
"Usage: %s [-n] [-] [file ...]\n"
"Options:\n"
" -n Disable XML input validation\n", av0);
exit(EX_USAGE);
}
......@@ -184,9 +195,18 @@ process_line(const char *fname, char *line, int lineno) {
(void)fname;
/* Find a tag opening angle bracket */
for(; *line == ' '; line++);
for(; *line == ' ' || *line == '\t'; 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);
exit(EX_DATAERR);
}
......@@ -240,10 +260,10 @@ process_line(const char *fname, char *line, int lineno) {
tcl_pos = strstr(op, "T=\"[");
tl_pos = strstr(op, "TL=\"");
v_pos = strstr(op, "V=\"");
if(!tcl_pos || !tl_pos || !v_pos) {
if(!tcl_pos || !tl_pos || (!v_pos && constr != 2)) {
fprintf(stderr,
"%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);
}
errno = 0;
......@@ -368,11 +388,12 @@ process_line(const char *fname, char *line, int lineno) {
fputc(v, stdout);
}
if(len != tlv_len) {
if(no_validation) fprintf(stderr, "Warning: ");
fprintf(stderr,
"%s: Could not encode value of %d chars "
"at line %d in %d bytes\n",
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).
Use the specified number of spaces for output indentation. Default is 4 spaces.
.TP
\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
\fB\-t\fR \fIdata-string\fR
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)
[\fIF\fR]
Indicates that the value was reformatted (pretty-printed)
.P
Example:
Sample XML output:
.Vb
\& <I T="[UNIVERSAL 16]" TL="2" V="Indefinite" A="SEQUENCE">
\& <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:
.Vb
\& cat \fI...\fR | unber \fB-\fR
.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
\& cat \fI...\fR | unber \fB-p\fR \fB-\fR | enber \fB-\fR > filename.ber\fI\fR
.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
.TP
\&\fIenber\fR\|(1), \&\fIasn1c\fR\|(1)
......
......@@ -158,7 +158,7 @@ typedef enum pd_code {
PD_FINISHED = 0,
PD_EOF = 1,
} 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 int print_V(const char *fname, FILE *fp, ber_tlv_tag_t, ber_tlv_len_t);
......@@ -169,6 +169,7 @@ static int
process(const char *fname) {
FILE *fp;
pd_code_e pdc;
ssize_t decoded = 0;
if(strcmp(fname, "-")) {
fp = fopen(fname, "r");
......@@ -184,7 +185,7 @@ process(const char *fname) {
* Fetch out BER-encoded data until EOF or error.
*/
do {
pdc = process_deeper(fname, fp, 0, -1);
pdc = process_deeper(fname, fp, 0, -1, &decoded, 0);
} while(pdc == PD_FINISHED && !single_type_decoding);
if(fp != stdin)
......@@ -198,7 +199,7 @@ process(const char *fname) {
/*
* 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];
ssize_t tblen = 0;
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
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 */
assert((t_len + l_len) == tblen);
print_TL(0, level, constr, tblen, tlv_tag, tlv_len);
if(!expect_eoc || tagbuf[0] || tagbuf[1])
print_TL(0, level, constr, tblen, tlv_tag, tlv_len);
if(limit != -1) {
/* If limit is set, account for the TL sequence */
limit -= (t_len + l_len);
assert(limit >= 0);
}
if(limit != -1) {
if(tlv_len > limit) {
fprintf(stderr,
"%s: Structure advertizes length (%ld) "
"greater than of a parent container (%ld)\n",
fname, (long)tlv_len, (long)limit);
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) {
ssize_t dec = 0;
/*
* 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 */
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(limit != -1) limit -= dec;
*decoded += dec;
if(tlv_len == -1) {
tblen = 0;
continue;
}
} else {
assert(tlv_len >= 0);
if(print_V(fname, fp, tlv_tag, tlv_len))
return PD_FAILED;
limit -= tlv_len;
*decoded += 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
ber_tlv_tag_fwrite(tlv_tag, stdout);
printf("\"");
if(!fin) {
if(!fin || tlv_len == -1)
printf(" TL=\"%ld\"", (long)tlen);
if(!fin) {
if(tlv_len == -1)
printf(" V=\"Indefinite\"");
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