Added SILC Thread Queue API
[silc.git] / apps / asn1utils / asn1dump.c
1 /*
2
3   asn1dump.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2007 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; version 2 of the License.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19
20 #include "silc.h"
21
22 SilcBool hexdump = FALSE;
23 SilcBool dec_base64 = FALSE;
24 SilcBool parse_all = FALSE;
25 SilcBool ignore_header = FALSE;
26
27 const char *asn1_tag_name(SilcAsn1Tag tag)
28 {
29   switch (tag) {
30   case SILC_ASN1_TAG_CHOICE:
31     return "choice";
32   case SILC_ASN1_TAG_ANY:
33     return "any";
34   case SILC_ASN1_TAG_ANY_PRIMITIVE:
35     return "any primitive";
36   case SILC_ASN1_TAG_SEQUENCE_OF:
37     return "sequence of";
38   case SILC_ASN1_TAG_SEQUENCE:
39     return "sequence";
40   case SILC_ASN1_TAG_SET:
41     return "set";
42   case SILC_ASN1_TAG_INTEGER:
43     return "integer";
44   case SILC_ASN1_TAG_SHORT_INTEGER:
45     return "short integer";
46   case SILC_ASN1_TAG_OID:
47     return "oid";
48   case SILC_ASN1_TAG_BOOLEAN:
49     return "boolean";
50   case SILC_ASN1_TAG_OCTET_STRING:
51     return "octet-string";
52   case SILC_ASN1_TAG_BIT_STRING:
53     return "bit-string";
54   case SILC_ASN1_TAG_NULL:
55     return "null";
56   case SILC_ASN1_TAG_ENUM:
57     return "enum";
58   case SILC_ASN1_TAG_UTC_TIME:
59     return "utc-time";
60   case SILC_ASN1_TAG_GENERALIZED_TIME:
61     return "generalized-time";
62   case SILC_ASN1_TAG_UTF8_STRING:
63     return "utf8-string";
64   case SILC_ASN1_TAG_NUMERIC_STRING:
65     return "numeric-string";
66   case SILC_ASN1_TAG_PRINTABLE_STRING:
67     return "printable-string";
68   case SILC_ASN1_TAG_IA5_STRING:
69     return "ia5-string";
70   case SILC_ASN1_TAG_VISIBLE_STRING:
71     return "visible-string";
72   case SILC_ASN1_TAG_UNIVERSAL_STRING:
73     return "universal-string";
74   case SILC_ASN1_TAG_UNRESTRICTED_STRING:
75     return "unrestricted-string";
76   case SILC_ASN1_TAG_BMP_STRING:
77     return "bmp-string";
78   case SILC_ASN1_TAG_ODE:
79     return "ode";
80   case SILC_ASN1_TAG_ETI:
81     return "eti";
82   case SILC_ASN1_TAG_REAL:
83     return "real";
84   case SILC_ASN1_TAG_EMBEDDED:
85     return "embedded";
86   case SILC_ASN1_TAG_ROI:
87     return "roi";
88   case SILC_ASN1_TAG_TELETEX_STRING:
89     return "teletex-string";
90   case SILC_ASN1_TAG_VIDEOTEX_STRING:
91     return "videotex-string";
92   case SILC_ASN1_TAG_GRAPHIC_STRING:
93     return "graphic-string";
94   case SILC_ASN1_TAG_GENERAL_STRING:
95     return "general-string";
96   default:
97     break;
98   }
99   return "unknown";
100 }
101
102 int asn1_dump(SilcAsn1 asn1, SilcBuffer src, int depth)
103 {
104   SilcBool ret = FALSE;
105   SilcBerEncoding renc;
106   SilcUInt32 rtag;
107   const unsigned char *rdata;
108   SilcBufferStruct buf;
109   SilcBerClass rclass;
110   SilcUInt32 rdata_len, len = 0;
111   SilcBool rindef;
112   char indent[256];
113
114   memset(indent, 0, sizeof(indent));
115
116   while (silc_buffer_len(src)) {
117     /* Decode the BER block */
118     ret = silc_ber_decode(src, &rclass, &renc, &rtag, &rdata,
119                           &rdata_len, &rindef, &len);
120     if (!ret) {
121       fprintf(stderr, "Error: Cannot parse BER block, malformed ASN.1 data\n");
122       return -1;
123     }
124
125     /* If class is 0, encoding 0, tag 0 and data length 0 ignore them
126        as they are zero bytes, unless user wants to see them */
127     if (rclass == 0 && renc == 0 && rtag == 0 && rdata_len == 0 &&
128         !parse_all) {
129       if (len && silc_buffer_len(src) >= len)
130         silc_buffer_pull(src, len);
131       continue;
132     }
133
134     if (depth)
135       memset(indent, 32, depth);
136
137     fprintf(stdout, "%04d: %s%s [%d] %s %s %s", depth, indent,
138             asn1_tag_name(rtag), (int)rtag,
139             rclass == SILC_BER_CLASS_UNIVERSAL   ? "univ" :
140             rclass == SILC_BER_CLASS_APPLICATION ? "appl" :
141             rclass == SILC_BER_CLASS_CONTEXT     ? "cont" : "priv",
142             renc == SILC_BER_ENC_PRIMITIVE ? "primit" : "constr",
143             rindef ? "indef" : "defin");
144
145     if (rtag != SILC_ASN1_TAG_SEQUENCE &&
146         rtag != SILC_ASN1_TAG_SET &&
147         rtag != SILC_ASN1_TAG_SEQUENCE_OF) {
148       if (hexdump) {
149         fprintf(stdout, " [len %lu]\n", rdata_len);
150         silc_hexdump(rdata, rdata_len, stdout);
151       } else {
152         fprintf(stdout, "\n");
153       }
154     } else {
155       fprintf(stdout, "\n");
156     }
157
158     if (renc == SILC_BER_ENC_PRIMITIVE)
159       len = len + rdata_len;
160     else
161       len = len;
162
163     if (len && silc_buffer_len(src) >= len)
164       silc_buffer_pull(src, len);
165
166     /* Decode sequences and sets recursively */
167     if ((rtag == SILC_ASN1_TAG_SEQUENCE ||
168          rtag == SILC_ASN1_TAG_SET ||
169          rtag == SILC_ASN1_TAG_SEQUENCE_OF) &&
170         depth + 1 < sizeof(indent) - 1) {
171       silc_buffer_set(&buf, (unsigned char *)rdata, rdata_len);
172       if (silc_buffer_len(src) >= rdata_len)
173         silc_buffer_pull(src, rdata_len);
174       if (asn1_dump(asn1, &buf, depth + 1) < 0)
175         return -1;
176       if (silc_buffer_len(src) == 0)
177         return 0;
178     }
179   }
180
181   return 0;
182 }
183
184 void usage(void)
185 {
186     fprintf(stdout, ""
187 "Usage: asn1dump [OPTIONS] FILE\n"
188 "\n"
189 "Operation modes:\n"
190 "  -h        Print this help, then exit\n"
191 "  -x        HEX dump ASN.1 data\n"
192 "  -b        Remove Base64 encoding before parsing\n"
193 "  -i        Remove file header/footer that has at least four '-' characters\n"
194 "  -a        Parse all data, including possible trailing zeroes\n"
195 "\n"
196 "ASN.1 classes:\n"
197 "  univ      Universal\n"
198 "  appl      Application\n"
199 "  cont      Context\n"
200 "  priv      Private\n"
201 "\n"
202 "ASN.1 length types:\n"
203 "  defin     Definitive\n"
204 "  indef     Indefinitive\n"
205 "\n"
206 "ASN.1 encoding types:\n"
207 "  primit    Primitive\n"
208 "  constr    Constructed\n"
209 "\n"
210             );
211 }
212
213 int main(int argc, char **argv)
214 {
215   int opt, ret, i;
216   SilcAsn1 asn1;
217   SilcBufferStruct buf;
218   unsigned char *data, *tmp;
219   SilcUInt32 data_len;
220
221   if (argc < 2) {
222     usage();
223     return 1;
224   }
225
226   while ((opt = getopt(argc, argv, "hxbai")) != EOF) {
227     switch (opt) {
228     case 'h':
229       usage();
230       return 1;
231       break;
232
233     case 'x':
234       hexdump = TRUE;
235       break;
236
237     case 'b':
238       dec_base64 = TRUE;
239       break;
240
241     case 'i':
242       ignore_header = TRUE;
243       break;
244
245     case 'a':
246       parse_all = TRUE;
247       break;
248
249     default:
250       usage();
251       return 1;
252     }
253   }
254
255   data = tmp = silc_file_readfile(argv[argc - 1], &data_len, NULL);
256   if (!data) {
257     fprintf(stderr, "Error: Cannot read file '%s': %s\n", argv[argc - 1],
258             strerror(errno));
259     return 1;
260   }
261
262   silc_buffer_set(&buf, data, data_len);
263
264   if (ignore_header) {
265     SilcBool header = FALSE;
266     for (i = 0; i < data_len; i++) {
267       if (data_len > i + 4 &&
268           data[i    ] == '-' && data[i + 1] == '-' &&
269           data[i + 2] == '-' && data[i + 3] == '-') {
270
271         if (data_len > i + 5 && (data[i + 4] == '\r' ||
272                                  tmp[i + 4] == '\n')) {
273           /* End of line, header */
274           if (data_len > i + 6 && data[i + 4] == '\r' &&
275               data[i + 5] == '\n')
276             i++;
277           i += 5;
278           silc_buffer_pull(&buf, i);
279           header = TRUE;
280         } else if (i > 0 && data_len > i + 5 && data[i + 4] != '-' &&
281                    header) {
282           /* Start of line, footer */
283           silc_buffer_push_tail(&buf, silc_buffer_truelen(&buf) - i);
284           break;
285         }
286       }
287     }
288   }
289
290   if (dec_base64) {
291     data = silc_base64_decode(NULL, silc_buffer_data(&buf),
292                               silc_buffer_len(&buf), &data_len);
293     if (!data) {
294       fprintf(stderr, "Error: Cannot decode Base64 encoding\n");
295       return 1;
296     }
297     silc_buffer_set(&buf, data, data_len);
298     silc_free(tmp);
299   }
300
301   asn1 = silc_asn1_alloc(NULL);
302
303   ret = asn1_dump(asn1, &buf, 0);
304
305   silc_asn1_free(asn1);
306   silc_free(data);
307
308   return ret;
309 }