3aeab6c1514777986f5a5c43c11cb735288b414b
[com/asn1c.git] / asn1c / enber.c
1 /*-
2  * Copyright (c) 2004, 2005 Lev Walkin <vlm@lionet.info>. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
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.
12  *
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
23  * SUCH DAMAGE.
24  *
25  * $Id$
26  */
27 #include "sys-common.h"
28
29 #include <asn1parser.h> /* For static string tables */
30
31 #include <asn_application.h>
32 #include <constraints.c>
33 #include <ber_tlv_tag.c>
34 #include <ber_tlv_length.c>
35
36 #undef COPYRIGHT
37 #define COPYRIGHT "Copyright (c) 2004, 2005 Lev Walkin <vlm@lionet.info>\n"
38
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);
42
43 static int no_validation; /* -n */
44
45 int
46 main(int ac, char **av) {
47     int ch; /* Command line character */
48     int i;  /* Index in some loops */
49
50     /*
51      * Process command-line options.
52      */
53     while((ch = getopt(ac, av, "nhv")) != -1) switch(ch) {
54         case 'n':
55             no_validation++;
56             break;
57         case 'v':
58             usage(av[0], 1);
59             break;
60         case 'h':
61         default:
62             usage(av[0], 0);
63         }
64
65     /*
66      * Ensure that there are some input files present.
67      */
68     if(ac > optind) {
69         ac -= optind;
70         av += optind;
71     } else {
72         fprintf(stderr, "%s: No input files specified\n", av[0]);
73         exit(1);
74     }
75
76     setvbuf(stdout, 0, _IOLBF, 0);
77
78     /*
79      * Iterate over input files and parse each.
80      * All syntax trees from all files will be bundled together.
81      */
82     for(i = 0; i < ac; i++) {
83         if(process(av[i])) exit(EX_DATAERR);
84     }
85
86     return 0;
87 }
88
89 /*
90  * Print the usage screen and exit(EX_USAGE).
91  */
92 static void
93 usage(const char *av0, int copyright_only) {
94     fprintf(stderr,
95             "Convert unber(1)'s output back into BER, "
96             "v" VERSION "\n" COPYRIGHT);
97     if(copyright_only) exit(0);
98     fprintf(stderr,
99             "Usage: %s [-n] [-] [file ...]\n"
100             "Options:\n"
101             "  -n      Disable XML input validation\n",
102             av0);
103     exit(EX_USAGE);
104 }
105
106 /*
107  * Open the file and initiate recursive processing.
108  */
109 static int
110 process(const char *fname) {
111     char buf[8192];
112     char *collector = 0;
113     size_t collector_size = sizeof(buf);
114     size_t collector_offset = 0;
115     int lineno = 0;
116     FILE *fp;
117
118     if(strcmp(fname, "-")) {
119         fp = fopen(fname, "r");
120         if(!fp) {
121             perror(fname);
122             return -1;
123         }
124     } else {
125         fp = stdin;
126     }
127
128
129     while(fgets(buf, sizeof(buf), fp) || !feof(fp)) {
130         size_t len = strlen(buf);
131
132         if(!len) continue;
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);
137                 if(!collector) {
138                     perror("realloc()");
139                     exit(EX_OSERR);
140                 }
141             }
142             memcpy(collector + collector_offset, buf, len + 1);
143             collector_offset += len;
144         }
145         if(buf[len - 1] != '\n') continue;
146
147         if(collector_offset) {
148             assert(collector[collector_offset - 1] == '\n');
149             process_line(fname, collector, ++lineno);
150             collector_offset = 0;
151         } else {
152             process_line(fname, buf, ++lineno);
153         }
154     }
155
156     if(fp != stdin) fclose(fp);
157
158     return 0;
159 }
160
161 static int
162 process_line(const char *fname, char *line, int lineno) {
163     char buf[32];
164     char *op;      /* '<' */
165     char *cl;      /* '>' */
166     char *tcl_pos; /* tag class (T=") position */
167     char *tl_pos;  /* tag length (TL=") position */
168     char *v_pos;   /* value length (V=") position */
169     int constr;
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 */
175     ssize_t ret;
176
177     /* Skip the whitespace */
178     for(; *line == ' ' || *line == '\t'; line++)
179         ;
180
181     /* Find a tag opening angle bracket */
182     op = line;
183     switch(*op) {
184     case '<': /* That's what we want! A tag opening */
185         break;
186     case '\r':
187     case '\n':
188     case '#': /* This is a comment */
189         return 0;
190     case '-': /* This is a comment (dash-dash) */
191         if(op[1] == '-') {
192             return 0;
193         }
194         /* Fall through */
195     default:
196         fprintf(stderr, "%s: Missing '<' after whitespace at line %d\n", fname,
197                 lineno);
198         exit(EX_DATAERR);
199     }
200
201     /* Find a tag closing angle bracket */
202     for(; *line && *line != '>'; line++) {
203         if(*line < ' ') {
204             fprintf(stderr, "%s: Invalid charset (%d) at line %d\n", fname,
205                     *(const unsigned char *)line, lineno);
206             exit(EX_DATAERR);
207         }
208     }
209     cl = line;
210     if(*cl != '>') {
211         fprintf(stderr, "%s: Missing '>' at line %d\n", fname, lineno);
212         exit(EX_DATAERR);
213     }
214
215     /* Ignore closing tags */
216     if(op[1] == '/') {
217         if(strchr(cl, '<')) { /* We are not very robust */
218             fprintf(stderr, "%s: Multiple tags per line at line %d\n", fname,
219                     lineno);
220             exit(EX_DATAERR);
221         }
222         /* End-of-content octets */
223         if(op[2] == 'I') {
224             buf[0] = buf[1] = 0x00;
225             fwrite(buf, 1, 2, stdout);
226         }
227         return 0;
228     }
229
230     switch(op[1]) {
231     case '!':
232         return 0; /* A comment */
233     case '?':
234         return 0; /* An XML preamble */
235     case 'C':
236         constr = 1;
237         break;
238     case 'P':
239         constr = 0;
240         break;
241     case 'I':
242         constr = 2;
243         break;
244     default:
245         fprintf(stderr,
246                 "%s: Expected \"C\"/\"P\"/\"I\" as the XML tag name (%c) at "
247                 "line %d\n",
248                 fname, op[1], lineno);
249         exit(EX_DATAERR);
250     }
251
252     *cl = '\0';
253     if(cl[-1] == 'F') {
254         fprintf(stderr,
255                 "%s: Detected pretty-printing of primitive types at line %d. "
256                 "Re-run `unber` with -p option to disable pretty-printing.\n",
257                 fname, lineno);
258         exit(EX_DATAERR);
259     }
260
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);
267         exit(EX_DATAERR);
268     }
269     errno = 0;
270     opt_tl_len = tl_pos ? strtoul(tl_pos + 4, 0, 10) : 0;
271     if(constr == 2) {
272         tlv_len = 0;
273     } else {
274         tlv_len = strtoul(v_pos + 3, 0, 10);
275     }
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,
278                 lineno);
279         exit(EX_DATAERR);
280     }
281
282     /* clang-format off */
283         tcl_pos += 4;
284         switch(*tcl_pos) {
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;
294         default:
295                 fprintf(stderr, "%s: Invalid tag class (%c) at line %d\n",
296                         fname, tcl_pos[4], lineno);
297                 exit(EX_DATAERR);
298         }
299         for(;; tcl_pos++) {
300                 switch(*tcl_pos) {
301                 case '"': tcl_pos = "";
302                 case '\0':
303                 case '0': case '1': case '2': case '3': case '4':
304                 case '5': case '6': case '7': case '8': case '9':
305                         break;
306                 default: continue;
307                 }
308                 break;
309         }
310     /* clang-format on */
311
312     unsigned long tag_value_UL;
313     errno = 0;
314     if(!*tcl_pos || ((tag_value_UL = strtoul(tcl_pos, 0, 10)) > UINT_MAX)
315        || errno) {
316         fprintf(stderr, "%s: Invalid tag value (%c) at line %d\n", fname,
317                 *tcl_pos, lineno);
318         exit(EX_DATAERR);
319     } else {
320         tag_value = tag_value_UL;
321     }
322     tlv_tag = ((tag_value << 2) | tag_class);
323
324     ret = ber_tlv_tag_serialize(tlv_tag, buf, sizeof(buf));
325     assert(ret >= 1 && (size_t)ret < sizeof(buf));
326     if(constr == 2) {
327         buf[ret] = 0x80;
328         ret += 1;
329     } else {
330         ret += der_tlv_length_serialize(tlv_len, buf + ret, sizeof(buf) - ret);
331         assert(ret >= 2 && (size_t)ret < sizeof(buf));
332     }
333     if(opt_tl_len && ret != opt_tl_len) {
334         fprintf(stderr,
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);
338         exit(EX_DATAERR);
339     }
340     if(constr) *buf |= 0x20; /* Enable "constructed" bit */
341     fwrite(buf, 1, ret, stdout);
342
343     if(!constr) {
344         ber_tlv_len_t len;
345         for(len = 0, cl++; *cl && *cl != '<'; cl++, len++) {
346             unsigned char v;
347             int h;
348             if(*cl != '&') {
349                 fputc(*cl, stdout);
350                 continue;
351             }
352             cl++;
353             if(*cl != '#') {
354                 fputc(*cl, stdout);
355                 continue;
356             }
357             cl++;
358             if(*cl != 'x') {
359                 fprintf(stderr, "%s: Expected \"&#xNN;\" at line %d\n", fname,
360                         lineno);
361                 exit(EX_DATAERR);
362             }
363             for(v = 0, h = 0; h < 2; h++) {
364                 unsigned char clv = *++cl;
365                 v <<= 4;
366                 /* clang-format off */
367                         switch(clv) {
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;
377                         default:
378                                 fprintf(stderr,
379                                         "%s: Expected \"&#xNN;\" at line %d (%c)\n",
380                                         fname, lineno, clv);
381                                 exit(EX_DATAERR);
382                         }
383                 /* clang-format on */
384             }
385             cl++;
386             if(*cl != ';') {
387                 fprintf(stderr, "%s: Expected \"&#xNN;\" at line %d\n", fname,
388                         lineno);
389                 exit(EX_DATAERR);
390             }
391             fputc(v, stdout);
392         }
393         if(len != tlv_len) {
394             if(no_validation) fprintf(stderr, "Warning: ");
395             fprintf(stderr,
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);
400         }
401     }
402
403     return 0;
404 }