2 * Copyright (c) 2004, 2005 Lev Walkin <vlm@lionet.info>. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 #include "sys-common.h"
29 #include <asn1parser.h> /* For static string tables */
31 #include <asn_application.h>
32 #include <constraints.c>
33 #include <ber_tlv_tag.c>
34 #include <ber_tlv_length.c>
37 #define COPYRIGHT "Copyright (c) 2004, 2005 Lev Walkin <vlm@lionet.info>\n"
39 static void usage(const char *av0, int); /* Print the Usage screen and exit */
40 static int process(const char *fname); /* Perform the BER decoding */
41 static int process_line(const char *fname, char *line, int lineno);
43 static int no_validation; /* -n */
46 main(int ac, char **av) {
47 int ch; /* Command line character */
48 int i; /* Index in some loops */
51 * Process command-line options.
53 while((ch = getopt(ac, av, "nhv")) != -1) switch(ch) {
66 * Ensure that there are some input files present.
72 fprintf(stderr, "%s: No input files specified\n", av[0]);
76 setvbuf(stdout, 0, _IOLBF, 0);
79 * Iterate over input files and parse each.
80 * All syntax trees from all files will be bundled together.
82 for(i = 0; i < ac; i++) {
83 if(process(av[i])) exit(EX_DATAERR);
90 * Print the usage screen and exit(EX_USAGE).
93 usage(const char *av0, int copyright_only) {
95 "Convert unber(1)'s output back into BER, "
96 "v" VERSION "\n" COPYRIGHT);
97 if(copyright_only) exit(0);
99 "Usage: %s [-n] [-] [file ...]\n"
101 " -n Disable XML input validation\n",
107 * Open the file and initiate recursive processing.
110 process(const char *fname) {
113 size_t collector_size = sizeof(buf);
114 size_t collector_offset = 0;
118 if(strcmp(fname, "-")) {
119 fp = fopen(fname, "r");
129 while(fgets(buf, sizeof(buf), fp) || !feof(fp)) {
130 size_t len = strlen(buf);
133 if(collector_offset || buf[len - 1] != '\n') {
134 if((collector_size - collector_offset) <= len || !collector) {
135 collector_size <<= 1;
136 collector = REALLOC(collector, collector_size);
142 memcpy(collector + collector_offset, buf, len + 1);
143 collector_offset += len;
145 if(buf[len - 1] != '\n') continue;
147 if(collector_offset) {
148 assert(collector[collector_offset - 1] == '\n');
149 process_line(fname, collector, ++lineno);
150 collector_offset = 0;
152 process_line(fname, buf, ++lineno);
156 if(fp != stdin) fclose(fp);
162 process_line(const char *fname, char *line, int lineno) {
166 char *tcl_pos; /* tag class (T=") position */
167 char *tl_pos; /* tag length (TL=") position */
168 char *v_pos; /* value length (V=") position */
170 ber_tlv_tag_t tag_value;
171 ber_tlv_tag_t tag_class;
172 ber_tlv_tag_t tlv_tag;
173 ber_tlv_len_t tlv_len;
174 ber_tlv_len_t opt_tl_len; /* optional TL length */
177 /* Skip the whitespace */
178 for(; *line == ' ' || *line == '\t'; line++)
181 /* Find a tag opening angle bracket */
184 case '<': /* That's what we want! A tag opening */
188 case '#': /* This is a comment */
190 case '-': /* This is a comment (dash-dash) */
196 fprintf(stderr, "%s: Missing '<' after whitespace at line %d\n", fname,
201 /* Find a tag closing angle bracket */
202 for(; *line && *line != '>'; line++) {
204 fprintf(stderr, "%s: Invalid charset (%d) at line %d\n", fname,
205 *(const unsigned char *)line, lineno);
211 fprintf(stderr, "%s: Missing '>' at line %d\n", fname, lineno);
215 /* Ignore closing tags */
217 if(strchr(cl, '<')) { /* We are not very robust */
218 fprintf(stderr, "%s: Multiple tags per line at line %d\n", fname,
222 /* End-of-content octets */
224 buf[0] = buf[1] = 0x00;
225 fwrite(buf, 1, 2, stdout);
232 return 0; /* A comment */
234 return 0; /* An XML preamble */
246 "%s: Expected \"C\"/\"P\"/\"I\" as the XML tag name (%c) at "
248 fname, op[1], lineno);
255 "%s: Detected pretty-printing of primitive types at line %d. "
256 "Re-run `unber` with -p option to disable pretty-printing.\n",
261 tcl_pos = strstr(op, "T=\"[");
262 tl_pos = strstr(op, "TL=\"");
263 v_pos = strstr(op, "V=\"");
264 if(!tcl_pos || (!v_pos && constr != 2)) {
265 fprintf(stderr, "%s: Mandatory attribute %s is not found at line %d\n",
266 fname, (!tcl_pos) ? "T" : "V", lineno);
270 opt_tl_len = tl_pos ? strtoul(tl_pos + 4, 0, 10) : 0;
274 tlv_len = strtoul(v_pos + 3, 0, 10);
276 if(errno || (opt_tl_len && opt_tl_len < 2) || tlv_len < 0) {
277 fprintf(stderr, "%s: Invalid TL or V value at line %d\n", fname,
282 /* clang-format off */
285 case 'U': /* UNIVERSAL */
286 tag_class = ASN_TAG_CLASS_UNIVERSAL; break;
287 case 'P': /* PRIVATE */
288 tag_class = ASN_TAG_CLASS_PRIVATE; break;
289 case 'A': /* APPLICATION */
290 tag_class = ASN_TAG_CLASS_APPLICATION; break;
291 case '0': case '1': case '2': case '3': case '4':
292 case '5': case '6': case '7': case '8': case '9': /* context */
293 tag_class = ASN_TAG_CLASS_CONTEXT; break;
295 fprintf(stderr, "%s: Invalid tag class (%c) at line %d\n",
296 fname, tcl_pos[4], lineno);
301 case '"': tcl_pos = "";
303 case '0': case '1': case '2': case '3': case '4':
304 case '5': case '6': case '7': case '8': case '9':
310 /* clang-format on */
312 unsigned long tag_value_UL;
314 if(!*tcl_pos || ((tag_value_UL = strtoul(tcl_pos, 0, 10)) > UINT_MAX)
316 fprintf(stderr, "%s: Invalid tag value (%c) at line %d\n", fname,
320 tag_value = tag_value_UL;
322 tlv_tag = ((tag_value << 2) | tag_class);
324 ret = ber_tlv_tag_serialize(tlv_tag, buf, sizeof(buf));
325 assert(ret >= 1 && (size_t)ret < sizeof(buf));
330 ret += der_tlv_length_serialize(tlv_len, buf + ret, sizeof(buf) - ret);
331 assert(ret >= 2 && (size_t)ret < sizeof(buf));
333 if(opt_tl_len && ret != opt_tl_len) {
335 "%s: Cannot encode TL at line %d "
336 "in the given number of bytes (%ld!=%ld)\n",
337 fname, lineno, (long)ret, (long)opt_tl_len);
340 if(constr) *buf |= 0x20; /* Enable "constructed" bit */
341 fwrite(buf, 1, ret, stdout);
345 for(len = 0, cl++; *cl && *cl != '<'; cl++, len++) {
359 fprintf(stderr, "%s: Expected \"&#xNN;\" at line %d\n", fname,
363 for(v = 0, h = 0; h < 2; h++) {
364 unsigned char clv = *++cl;
366 /* clang-format off */
368 case '0': case '1': case '2': case '3': case '4':
369 case '5': case '6': case '7': case '8': case '9':
370 v |= clv - '0'; break;
371 case 'A': case 'B': case 'C':
372 case 'D': case 'E': case 'F':
373 v |= clv - 'A' + 10; break;
374 case 'a': case 'b': case 'c':
375 case 'd': case 'e': case 'f':
376 v |= clv - 'a' + 10; break;
379 "%s: Expected \"&#xNN;\" at line %d (%c)\n",
383 /* clang-format on */
387 fprintf(stderr, "%s: Expected \"&#xNN;\" at line %d\n", fname,
394 if(no_validation) fprintf(stderr, "Warning: ");
396 "%s: Could not encode value of %ld chars "
397 "at line %d in %ld bytes\n",
398 fname, (long)len, lineno, (long)tlv_len);
399 if(!no_validation) exit(EX_DATAERR);