NativeEnumerated.c vars NULL init and check
[com/asn1c.git] / asn1c / unber.c
1 /*-
2  * Copyright (c) 2004, 2005, 2006 Lev Walkin <vlm@lionet.info>.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $Id$
27  */
28 #include "sys-common.h"
29
30 #define ASN_DISABLE_PER_SUPPORT 1
31 #define ASN_DISABLE_OER_SUPPORT 1
32
33 #include <asn1parser.h> /* For static string tables */
34
35 #include <asn_application.h>
36 #include <constraints.c>
37 #include <ber_tlv_tag.c>
38 #include <ber_tlv_length.c>
39 #include <INTEGER.c>
40 #include <OBJECT_IDENTIFIER.c>
41 #include <RELATIVE-OID.c>
42 #include <asn_codecs_prim.c>
43 #include <asn1p_integer.c>
44 #include <asn_internal.c>
45
46 #undef COPYRIGHT
47 #define COPYRIGHT "Copyright (c) 2004, 2005 Lev Walkin <vlm@lionet.info>\n"
48
49 static void usage(const char *av0);    /* Print the Usage screen and exit */
50 static int process(const char *fname); /* Perform the BER decoding */
51 static int decode_tlv_from_string(const char *datastring);
52
53 static int single_type_decoding = 0;   /* -1 enables that */
54 static int minimalistic = 0;           /* -m enables that */
55 static int pretty_printing = 1;        /* -p disables that */
56 static long skip_bytes = 0;            /* -s controls that */
57 static char indent_bytes[16] = "    "; /* -i controls that */
58
59 int
60 main(int ac, char **av) {
61     int ch; /* Command line character */
62     int i;  /* Index in some loops */
63
64     /*
65      * Process command-line options.
66      */
67     while((ch = getopt(ac, av, "1hi:mps:t:v")) != -1) switch(ch) {
68         case '1':
69             single_type_decoding = 1;
70             break;
71         case 'i':
72             i = atoi(optarg);
73             if(i >= 0 && i < (int)sizeof(indent_bytes)) {
74                 memset(indent_bytes, ' ', i);
75                 indent_bytes[i] = '\0';
76             } else {
77                 fprintf(stderr, "-i %s: Invalid indent value\n", optarg);
78                 exit(EX_USAGE);
79             }
80             break;
81         case 'm':
82             minimalistic = 1;
83             break;
84         case 'p':
85             pretty_printing = 0;
86             break;
87         case 's':
88             skip_bytes = atol(optarg);
89             if(skip_bytes < 0) {
90                 fprintf(stderr, "-s %s: positive value expected\n", optarg);
91                 exit(EX_USAGE);
92             }
93             break;
94         case 't':
95             if(decode_tlv_from_string(optarg)) exit(EX_DATAERR);
96             exit(0);
97         case 'v':
98             fprintf(stderr, "ASN.1 BER Decoder, v" VERSION "\n" COPYRIGHT);
99             exit(0);
100             break;
101         case 'h':
102         default:
103             usage(av[0]);
104         }
105
106     /*
107      * Ensure that there are some input files present.
108      */
109     if(ac > optind) {
110         ac -= optind;
111         av += optind;
112     } else {
113         fprintf(stderr, "%s: No input files specified\n", av[0]);
114         exit(1);
115     }
116
117     setvbuf(stdout, 0, _IOLBF, 0);
118
119     /*
120      * Iterate over input files and parse each.
121      * All syntax trees from all files will be bundled together.
122      */
123     for(i = 0; i < ac; i++) {
124         if(process(av[i])) exit(EX_DATAERR);
125     }
126
127     return 0;
128 }
129
130 /*
131  * Print the usage screen and exit(EX_USAGE).
132  */
133 static void
134 usage(const char *av0) {
135     /* clang-format off */
136         fprintf(stderr,
137 "ASN.1 BER Decoder, v" VERSION "\n" COPYRIGHT
138 "Usage: %s [options] [-] [file ...]\n"
139 "Options:\n"
140 "  -1                Decode only the first BER structure (otherwise, until EOF)\n"
141 "  -i <indent>       Amount of spaces for output indentation (default is 4)\n"
142 "  -m                Minimalistic mode: print as little as possible\n"
143 "  -p                Do not attempt pretty-printing of known ASN.1 types\n"
144 "  -s <skip>         Ignore first <skip> bytes of input\n"
145 "  -t <hex-string>   Decode the given tag[/length] sequence (e.g. -t \"bf20\")\n"
146 "\n"
147 "The XML opening tag format is as follows:\n"
148 "  <tform O=\"off\" T=\"tag\" TL=\"tl_len\" V=\"{Indefinite|v_len}\" [A=\"type\"] [F]>\n"
149 "Where:\n"
150 "  tform    Which form the value is in: constructed (\"C\", \"I\") or primitive (\"P\")\n"
151 "  off      Offset of the encoded element in the unber input stream\n"
152 "  tag      The tag class and value in human readable form\n"
153 "  tl_len   The length of the TL (BER Tag and Length) encoding\n"
154 "  v_len    The length of the value (V, encoded by the L), may be \"Indefinite\"\n"
155 "  type     Likely name of the underlying ASN.1 type (for [UNIVERSAL n] tags)\n"
156 "  [F]      Indicates that the value was reformatted (pretty-printed)\n"
157 "See the manual page for details\n"
158         , av0);
159     /* clang-format on */
160     exit(EX_USAGE);
161 }
162
163 typedef enum pd_code {
164     PD_FAILED = -1,
165     PD_FINISHED = 0,
166     PD_EOF = 1,
167 } pd_code_e;
168 static pd_code_e process_deeper(const char *fname, FILE *fp,
169                                 size_t *offset, int level,
170                                 ssize_t limit, ber_tlv_len_t *frame_size,
171                                 ber_tlv_len_t effective_size, int expect_eoc);
172 static void print_TL(int fin, size_t offset, int level, int constr,
173                      ssize_t tlen, ber_tlv_tag_t, ber_tlv_len_t,
174                      ber_tlv_len_t effective_frame_size);
175 static int print_V(const char *fname, FILE *fp, ber_tlv_tag_t, ber_tlv_len_t);
176
177 /*
178  * Open the file and initiate recursive processing.
179  */
180 static int
181 process(const char *fname) {
182     FILE *fp;
183     pd_code_e pdc;
184     size_t offset = 0;   /* Stream decoding position */
185     ber_tlv_len_t frame_size = 0; /* Single frame size */
186
187     if(strcmp(fname, "-")) {
188         fp = fopen(fname, "rb");
189         if(!fp) {
190             perror(fname);
191             return -1;
192         }
193     } else {
194         fp = stdin;
195     }
196
197     /*
198      * Skip the requested amount of bytes.
199      */
200     for(; offset < (size_t)skip_bytes; offset++) {
201         if(fgetc(fp) == -1) {
202             fprintf(stderr, "%s: input source (%zu bytes) "
203                             "has less data than \"-s %ld\" switch "
204                             "wants to skip\n",
205                     fname, offset, skip_bytes);
206             if(fp != stdin) fclose(fp);
207             return -1;
208         }
209     }
210
211     /*
212      * Fetch out BER-encoded data until EOF or error.
213      */
214     do {
215         pdc = process_deeper(fname, fp, &offset, 0, -1, &frame_size, 0, 0);
216     } while(pdc == PD_FINISHED && !single_type_decoding);
217
218     if(fp != stdin) fclose(fp);
219
220     if(pdc == PD_FAILED) return -1;
221     return 0;
222 }
223
224 /*
225  * Process the TLV recursively.
226  */
227 static pd_code_e
228 process_deeper(const char *fname, FILE *fp, size_t *offset, int level,
229                ssize_t limit, ber_tlv_len_t *frame_size,
230                ber_tlv_len_t effective_size, int expect_eoc) {
231     unsigned char tagbuf[32];
232     ssize_t tblen = 0;
233     pd_code_e pdc = PD_FINISHED;
234     ber_tlv_tag_t tlv_tag;
235     ber_tlv_len_t tlv_len;
236     ssize_t t_len;
237     ssize_t l_len;
238
239     for(;;) {
240         ber_tlv_len_t local_esize = 0;
241         int constr;
242         int ch;
243
244         if(limit == 0) return PD_FINISHED;
245
246         if(limit >= 0 && tblen >= limit) {
247             fprintf(stderr,
248                     "%s: Too long TL sequence (%ld >= %ld)"
249                     " at %zu. "
250                     "Broken or maliciously constructed file\n",
251                     fname, (long)tblen, (long)limit, *offset);
252             return PD_FAILED;
253         }
254
255         /* Get the next byte from the input stream */
256         ch = fgetc(fp);
257         if(ch == -1) {
258             if(limit > 0 || expect_eoc) {
259                 fprintf(stderr,
260                         "%s: Unexpected end of file (TL)"
261                         " at %zu\n",
262                         fname, *offset);
263                 return PD_FAILED;
264             } else {
265                 return PD_EOF;
266             }
267         }
268
269         tagbuf[tblen++] = ch;
270
271         /*
272          * Decode the TLV tag.
273          */
274         t_len = ber_fetch_tag(tagbuf, tblen, &tlv_tag);
275         switch(t_len) {
276         case -1:
277             fprintf(stderr,
278                     "%s: Fatal error decoding tag"
279                     " at %zu+%ld\n",
280                     fname, *offset, (long)tblen);
281             return PD_FAILED;
282         case 0:
283             /* More data expected */
284             continue;
285         }
286
287         /*
288          * Decode the TLV length.
289          */
290         constr = BER_TLV_CONSTRUCTED(tagbuf);
291         l_len =
292             ber_fetch_length(constr, tagbuf + t_len, tblen - t_len, &tlv_len);
293         switch(l_len) {
294         case -1:
295             fprintf(stderr,
296                     "%s: Fatal error decoding value length"
297                     " at %zu\n",
298                     fname, *offset + t_len);
299             return PD_FAILED;
300         case 0:
301             /* More data expected */
302             continue;
303         }
304
305         /* Make sure the T & L decoders took exactly the whole buffer */
306         assert((t_len + l_len) == tblen);
307
308         if(!expect_eoc || tagbuf[0] || tagbuf[1])
309             print_TL(0, *offset, level, constr, tblen, tlv_tag, tlv_len,
310                      effective_size);
311
312         if(limit != -1) {
313             /* If limit is set, account for the TL sequence */
314             limit -= (t_len + l_len);
315             assert(limit >= 0);
316
317             if(tlv_len > limit) {
318                 fprintf(stderr,
319                         "%s: Structure advertizes length (%ld) "
320                         "greater than of a parent container (%ld)\n",
321                         fname, (long)tlv_len, (long)limit);
322                 return PD_FAILED;
323             }
324         }
325
326         *offset += t_len + l_len;
327         *frame_size += t_len + l_len;
328         effective_size += t_len + l_len;
329         local_esize += t_len + l_len;
330
331         if(expect_eoc && !tagbuf[0] && !tagbuf[1]) {
332             /* End of content octets */
333             print_TL(1, *offset - 2, level - 1, 1, 2, 0, -1, effective_size);
334             return PD_FINISHED;
335         }
336
337         if(constr) {
338             ber_tlv_len_t dec = 0;
339             /*
340              * This is a constructed type. Process recursively.
341              */
342             printf(">\n"); /* Close the opening tag */
343             if(tlv_len != -1 && limit != -1) {
344                 assert(limit >= tlv_len);
345             }
346             pdc = process_deeper(fname, fp, offset, level + 1,
347                                  tlv_len == -1 ? limit : tlv_len, &dec,
348                                  t_len + l_len, tlv_len == -1);
349             if(pdc == PD_FAILED) return pdc;
350             if(limit != -1) {
351                 assert(limit >= dec);
352                 limit -= dec;
353             }
354             *frame_size += dec;
355             effective_size += dec;
356             local_esize += dec;
357             if(tlv_len == -1) {
358                 tblen = 0;
359                 if(pdc == PD_FINISHED && limit < 0 && !expect_eoc) return pdc;
360                 continue;
361             }
362         } else {
363             assert(tlv_len >= 0);
364             if(print_V(fname, fp, tlv_tag, tlv_len)) return PD_FAILED;
365
366             if(limit != -1) {
367                 assert(limit >= tlv_len);
368                 limit -= tlv_len;
369             }
370             *offset += tlv_len;
371             *frame_size += tlv_len;
372             effective_size += tlv_len;
373             local_esize += tlv_len;
374         }
375
376         print_TL(1, *offset, level, constr, tblen, tlv_tag, tlv_len,
377                  local_esize);
378
379         tblen = 0;
380
381         /* Report success for a single top level TLV */
382         if(level == 0 && limit == -1 && !expect_eoc) break;
383     } /* for(;;) */
384
385     return pdc;
386 }
387
388 static void
389 print_TL(int fin, size_t offset, int level, int constr, ssize_t tlen,
390          ber_tlv_tag_t tlv_tag, ber_tlv_len_t tlv_len,
391          ber_tlv_len_t effective_size) {
392     if(fin && !constr) {
393         printf("</P>\n");
394         return;
395     }
396
397     while(level-- > 0) fputs(indent_bytes, stdout); /* Print indent */
398     printf(fin ? "</" : "<");
399
400     printf(constr ? ((tlv_len == -1) ? "I" : "C") : "P");
401
402     /* Print out the offset of this boundary, even if closing tag */
403     if(!minimalistic) printf(" O=\"%zu\"", offset);
404
405     printf(" T=\"");
406     ber_tlv_tag_fwrite(tlv_tag, stdout);
407     printf("\"");
408
409     if(!fin || (tlv_len == -1 && !minimalistic))
410         printf(" TL=\"%ld\"", (long)tlen);
411     if(!fin) {
412         if(tlv_len == -1)
413             printf(" V=\"Indefinite\"");
414         else
415             printf(" V=\"%ld\"", (long)tlv_len);
416     }
417
418     if(!minimalistic && BER_TAG_CLASS(tlv_tag) == ASN_TAG_CLASS_UNIVERSAL) {
419         const char *str;
420         ber_tlv_tag_t tvalue = BER_TAG_VALUE(tlv_tag);
421         str = ASN_UNIVERSAL_TAG2STR(tvalue);
422         if(str) printf(" A=\"%s\"", str);
423     }
424
425     if(fin) {
426         if(constr && !minimalistic) printf(" L=\"%ld\"", (long)effective_size);
427         printf(">\n");
428     }
429 }
430
431 /*
432  * Print the value in binary form, or reformat for pretty-printing.
433  */
434 static int
435 print_V(const char *fname, FILE *fp, ber_tlv_tag_t tlv_tag,
436         ber_tlv_len_t tlv_len) {
437     asn_oid_arc_t *arcs = 0; /* Object identifier arcs */
438     unsigned char *vbuf = 0;
439     asn1p_expr_type_e etype = 0;
440     asn1c_integer_t collector = 0;
441     int special_format = 0;
442     ssize_t i;
443
444     /* Figure out what type is it */
445     if(BER_TAG_CLASS(tlv_tag) == ASN_TAG_CLASS_UNIVERSAL && pretty_printing) {
446         ber_tlv_tag_t tvalue = BER_TAG_VALUE(tlv_tag);
447         etype = ASN_UNIVERSAL_TAG2TYPE(tvalue);
448     }
449
450     /*
451      * Determine how to print the value, either in its native binary form,
452      * encoded with &xNN characters, or using pretty-printing.
453      * The basic string types (including "useful types", like UTCTime)
454      * are excempt from this determination logic, because their alphabets
455      * are subsets of the XML's native UTF-8 encoding.
456      */
457     switch(etype) {
458     case ASN_BASIC_BOOLEAN:
459         if(tlv_len == 1)
460             special_format = 1;
461         else
462             etype = 0;
463         break;
464     case ASN_BASIC_INTEGER:
465     case ASN_BASIC_ENUMERATED:
466         if((size_t)tlv_len <= sizeof(collector))
467             special_format = 1;
468         else
469             etype = 0;
470         break;
471     case ASN_BASIC_OBJECT_IDENTIFIER:
472     case ASN_BASIC_RELATIVE_OID:
473         if(tlv_len > 0 && tlv_len < 128 * 1024 /* VERY long OID! */) {
474             arcs = MALLOC(sizeof(*arcs) * (tlv_len + 1));
475             if(arcs) {
476                 vbuf = MALLOC(tlv_len + 1);
477                 /* Not checking is intentional */
478             }
479         }
480     case ASN_BASIC_UTCTime:
481     case ASN_BASIC_GeneralizedTime:
482     case ASN_STRING_NumericString:
483     case ASN_STRING_PrintableString:
484     case ASN_STRING_VisibleString:
485     case ASN_STRING_IA5String:
486     case ASN_STRING_UTF8String:
487         break; /* Directly compatible with UTF-8 */
488     case ASN_STRING_BMPString:
489     case ASN_STRING_UniversalString:
490         break; /* Not directly compatible with UTF-8 */
491     default:
492         /* Conditionally compatible with UTF-8 */
493         if(((etype & ASN_STRING_MASK) || (etype == ASN_BASIC_OCTET_STRING) ||
494             /*
495              * AUTOMATIC TAGS or IMPLICIT TAGS in effect,
496              * Treat this primitive type as OCTET_STRING.
497              */
498             (BER_TAG_CLASS(tlv_tag) != ASN_TAG_CLASS_UNIVERSAL
499              && pretty_printing))
500            && (tlv_len > 0 && tlv_len < 128 * 1024)) {
501             vbuf = MALLOC(tlv_len + 1);
502             /* Not checking is intentional */
503         }
504         break;
505     }
506
507     /* If collection vbuf is present, defer printing the F flag. */
508     if(!vbuf) printf(special_format ? " F>" : ">");
509
510     /*
511      * Print the value in binary or text form,
512      * or collect the bytes into vbuf.
513      */
514     for(i = 0; i < tlv_len; i++) {
515         int ch = fgetc(fp);
516         if(ch == -1) {
517             fprintf(stderr, "%s: Unexpected end of file (V)\n", fname);
518             if(vbuf) FREEMEM(vbuf);
519             if(arcs) FREEMEM(arcs);
520             return -1;
521         }
522         switch(etype) {
523         case ASN_BASIC_UTCTime:
524         case ASN_BASIC_GeneralizedTime:
525         case ASN_STRING_NumericString:
526         case ASN_STRING_PrintableString:
527         case ASN_STRING_VisibleString:
528         case ASN_STRING_IA5String:
529         case ASN_STRING_UTF8String:
530             switch(ch) {
531             default:
532                 if(((etype == ASN_STRING_UTF8String) || !(ch & 0x80))
533                    && (ch >= 0x20)) {
534                     printf("%c", ch);
535                     break;
536                 }
537             /* Fall through */
538             case 0x3c:
539             case 0x3e:
540             case 0x26:
541                 printf("&#x%02x;", ch);
542             }
543             break;
544         case ASN_BASIC_BOOLEAN:
545             switch(ch) {
546             case 0:
547                 printf("<false/>");
548                 break;
549             case 0xff:
550                 printf("<true/>");
551                 break;
552             default:
553                 printf("<true value=\"&#x%02x\"/>", ch);
554             }
555             break;
556         case ASN_BASIC_INTEGER:
557         case ASN_BASIC_ENUMERATED:
558             if(i)
559                 collector = collector * 256 + ch;
560             else
561                 collector = (int)(signed char)ch;
562             break;
563         default:
564             if(vbuf) {
565                 vbuf[i] = ch;
566             } else {
567                 printf("&#x%02x;", ch);
568             }
569         }
570     }
571
572     /* Do post-processing */
573     switch(etype) {
574     case ASN_BASIC_INTEGER:
575     case ASN_BASIC_ENUMERATED:
576         printf("%s", asn1p_itoa(collector));
577         break;
578     case ASN_BASIC_OBJECT_IDENTIFIER:
579         if(vbuf) {
580             OBJECT_IDENTIFIER_t oid = {0, 0};
581             ssize_t arcno;
582
583             oid.buf = vbuf;
584             oid.size = tlv_len;
585
586             arcno = OBJECT_IDENTIFIER_get_arcs(&oid, arcs, tlv_len + 1);
587             if(arcno >= 0) {
588                 assert(arcno <= (tlv_len + 1));
589                 printf(" F>");
590                 for(i = 0; i < arcno; i++) {
591                     if(i) printf(".");
592                     printf("%" PRIu32, arcs[i]);
593                 }
594                 FREEMEM(vbuf);
595                 vbuf = 0;
596             }
597         }
598         break;
599     case ASN_BASIC_RELATIVE_OID:
600         if(vbuf) {
601             RELATIVE_OID_t oid;
602             int arcno;
603
604             oid.buf = vbuf;
605             oid.size = tlv_len;
606
607             arcno = RELATIVE_OID_get_arcs(&oid, arcs, tlv_len);
608             if(arcno >= 0) {
609                 assert(arcno <= tlv_len);
610                 printf(" F>");
611                 for(i = 0; i < arcno; i++) {
612                     if(i) printf(".");
613                     printf("%" PRIu32, arcs[i]);
614                 }
615                 FREEMEM(vbuf);
616                 vbuf = 0;
617             }
618         }
619         break;
620     default:
621         break;
622     }
623
624     /*
625      * If the buffer was not consumed, print it out.
626      * It might be an OCTET STRING or other primitive type,
627      * which might actually be printable, but we need to figure it out.
628      */
629     if(vbuf) {
630         int binary;
631
632         /*
633          * Check whether the data could be represented as text
634          */
635         binary = -1 * (tlv_len >> 3); /* Threshold is 12.5% binary */
636         for(i = 0; i < tlv_len; i++) {
637             switch(vbuf[i]) {
638             case 0x1b:
639                 binary = 1;
640                 break;
641             case 0x09:
642             case 0x0a:
643             case 0x0d:
644                 continue;
645             default:
646                 if(vbuf[i] < 0x20 || vbuf[i] >= 0x7f)
647                     if(++binary > 0) /* Way too many */
648                         break;
649                 continue;
650             }
651             break;
652         }
653         printf(">");
654         for(i = 0; i < tlv_len; i++) {
655             if(binary > 0 || vbuf[i] < 0x20 || vbuf[i] >= 0x7f
656                || vbuf[i] == 0x26 /* '&' */
657                || vbuf[i] == 0x3c /* '<' */
658                || vbuf[i] == 0x3e /* '>' */
659                )
660                 printf("&#x%02x;", vbuf[i]);
661             else
662                 printf("%c", vbuf[i]);
663         }
664         FREEMEM(vbuf);
665     }
666
667     if(arcs) FREEMEM(arcs);
668     return 0;
669 }
670
671
672 static int
673 decode_tlv_from_string(const char *datastring) {
674     unsigned char *data, *dp;
675     size_t dsize; /* Data size */
676     ssize_t len;
677     ber_tlv_tag_t tlv_tag;
678     ber_tlv_len_t tlv_len;
679     const char *p;
680     int half;
681
682     dsize = strlen(datastring) + 1;
683     dp = data = CALLOC(1, dsize);
684     assert(data);
685
686     for(half = 0, p = datastring; *p; p++) {
687         /* clang-format off */
688                 switch(*p) {
689                 case '0': case '1': case '2': case '3': case '4':
690                 case '5': case '6': case '7': case '8': case '9':
691                         *dp |= *p - '0'; break;
692                 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
693                         *dp |= *p - 'A' + 10; break;
694                 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
695                         *dp |= *p - 'a' + 10; break;
696                 case ' ': case '\t': case '\r': case '\n':
697                         continue;
698                 default:
699                         fprintf(stderr, "Unexpected symbols in data string:\n");
700                         fprintf(stderr, "%s\n", datastring);
701                         for(dp = data; datastring < p; datastring++, dp++)
702                                 *dp = ' ';
703                         *dp = '\0';
704                         fprintf(stderr, "%s^ <- here\n", (char *)data);
705                         return -1;
706                 }
707         /* clang-format on */
708         if(half)
709             dp++;
710         else
711             (*dp) <<= 4;
712         half = !half;
713     }
714
715     assert((size_t)(dp - data) <= dsize);
716     dsize = dp - data;
717
718     printf("BER: ");
719     for(dp = data; dp < data + dsize; dp++) printf("%02X", *dp);
720     printf("\n");
721
722     len = ber_fetch_tag(data, dsize, &tlv_tag);
723     switch(len) {
724     case -1:
725         fprintf(stderr, "TAG: Fatal error decoding tag\n");
726         return -1;
727     case 0:
728         fprintf(stderr, "TAG: More data expected\n");
729         return -1;
730     default:
731         printf("TAG: ");
732         ber_tlv_tag_fwrite(tlv_tag, stdout);
733         if(BER_TLV_CONSTRUCTED(data)) {
734             printf(" (constructed)");
735         } else if(dsize >= 2 && data[0] == 0 && data[1] == 0) {
736             printf(" (end-of-content)");
737         } else {
738             printf(" (primitive)");
739         }
740         if(BER_TAG_CLASS(tlv_tag) == ASN_TAG_CLASS_UNIVERSAL) {
741             const char *str;
742             ber_tlv_tag_t tvalue = BER_TAG_VALUE(tlv_tag);
743             str = ASN_UNIVERSAL_TAG2STR(tvalue);
744             if(str) printf(" \"%s\"", str);
745         }
746         printf("\n");
747     }
748
749     if(dsize > (size_t)len) {
750         len = ber_fetch_length(BER_TLV_CONSTRUCTED(data), data + len,
751                                dsize - len, &tlv_len);
752         switch(len) {
753         case -1:
754             fprintf(stderr, "LEN: Fatal error decoding length\n");
755             return -1;
756         case 0:
757             fprintf(stderr, "LEN: More data expected\n");
758             return -1;
759         default:
760             if(tlv_len == (ber_tlv_len_t)-1)
761                 printf("LEN: Indefinite length encoding\n");
762             else
763                 printf("LEN: %ld bytes\n", (long)tlv_len);
764         }
765     }
766
767     return 0;
768 }
769
770 /*
771  * Dummy functions.
772  */
773 asn_dec_rval_t
774 ber_check_tags(const asn_codec_ctx_t *opt_codec_ctx,
775                const asn_TYPE_descriptor_t *td, asn_struct_ctx_t *opt_ctx,
776                const void *ptr, size_t size, int tag_mode, int last_tag_form,
777                ber_tlv_len_t *last_length, int *opt_tlv_form) {
778     asn_dec_rval_t rv = {0, 0};
779     (void)opt_codec_ctx;
780     (void)td;
781     (void)opt_ctx;
782     (void)ptr;
783     (void)size;
784     (void)tag_mode;
785     (void)last_tag_form;
786     (void)last_length;
787     (void)opt_tlv_form;
788     return rv;
789 }
790
791 ssize_t
792 der_write_tags(const asn_TYPE_descriptor_t *td, size_t slen, int tag_mode,
793                int last_tag_form, ber_tlv_tag_t tag,
794                asn_app_consume_bytes_f *cb, void *app_key) {
795     (void)td;
796     (void)slen;
797     (void)tag_mode;
798     (void)last_tag_form;
799     (void)tag;
800     (void)cb;
801     (void)app_key;
802     return -1;
803 }
804
805 asn_dec_rval_t
806 xer_decode_general(const asn_codec_ctx_t *opt_codec_ctx, asn_struct_ctx_t *ctx,
807                    void *struct_key, const char *xml_tag, const void *buf_ptr,
808                    size_t size,
809                    int (*otd)(void *struct_key, const void *chunk_buf,
810                               size_t chunk_size),
811                    ssize_t (*br)(void *struct_key, const void *chunk_buf,
812                                  size_t chunk_size, int have_more)) {
813     asn_dec_rval_t rv = {0, 0};
814     (void)opt_codec_ctx;
815     (void)ctx;
816     (void)struct_key;
817     (void)xml_tag;
818     (void)buf_ptr;
819     (void)size;
820     (void)otd;
821     (void)br;
822     return rv;
823 }
824
825 size_t
826 xer_whitespace_span(const void *chunk_buf, size_t chunk_size) {
827     (void)chunk_buf;
828     (void)chunk_size;
829     return 0;
830 }
831
832 int
833 OCTET_STRING_compare(const asn_TYPE_descriptor_t *td, const void *a,
834                      const void *b) {
835     (void)td;
836     (void)a;
837     (void)b;
838     return 0;
839 }
840
841 intmax_t
842 asn_random_between(intmax_t a, intmax_t b) {
843     (void)b;
844     return a;
845 };
846