mirror of https://gerrit.osmocom.org/asn1c
weeded the library out of unber program
This commit is contained in:
parent
8e1d7591ab
commit
ccfaf2ab89
|
@ -36,8 +36,8 @@ stamp-h*
|
|||
/asn1c/asn1c
|
||||
|
||||
# /asn1c-tools
|
||||
/asn1-tools/enber
|
||||
/asn1-tools/unber
|
||||
/asn1-tools/enber/enber
|
||||
/asn1-tools/unber/unber
|
||||
|
||||
# /skeletons
|
||||
/skeletons/check-*
|
||||
|
|
|
@ -6,6 +6,12 @@ AM_CPPFLAGS = \
|
|||
-I${top_srcdir}/libasn1parser \
|
||||
-I${top_srcdir}/skeletons
|
||||
|
||||
unber_LDADD = $(top_builddir)/libasn1common/libasn1common.la
|
||||
noinst_LTLIBRARIES = libasn1-unber-tool.la
|
||||
|
||||
libasn1_unber_tool_la_SOURCES = \
|
||||
libasn1_unber_tool.c libasn1_unber_tool.h
|
||||
|
||||
unber_LDADD = libasn1-unber-tool.la \
|
||||
$(top_builddir)/libasn1common/libasn1common.la
|
||||
|
||||
bin_PROGRAMS = unber
|
||||
|
|
|
@ -0,0 +1,754 @@
|
|||
/*
|
||||
* Copyright (c) 2004, 2005, 2006 Lev Walkin <vlm@lionet.info>.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
#include "asn1_common.h"
|
||||
#include "libasn1_unber_tool.h"
|
||||
|
||||
#define ASN_DISABLE_PER_SUPPORT 1
|
||||
#define ASN_DISABLE_OER_SUPPORT 1
|
||||
|
||||
#include <asn1parser.h> /* For static string tables */
|
||||
|
||||
#include <asn_application.h>
|
||||
#include <constraints.c>
|
||||
#include <ber_tlv_tag.c>
|
||||
#include <ber_tlv_length.c>
|
||||
#include <INTEGER.c>
|
||||
#include <OBJECT_IDENTIFIER.c>
|
||||
#include <RELATIVE-OID.c>
|
||||
#include <asn_codecs_prim.c>
|
||||
#include <asn1p_integer.c>
|
||||
#include <asn_internal.c>
|
||||
|
||||
static int single_type_decoding = 0; /* -1 enables that */
|
||||
static int minimalistic = 0; /* -m enables that */
|
||||
static int pretty_printing = 1; /* -p disables that */
|
||||
static long skip_bytes = 0; /* -s controls that */
|
||||
static char indent_bytes[16] = " "; /* -i controls that */
|
||||
|
||||
void set_minimalistic_output(int v) { minimalistic = v; }
|
||||
void set_single_type_decoding(int v) { single_type_decoding = v; }
|
||||
void set_pretty_printing(int v) { pretty_printing = v; }
|
||||
int set_skip_bytes(long v) { if(v < 0) return -1; skip_bytes = v; return 0; }
|
||||
int set_indent_size(int indent_size) {
|
||||
if(indent_size < 0 || indent_size >= (int)sizeof(indent_bytes)) {
|
||||
return -1;
|
||||
}
|
||||
memset(indent_bytes, ' ', indent_size);
|
||||
indent_bytes[indent_size] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef enum pd_code {
|
||||
PD_FAILED = -1,
|
||||
PD_FINISHED = 0,
|
||||
PD_EOF = 1,
|
||||
} pd_code_e;
|
||||
static pd_code_e process_deeper(const char *fname, FILE *fp,
|
||||
size_t *offset, int level,
|
||||
ssize_t limit, ber_tlv_len_t *frame_size,
|
||||
ber_tlv_len_t effective_size, int expect_eoc);
|
||||
static void print_TL(int fin, size_t offset, int level, int constr,
|
||||
ssize_t tlen, ber_tlv_tag_t, ber_tlv_len_t,
|
||||
ber_tlv_len_t effective_frame_size);
|
||||
static int print_V(const char *fname, FILE *fp, ber_tlv_tag_t, ber_tlv_len_t);
|
||||
|
||||
/*
|
||||
* Open the file and initiate recursive processing.
|
||||
*/
|
||||
static int
|
||||
unber_stream(const char *fname, FILE *fp) {
|
||||
pd_code_e pdc;
|
||||
size_t offset = 0; /* Stream decoding position */
|
||||
ber_tlv_len_t frame_size = 0; /* Single frame size */
|
||||
|
||||
/*
|
||||
* Skip the requested amount of bytes.
|
||||
*/
|
||||
for(; offset < (size_t)skip_bytes; offset++) {
|
||||
if(fgetc(fp) == -1) {
|
||||
fprintf(stderr, "%s: input source (%zu bytes) "
|
||||
"has less data than \"-s %ld\" switch "
|
||||
"wants to skip\n",
|
||||
fname, offset, skip_bytes);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Fetch out BER-encoded data until EOF or error.
|
||||
*/
|
||||
do {
|
||||
pdc = process_deeper(fname, fp, &offset, 0, -1, &frame_size, 0, 0);
|
||||
} while(pdc == PD_FINISHED && !single_type_decoding);
|
||||
|
||||
if(pdc == PD_FAILED) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Process the TLV recursively.
|
||||
*/
|
||||
static pd_code_e
|
||||
process_deeper(const char *fname, FILE *fp, size_t *offset, int level,
|
||||
ssize_t limit, ber_tlv_len_t *frame_size,
|
||||
ber_tlv_len_t effective_size, int expect_eoc) {
|
||||
unsigned char tagbuf[32];
|
||||
ssize_t tblen = 0;
|
||||
pd_code_e pdc = PD_FINISHED;
|
||||
ber_tlv_tag_t tlv_tag;
|
||||
ber_tlv_len_t tlv_len;
|
||||
ssize_t t_len;
|
||||
ssize_t l_len;
|
||||
|
||||
for(;;) {
|
||||
ber_tlv_len_t local_esize = 0;
|
||||
int constr;
|
||||
int ch;
|
||||
|
||||
if(limit == 0) return PD_FINISHED;
|
||||
|
||||
if(limit >= 0 && tblen >= limit) {
|
||||
fprintf(stderr,
|
||||
"%s: Too long TL sequence (%ld >= %ld)"
|
||||
" at %zu. "
|
||||
"Broken or maliciously constructed file\n",
|
||||
fname, (long)tblen, (long)limit, *offset);
|
||||
return PD_FAILED;
|
||||
}
|
||||
|
||||
/* Get the next byte from the input stream */
|
||||
ch = fgetc(fp);
|
||||
if(ch == -1) {
|
||||
if(limit > 0 || expect_eoc) {
|
||||
fprintf(stderr,
|
||||
"%s: Unexpected end of file (TL)"
|
||||
" at %zu\n",
|
||||
fname, *offset);
|
||||
return PD_FAILED;
|
||||
} else {
|
||||
return PD_EOF;
|
||||
}
|
||||
}
|
||||
|
||||
tagbuf[tblen++] = ch;
|
||||
|
||||
/*
|
||||
* Decode the TLV tag.
|
||||
*/
|
||||
t_len = ber_fetch_tag(tagbuf, tblen, &tlv_tag);
|
||||
switch(t_len) {
|
||||
case -1:
|
||||
fprintf(stderr,
|
||||
"%s: Fatal error decoding tag"
|
||||
" at %zu+%ld\n",
|
||||
fname, *offset, (long)tblen);
|
||||
return PD_FAILED;
|
||||
case 0:
|
||||
/* More data expected */
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Decode the TLV length.
|
||||
*/
|
||||
constr = BER_TLV_CONSTRUCTED(tagbuf);
|
||||
l_len =
|
||||
ber_fetch_length(constr, tagbuf + t_len, tblen - t_len, &tlv_len);
|
||||
switch(l_len) {
|
||||
case -1:
|
||||
fprintf(stderr,
|
||||
"%s: Fatal error decoding value length"
|
||||
" at %zu\n",
|
||||
fname, *offset + t_len);
|
||||
return PD_FAILED;
|
||||
case 0:
|
||||
/* More data expected */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Make sure the T & L decoders took exactly the whole buffer */
|
||||
assert((t_len + l_len) == tblen);
|
||||
|
||||
if(!expect_eoc || tagbuf[0] || tagbuf[1])
|
||||
print_TL(0, *offset, level, constr, tblen, tlv_tag, tlv_len,
|
||||
effective_size);
|
||||
|
||||
if(limit != -1) {
|
||||
/* If limit is set, account for the TL sequence */
|
||||
limit -= (t_len + l_len);
|
||||
assert(limit >= 0);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
*offset += t_len + l_len;
|
||||
*frame_size += t_len + l_len;
|
||||
effective_size += t_len + l_len;
|
||||
local_esize += t_len + l_len;
|
||||
|
||||
if(expect_eoc && !tagbuf[0] && !tagbuf[1]) {
|
||||
/* End of content octets */
|
||||
print_TL(1, *offset - 2, level - 1, 1, 2, 0, -1, effective_size);
|
||||
return PD_FINISHED;
|
||||
}
|
||||
|
||||
if(constr) {
|
||||
ber_tlv_len_t dec = 0;
|
||||
/*
|
||||
* This is a constructed type. Process recursively.
|
||||
*/
|
||||
printf(">\n"); /* Close the opening tag */
|
||||
if(tlv_len != -1 && limit != -1) {
|
||||
assert(limit >= tlv_len);
|
||||
}
|
||||
pdc = process_deeper(fname, fp, offset, level + 1,
|
||||
tlv_len == -1 ? limit : tlv_len, &dec,
|
||||
t_len + l_len, tlv_len == -1);
|
||||
if(pdc == PD_FAILED) return pdc;
|
||||
if(limit != -1) {
|
||||
assert(limit >= dec);
|
||||
limit -= dec;
|
||||
}
|
||||
*frame_size += dec;
|
||||
effective_size += dec;
|
||||
local_esize += dec;
|
||||
if(tlv_len == -1) {
|
||||
tblen = 0;
|
||||
if(pdc == PD_FINISHED && limit < 0 && !expect_eoc) return pdc;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
assert(tlv_len >= 0);
|
||||
if(print_V(fname, fp, tlv_tag, tlv_len)) return PD_FAILED;
|
||||
|
||||
if(limit != -1) {
|
||||
assert(limit >= tlv_len);
|
||||
limit -= tlv_len;
|
||||
}
|
||||
*offset += tlv_len;
|
||||
*frame_size += tlv_len;
|
||||
effective_size += tlv_len;
|
||||
local_esize += tlv_len;
|
||||
}
|
||||
|
||||
print_TL(1, *offset, level, constr, tblen, tlv_tag, tlv_len,
|
||||
local_esize);
|
||||
|
||||
tblen = 0;
|
||||
|
||||
/* Report success for a single top level TLV */
|
||||
if(level == 0 && limit == -1 && !expect_eoc) break;
|
||||
} /* for(;;) */
|
||||
|
||||
return pdc;
|
||||
}
|
||||
|
||||
static void
|
||||
print_TL(int fin, size_t offset, int level, int constr, ssize_t tlen,
|
||||
ber_tlv_tag_t tlv_tag, ber_tlv_len_t tlv_len,
|
||||
ber_tlv_len_t effective_size) {
|
||||
if(fin && !constr) {
|
||||
printf("</P>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
while(level-- > 0) fputs(indent_bytes, stdout); /* Print indent */
|
||||
printf(fin ? "</" : "<");
|
||||
|
||||
printf(constr ? ((tlv_len == -1) ? "I" : "C") : "P");
|
||||
|
||||
/* Print out the offset of this boundary, even if closing tag */
|
||||
if(!minimalistic) printf(" O=\"%zu\"", offset);
|
||||
|
||||
printf(" T=\"");
|
||||
ber_tlv_tag_fwrite(tlv_tag, stdout);
|
||||
printf("\"");
|
||||
|
||||
if(!fin || (tlv_len == -1 && !minimalistic))
|
||||
printf(" TL=\"%ld\"", (long)tlen);
|
||||
if(!fin) {
|
||||
if(tlv_len == -1)
|
||||
printf(" V=\"Indefinite\"");
|
||||
else
|
||||
printf(" V=\"%ld\"", (long)tlv_len);
|
||||
}
|
||||
|
||||
if(!minimalistic && BER_TAG_CLASS(tlv_tag) == ASN_TAG_CLASS_UNIVERSAL) {
|
||||
const char *str;
|
||||
ber_tlv_tag_t tvalue = BER_TAG_VALUE(tlv_tag);
|
||||
str = ASN_UNIVERSAL_TAG2STR(tvalue);
|
||||
if(str) printf(" A=\"%s\"", str);
|
||||
}
|
||||
|
||||
if(fin) {
|
||||
if(constr && !minimalistic) printf(" L=\"%ld\"", (long)effective_size);
|
||||
printf(">\n");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Print the value in binary form, or reformat for pretty-printing.
|
||||
*/
|
||||
static int
|
||||
print_V(const char *fname, FILE *fp, ber_tlv_tag_t tlv_tag,
|
||||
ber_tlv_len_t tlv_len) {
|
||||
asn_oid_arc_t *arcs = 0; /* Object identifier arcs */
|
||||
unsigned char *vbuf = 0;
|
||||
asn1p_expr_type_e etype = 0;
|
||||
asn1c_integer_t collector = 0;
|
||||
int special_format = 0;
|
||||
ssize_t i;
|
||||
|
||||
/* Figure out what type is it */
|
||||
if(BER_TAG_CLASS(tlv_tag) == ASN_TAG_CLASS_UNIVERSAL && pretty_printing) {
|
||||
ber_tlv_tag_t tvalue = BER_TAG_VALUE(tlv_tag);
|
||||
etype = ASN_UNIVERSAL_TAG2TYPE(tvalue);
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine how to print the value, either in its native binary form,
|
||||
* encoded with &xNN characters, or using pretty-printing.
|
||||
* The basic string types (including "useful types", like UTCTime)
|
||||
* are excempt from this determination logic, because their alphabets
|
||||
* are subsets of the XML's native UTF-8 encoding.
|
||||
*/
|
||||
switch(etype) {
|
||||
case ASN_BASIC_BOOLEAN:
|
||||
if(tlv_len == 1)
|
||||
special_format = 1;
|
||||
else
|
||||
etype = 0;
|
||||
break;
|
||||
case ASN_BASIC_INTEGER:
|
||||
case ASN_BASIC_ENUMERATED:
|
||||
if((size_t)tlv_len <= sizeof(collector))
|
||||
special_format = 1;
|
||||
else
|
||||
etype = 0;
|
||||
break;
|
||||
case ASN_BASIC_OBJECT_IDENTIFIER:
|
||||
case ASN_BASIC_RELATIVE_OID:
|
||||
if(tlv_len > 0 && tlv_len < 128 * 1024 /* VERY long OID! */) {
|
||||
arcs = MALLOC(sizeof(*arcs) * (tlv_len + 1));
|
||||
if(arcs) {
|
||||
vbuf = MALLOC(tlv_len + 1);
|
||||
/* Not checking is intentional */
|
||||
}
|
||||
}
|
||||
case ASN_BASIC_UTCTime:
|
||||
case ASN_BASIC_GeneralizedTime:
|
||||
case ASN_STRING_NumericString:
|
||||
case ASN_STRING_PrintableString:
|
||||
case ASN_STRING_VisibleString:
|
||||
case ASN_STRING_IA5String:
|
||||
case ASN_STRING_UTF8String:
|
||||
break; /* Directly compatible with UTF-8 */
|
||||
case ASN_STRING_BMPString:
|
||||
case ASN_STRING_UniversalString:
|
||||
break; /* Not directly compatible with UTF-8 */
|
||||
default:
|
||||
/* Conditionally compatible with UTF-8 */
|
||||
if(((etype & ASN_STRING_MASK) || (etype == ASN_BASIC_OCTET_STRING) ||
|
||||
/*
|
||||
* AUTOMATIC TAGS or IMPLICIT TAGS in effect,
|
||||
* Treat this primitive type as OCTET_STRING.
|
||||
*/
|
||||
(BER_TAG_CLASS(tlv_tag) != ASN_TAG_CLASS_UNIVERSAL
|
||||
&& pretty_printing))
|
||||
&& (tlv_len > 0 && tlv_len < 128 * 1024)) {
|
||||
vbuf = MALLOC(tlv_len + 1);
|
||||
/* Not checking is intentional */
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* If collection vbuf is present, defer printing the F flag. */
|
||||
if(!vbuf) printf(special_format ? " F>" : ">");
|
||||
|
||||
/*
|
||||
* Print the value in binary or text form,
|
||||
* or collect the bytes into vbuf.
|
||||
*/
|
||||
for(i = 0; i < tlv_len; i++) {
|
||||
int ch = fgetc(fp);
|
||||
if(ch == -1) {
|
||||
fprintf(stderr, "%s: Unexpected end of file (V)\n", fname);
|
||||
if(vbuf) FREEMEM(vbuf);
|
||||
if(arcs) FREEMEM(arcs);
|
||||
return -1;
|
||||
}
|
||||
switch(etype) {
|
||||
case ASN_BASIC_UTCTime:
|
||||
case ASN_BASIC_GeneralizedTime:
|
||||
case ASN_STRING_NumericString:
|
||||
case ASN_STRING_PrintableString:
|
||||
case ASN_STRING_VisibleString:
|
||||
case ASN_STRING_IA5String:
|
||||
case ASN_STRING_UTF8String:
|
||||
switch(ch) {
|
||||
default:
|
||||
if(((etype == ASN_STRING_UTF8String) || !(ch & 0x80))
|
||||
&& (ch >= 0x20)) {
|
||||
printf("%c", ch);
|
||||
break;
|
||||
}
|
||||
/* Fall through */
|
||||
case 0x3c:
|
||||
case 0x3e:
|
||||
case 0x26:
|
||||
printf("&#x%02x;", ch);
|
||||
}
|
||||
break;
|
||||
case ASN_BASIC_BOOLEAN:
|
||||
switch(ch) {
|
||||
case 0:
|
||||
printf("<false/>");
|
||||
break;
|
||||
case 0xff:
|
||||
printf("<true/>");
|
||||
break;
|
||||
default:
|
||||
printf("<true value=\"&#x%02x\"/>", ch);
|
||||
}
|
||||
break;
|
||||
case ASN_BASIC_INTEGER:
|
||||
case ASN_BASIC_ENUMERATED:
|
||||
if(i)
|
||||
collector = collector * 256 + ch;
|
||||
else
|
||||
collector = (int)(signed char)ch;
|
||||
break;
|
||||
default:
|
||||
if(vbuf) {
|
||||
vbuf[i] = ch;
|
||||
} else {
|
||||
printf("&#x%02x;", ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Do post-processing */
|
||||
switch(etype) {
|
||||
case ASN_BASIC_INTEGER:
|
||||
case ASN_BASIC_ENUMERATED:
|
||||
printf("%s", asn1p_itoa(collector));
|
||||
break;
|
||||
case ASN_BASIC_OBJECT_IDENTIFIER:
|
||||
if(vbuf) {
|
||||
OBJECT_IDENTIFIER_t oid = {0, 0};
|
||||
ssize_t arcno;
|
||||
|
||||
oid.buf = vbuf;
|
||||
oid.size = tlv_len;
|
||||
|
||||
arcno = OBJECT_IDENTIFIER_get_arcs(&oid, arcs, tlv_len + 1);
|
||||
if(arcno >= 0) {
|
||||
assert(arcno <= (tlv_len + 1));
|
||||
printf(" F>");
|
||||
for(i = 0; i < arcno; i++) {
|
||||
if(i) printf(".");
|
||||
printf("%" PRIu32, arcs[i]);
|
||||
}
|
||||
FREEMEM(vbuf);
|
||||
vbuf = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ASN_BASIC_RELATIVE_OID:
|
||||
if(vbuf) {
|
||||
RELATIVE_OID_t oid;
|
||||
int arcno;
|
||||
|
||||
oid.buf = vbuf;
|
||||
oid.size = tlv_len;
|
||||
|
||||
arcno = RELATIVE_OID_get_arcs(&oid, arcs, tlv_len);
|
||||
if(arcno >= 0) {
|
||||
assert(arcno <= tlv_len);
|
||||
printf(" F>");
|
||||
for(i = 0; i < arcno; i++) {
|
||||
if(i) printf(".");
|
||||
printf("%" PRIu32, arcs[i]);
|
||||
}
|
||||
FREEMEM(vbuf);
|
||||
vbuf = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the buffer was not consumed, print it out.
|
||||
* It might be an OCTET STRING or other primitive type,
|
||||
* which might actually be printable, but we need to figure it out.
|
||||
*/
|
||||
if(vbuf) {
|
||||
int binary;
|
||||
|
||||
/*
|
||||
* Check whether the data could be represented as text
|
||||
*/
|
||||
binary = -1 * (tlv_len >> 3); /* Threshold is 12.5% binary */
|
||||
for(i = 0; i < tlv_len; i++) {
|
||||
switch(vbuf[i]) {
|
||||
case 0x1b:
|
||||
binary = 1;
|
||||
break;
|
||||
case 0x09:
|
||||
case 0x0a:
|
||||
case 0x0d:
|
||||
continue;
|
||||
default:
|
||||
if(vbuf[i] < 0x20 || vbuf[i] >= 0x7f)
|
||||
if(++binary > 0) /* Way too many */
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
printf(">");
|
||||
for(i = 0; i < tlv_len; i++) {
|
||||
if(binary > 0 || vbuf[i] < 0x20 || vbuf[i] >= 0x7f
|
||||
|| vbuf[i] == 0x26 /* '&' */
|
||||
|| vbuf[i] == 0x3c /* '<' */
|
||||
|| vbuf[i] == 0x3e /* '>' */
|
||||
)
|
||||
printf("&#x%02x;", vbuf[i]);
|
||||
else
|
||||
printf("%c", vbuf[i]);
|
||||
}
|
||||
FREEMEM(vbuf);
|
||||
}
|
||||
|
||||
if(arcs) FREEMEM(arcs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
unber_file(const char *fname) {
|
||||
FILE *fp;
|
||||
|
||||
if(strcmp(fname, "-")) {
|
||||
fp = fopen(fname, "rb");
|
||||
if(!fp) {
|
||||
perror(fname);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
fp = stdin;
|
||||
}
|
||||
|
||||
int ret = unber_stream(fname, fp);
|
||||
|
||||
if(fp != stdin) fclose(fp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
decode_tlv_from_hex_string(const char *datastring) {
|
||||
unsigned char *data, *dp;
|
||||
size_t dsize; /* Data size */
|
||||
ssize_t len;
|
||||
ber_tlv_tag_t tlv_tag;
|
||||
ber_tlv_len_t tlv_len;
|
||||
const char *p;
|
||||
int half;
|
||||
|
||||
dsize = strlen(datastring) + 1;
|
||||
dp = data = CALLOC(1, dsize);
|
||||
assert(data);
|
||||
|
||||
for(half = 0, p = datastring; *p; p++) {
|
||||
/* clang-format off */
|
||||
switch(*p) {
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
*dp |= *p - '0'; break;
|
||||
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
|
||||
*dp |= *p - 'A' + 10; break;
|
||||
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
|
||||
*dp |= *p - 'a' + 10; break;
|
||||
case ' ': case '\t': case '\r': case '\n':
|
||||
continue;
|
||||
default:
|
||||
fprintf(stderr, "Unexpected symbols in data string:\n");
|
||||
fprintf(stderr, "%s\n", datastring);
|
||||
for(dp = data; datastring < p; datastring++, dp++)
|
||||
*dp = ' ';
|
||||
*dp = '\0';
|
||||
fprintf(stderr, "%s^ <- here\n", (char *)data);
|
||||
return -1;
|
||||
}
|
||||
/* clang-format on */
|
||||
if(half)
|
||||
dp++;
|
||||
else
|
||||
(*dp) <<= 4;
|
||||
half = !half;
|
||||
}
|
||||
|
||||
assert((size_t)(dp - data) <= dsize);
|
||||
dsize = dp - data;
|
||||
|
||||
printf("BER: ");
|
||||
for(dp = data; dp < data + dsize; dp++) printf("%02X", *dp);
|
||||
printf("\n");
|
||||
|
||||
len = ber_fetch_tag(data, dsize, &tlv_tag);
|
||||
switch(len) {
|
||||
case -1:
|
||||
fprintf(stderr, "TAG: Fatal error decoding tag\n");
|
||||
return -1;
|
||||
case 0:
|
||||
fprintf(stderr, "TAG: More data expected\n");
|
||||
return -1;
|
||||
default:
|
||||
printf("TAG: ");
|
||||
ber_tlv_tag_fwrite(tlv_tag, stdout);
|
||||
if(BER_TLV_CONSTRUCTED(data)) {
|
||||
printf(" (constructed)");
|
||||
} else if(dsize >= 2 && data[0] == 0 && data[1] == 0) {
|
||||
printf(" (end-of-content)");
|
||||
} else {
|
||||
printf(" (primitive)");
|
||||
}
|
||||
if(BER_TAG_CLASS(tlv_tag) == ASN_TAG_CLASS_UNIVERSAL) {
|
||||
const char *str;
|
||||
ber_tlv_tag_t tvalue = BER_TAG_VALUE(tlv_tag);
|
||||
str = ASN_UNIVERSAL_TAG2STR(tvalue);
|
||||
if(str) printf(" \"%s\"", str);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
if(dsize > (size_t)len) {
|
||||
len = ber_fetch_length(BER_TLV_CONSTRUCTED(data), data + len,
|
||||
dsize - len, &tlv_len);
|
||||
switch(len) {
|
||||
case -1:
|
||||
fprintf(stderr, "LEN: Fatal error decoding length\n");
|
||||
return -1;
|
||||
case 0:
|
||||
fprintf(stderr, "LEN: More data expected\n");
|
||||
return -1;
|
||||
default:
|
||||
if(tlv_len == (ber_tlv_len_t)-1)
|
||||
printf("LEN: Indefinite length encoding\n");
|
||||
else
|
||||
printf("LEN: %ld bytes\n", (long)tlv_len);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Dummy functions.
|
||||
*/
|
||||
asn_dec_rval_t
|
||||
ber_check_tags(const asn_codec_ctx_t *opt_codec_ctx,
|
||||
const asn_TYPE_descriptor_t *td, asn_struct_ctx_t *opt_ctx,
|
||||
const void *ptr, size_t size, int tag_mode, int last_tag_form,
|
||||
ber_tlv_len_t *last_length, int *opt_tlv_form) {
|
||||
asn_dec_rval_t rv = {0, 0};
|
||||
(void)opt_codec_ctx;
|
||||
(void)td;
|
||||
(void)opt_ctx;
|
||||
(void)ptr;
|
||||
(void)size;
|
||||
(void)tag_mode;
|
||||
(void)last_tag_form;
|
||||
(void)last_length;
|
||||
(void)opt_tlv_form;
|
||||
return rv;
|
||||
}
|
||||
|
||||
ssize_t
|
||||
der_write_tags(const asn_TYPE_descriptor_t *td, size_t slen, int tag_mode,
|
||||
int last_tag_form, ber_tlv_tag_t tag,
|
||||
asn_app_consume_bytes_f *cb, void *app_key) {
|
||||
(void)td;
|
||||
(void)slen;
|
||||
(void)tag_mode;
|
||||
(void)last_tag_form;
|
||||
(void)tag;
|
||||
(void)cb;
|
||||
(void)app_key;
|
||||
return -1;
|
||||
}
|
||||
|
||||
asn_dec_rval_t
|
||||
xer_decode_general(const asn_codec_ctx_t *opt_codec_ctx, asn_struct_ctx_t *ctx,
|
||||
void *struct_key, const char *xml_tag, const void *buf_ptr,
|
||||
size_t size,
|
||||
int (*otd)(void *struct_key, const void *chunk_buf,
|
||||
size_t chunk_size),
|
||||
ssize_t (*br)(void *struct_key, const void *chunk_buf,
|
||||
size_t chunk_size, int have_more)) {
|
||||
asn_dec_rval_t rv = {0, 0};
|
||||
(void)opt_codec_ctx;
|
||||
(void)ctx;
|
||||
(void)struct_key;
|
||||
(void)xml_tag;
|
||||
(void)buf_ptr;
|
||||
(void)size;
|
||||
(void)otd;
|
||||
(void)br;
|
||||
return rv;
|
||||
}
|
||||
|
||||
size_t
|
||||
xer_whitespace_span(const void *chunk_buf, size_t chunk_size) {
|
||||
(void)chunk_buf;
|
||||
(void)chunk_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
OCTET_STRING_compare(const asn_TYPE_descriptor_t *td, const void *a,
|
||||
const void *b) {
|
||||
(void)td;
|
||||
(void)a;
|
||||
(void)b;
|
||||
return 0;
|
||||
}
|
||||
|
||||
intmax_t
|
||||
asn_random_between(intmax_t a, intmax_t b) {
|
||||
(void)b;
|
||||
return a;
|
||||
};
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright (c) 2004-2019 Lev Walkin <vlm@lionet.info>.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Set global options. */
|
||||
void set_minimalistic_output(int v);
|
||||
void set_single_type_decoding(int v);
|
||||
void set_pretty_printing(int v);
|
||||
int set_skip_bytes(long v);
|
||||
int set_indent_size(int indent_size);
|
||||
|
||||
/*
|
||||
* Convert BER-encoded file into the low level non-standard XML-like structure.
|
||||
* Primarily used for manual debugging.
|
||||
*/
|
||||
int unber_file(const char *fname);
|
||||
|
||||
/*
|
||||
* Decode the TLV given by the given string.
|
||||
*/
|
||||
int decode_tlv_from_hex_string(const char *datastring);
|
||||
|
|
@ -25,73 +25,45 @@
|
|||
*
|
||||
*/
|
||||
#include "asn1_common.h"
|
||||
|
||||
#define ASN_DISABLE_PER_SUPPORT 1
|
||||
#define ASN_DISABLE_OER_SUPPORT 1
|
||||
|
||||
#include <asn1parser.h> /* For static string tables */
|
||||
|
||||
#include <asn_application.h>
|
||||
#include <constraints.c>
|
||||
#include <ber_tlv_tag.c>
|
||||
#include <ber_tlv_length.c>
|
||||
#include <INTEGER.c>
|
||||
#include <OBJECT_IDENTIFIER.c>
|
||||
#include <RELATIVE-OID.c>
|
||||
#include <asn_codecs_prim.c>
|
||||
#include <asn1p_integer.c>
|
||||
#include <asn_internal.c>
|
||||
#include "libasn1_unber_tool.h"
|
||||
|
||||
#undef COPYRIGHT
|
||||
#define COPYRIGHT "Copyright (c) 2004, 2005 Lev Walkin <vlm@lionet.info>\n"
|
||||
#define COPYRIGHT "Copyright (c) 2004-2019 Lev Walkin <vlm@lionet.info>\n"
|
||||
|
||||
static void usage(const char *av0); /* Print the Usage screen and exit */
|
||||
static int process(const char *fname); /* Perform the BER decoding */
|
||||
static int decode_tlv_from_string(const char *datastring);
|
||||
|
||||
static int single_type_decoding = 0; /* -1 enables that */
|
||||
static int minimalistic = 0; /* -m enables that */
|
||||
static int pretty_printing = 1; /* -p disables that */
|
||||
static long skip_bytes = 0; /* -s controls that */
|
||||
static char indent_bytes[16] = " "; /* -i controls that */
|
||||
|
||||
int
|
||||
main(int ac, char **av) {
|
||||
int ch; /* Command line character */
|
||||
int i; /* Index in some loops */
|
||||
|
||||
/*
|
||||
* Process command-line options.
|
||||
*/
|
||||
while((ch = getopt(ac, av, "1hi:mps:t:v")) != -1) switch(ch) {
|
||||
while((ch = getopt(ac, av, "1hi:mps:t:v")) != -1) {
|
||||
switch(ch) {
|
||||
case '1':
|
||||
single_type_decoding = 1;
|
||||
set_single_type_decoding(1);
|
||||
break;
|
||||
case 'i':
|
||||
i = atoi(optarg);
|
||||
if(i >= 0 && i < (int)sizeof(indent_bytes)) {
|
||||
memset(indent_bytes, ' ', i);
|
||||
indent_bytes[i] = '\0';
|
||||
} else {
|
||||
if(set_indent_size(atoi(optarg)) != 0) {
|
||||
fprintf(stderr, "-i %s: Invalid indent value\n", optarg);
|
||||
exit(EX_USAGE);
|
||||
}
|
||||
break;
|
||||
case 'm':
|
||||
minimalistic = 1;
|
||||
set_minimalistic_output(1);
|
||||
break;
|
||||
case 'p':
|
||||
pretty_printing = 0;
|
||||
set_pretty_printing(0);
|
||||
break;
|
||||
case 's':
|
||||
skip_bytes = atol(optarg);
|
||||
if(skip_bytes < 0) {
|
||||
if(set_skip_bytes(atol(optarg)) != 0) {
|
||||
fprintf(stderr, "-s %s: positive value expected\n", optarg);
|
||||
exit(EX_USAGE);
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
if(decode_tlv_from_string(optarg)) exit(EX_DATAERR);
|
||||
if(decode_tlv_from_hex_string(optarg)) exit(EX_DATAERR);
|
||||
exit(0);
|
||||
case 'v':
|
||||
fprintf(stderr, "ASN.1 BER Decoder, v" VERSION "\n" COPYRIGHT);
|
||||
|
@ -101,6 +73,7 @@ main(int ac, char **av) {
|
|||
default:
|
||||
usage(av[0]);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensure that there are some input files present.
|
||||
|
@ -119,8 +92,8 @@ main(int ac, char **av) {
|
|||
* Iterate over input files and parse each.
|
||||
* All syntax trees from all files will be bundled together.
|
||||
*/
|
||||
for(i = 0; i < ac; i++) {
|
||||
if(process(av[i])) exit(EX_DATAERR);
|
||||
for(int i = 0; i < ac; i++) {
|
||||
if(unber_file(av[i])) exit(EX_DATAERR);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -159,687 +132,3 @@ usage(const char *av0) {
|
|||
exit(EX_USAGE);
|
||||
}
|
||||
|
||||
typedef enum pd_code {
|
||||
PD_FAILED = -1,
|
||||
PD_FINISHED = 0,
|
||||
PD_EOF = 1,
|
||||
} pd_code_e;
|
||||
static pd_code_e process_deeper(const char *fname, FILE *fp,
|
||||
size_t *offset, int level,
|
||||
ssize_t limit, ber_tlv_len_t *frame_size,
|
||||
ber_tlv_len_t effective_size, int expect_eoc);
|
||||
static void print_TL(int fin, size_t offset, int level, int constr,
|
||||
ssize_t tlen, ber_tlv_tag_t, ber_tlv_len_t,
|
||||
ber_tlv_len_t effective_frame_size);
|
||||
static int print_V(const char *fname, FILE *fp, ber_tlv_tag_t, ber_tlv_len_t);
|
||||
|
||||
/*
|
||||
* Open the file and initiate recursive processing.
|
||||
*/
|
||||
static int
|
||||
process(const char *fname) {
|
||||
FILE *fp;
|
||||
pd_code_e pdc;
|
||||
size_t offset = 0; /* Stream decoding position */
|
||||
ber_tlv_len_t frame_size = 0; /* Single frame size */
|
||||
|
||||
if(strcmp(fname, "-")) {
|
||||
fp = fopen(fname, "rb");
|
||||
if(!fp) {
|
||||
perror(fname);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
fp = stdin;
|
||||
}
|
||||
|
||||
/*
|
||||
* Skip the requested amount of bytes.
|
||||
*/
|
||||
for(; offset < (size_t)skip_bytes; offset++) {
|
||||
if(fgetc(fp) == -1) {
|
||||
fprintf(stderr, "%s: input source (%zu bytes) "
|
||||
"has less data than \"-s %ld\" switch "
|
||||
"wants to skip\n",
|
||||
fname, offset, skip_bytes);
|
||||
if(fp != stdin) fclose(fp);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Fetch out BER-encoded data until EOF or error.
|
||||
*/
|
||||
do {
|
||||
pdc = process_deeper(fname, fp, &offset, 0, -1, &frame_size, 0, 0);
|
||||
} while(pdc == PD_FINISHED && !single_type_decoding);
|
||||
|
||||
if(fp != stdin) fclose(fp);
|
||||
|
||||
if(pdc == PD_FAILED) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Process the TLV recursively.
|
||||
*/
|
||||
static pd_code_e
|
||||
process_deeper(const char *fname, FILE *fp, size_t *offset, int level,
|
||||
ssize_t limit, ber_tlv_len_t *frame_size,
|
||||
ber_tlv_len_t effective_size, int expect_eoc) {
|
||||
unsigned char tagbuf[32];
|
||||
ssize_t tblen = 0;
|
||||
pd_code_e pdc = PD_FINISHED;
|
||||
ber_tlv_tag_t tlv_tag;
|
||||
ber_tlv_len_t tlv_len;
|
||||
ssize_t t_len;
|
||||
ssize_t l_len;
|
||||
|
||||
for(;;) {
|
||||
ber_tlv_len_t local_esize = 0;
|
||||
int constr;
|
||||
int ch;
|
||||
|
||||
if(limit == 0) return PD_FINISHED;
|
||||
|
||||
if(limit >= 0 && tblen >= limit) {
|
||||
fprintf(stderr,
|
||||
"%s: Too long TL sequence (%ld >= %ld)"
|
||||
" at %zu. "
|
||||
"Broken or maliciously constructed file\n",
|
||||
fname, (long)tblen, (long)limit, *offset);
|
||||
return PD_FAILED;
|
||||
}
|
||||
|
||||
/* Get the next byte from the input stream */
|
||||
ch = fgetc(fp);
|
||||
if(ch == -1) {
|
||||
if(limit > 0 || expect_eoc) {
|
||||
fprintf(stderr,
|
||||
"%s: Unexpected end of file (TL)"
|
||||
" at %zu\n",
|
||||
fname, *offset);
|
||||
return PD_FAILED;
|
||||
} else {
|
||||
return PD_EOF;
|
||||
}
|
||||
}
|
||||
|
||||
tagbuf[tblen++] = ch;
|
||||
|
||||
/*
|
||||
* Decode the TLV tag.
|
||||
*/
|
||||
t_len = ber_fetch_tag(tagbuf, tblen, &tlv_tag);
|
||||
switch(t_len) {
|
||||
case -1:
|
||||
fprintf(stderr,
|
||||
"%s: Fatal error decoding tag"
|
||||
" at %zu+%ld\n",
|
||||
fname, *offset, (long)tblen);
|
||||
return PD_FAILED;
|
||||
case 0:
|
||||
/* More data expected */
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Decode the TLV length.
|
||||
*/
|
||||
constr = BER_TLV_CONSTRUCTED(tagbuf);
|
||||
l_len =
|
||||
ber_fetch_length(constr, tagbuf + t_len, tblen - t_len, &tlv_len);
|
||||
switch(l_len) {
|
||||
case -1:
|
||||
fprintf(stderr,
|
||||
"%s: Fatal error decoding value length"
|
||||
" at %zu\n",
|
||||
fname, *offset + t_len);
|
||||
return PD_FAILED;
|
||||
case 0:
|
||||
/* More data expected */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Make sure the T & L decoders took exactly the whole buffer */
|
||||
assert((t_len + l_len) == tblen);
|
||||
|
||||
if(!expect_eoc || tagbuf[0] || tagbuf[1])
|
||||
print_TL(0, *offset, level, constr, tblen, tlv_tag, tlv_len,
|
||||
effective_size);
|
||||
|
||||
if(limit != -1) {
|
||||
/* If limit is set, account for the TL sequence */
|
||||
limit -= (t_len + l_len);
|
||||
assert(limit >= 0);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
*offset += t_len + l_len;
|
||||
*frame_size += t_len + l_len;
|
||||
effective_size += t_len + l_len;
|
||||
local_esize += t_len + l_len;
|
||||
|
||||
if(expect_eoc && !tagbuf[0] && !tagbuf[1]) {
|
||||
/* End of content octets */
|
||||
print_TL(1, *offset - 2, level - 1, 1, 2, 0, -1, effective_size);
|
||||
return PD_FINISHED;
|
||||
}
|
||||
|
||||
if(constr) {
|
||||
ber_tlv_len_t dec = 0;
|
||||
/*
|
||||
* This is a constructed type. Process recursively.
|
||||
*/
|
||||
printf(">\n"); /* Close the opening tag */
|
||||
if(tlv_len != -1 && limit != -1) {
|
||||
assert(limit >= tlv_len);
|
||||
}
|
||||
pdc = process_deeper(fname, fp, offset, level + 1,
|
||||
tlv_len == -1 ? limit : tlv_len, &dec,
|
||||
t_len + l_len, tlv_len == -1);
|
||||
if(pdc == PD_FAILED) return pdc;
|
||||
if(limit != -1) {
|
||||
assert(limit >= dec);
|
||||
limit -= dec;
|
||||
}
|
||||
*frame_size += dec;
|
||||
effective_size += dec;
|
||||
local_esize += dec;
|
||||
if(tlv_len == -1) {
|
||||
tblen = 0;
|
||||
if(pdc == PD_FINISHED && limit < 0 && !expect_eoc) return pdc;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
assert(tlv_len >= 0);
|
||||
if(print_V(fname, fp, tlv_tag, tlv_len)) return PD_FAILED;
|
||||
|
||||
if(limit != -1) {
|
||||
assert(limit >= tlv_len);
|
||||
limit -= tlv_len;
|
||||
}
|
||||
*offset += tlv_len;
|
||||
*frame_size += tlv_len;
|
||||
effective_size += tlv_len;
|
||||
local_esize += tlv_len;
|
||||
}
|
||||
|
||||
print_TL(1, *offset, level, constr, tblen, tlv_tag, tlv_len,
|
||||
local_esize);
|
||||
|
||||
tblen = 0;
|
||||
|
||||
/* Report success for a single top level TLV */
|
||||
if(level == 0 && limit == -1 && !expect_eoc) break;
|
||||
} /* for(;;) */
|
||||
|
||||
return pdc;
|
||||
}
|
||||
|
||||
static void
|
||||
print_TL(int fin, size_t offset, int level, int constr, ssize_t tlen,
|
||||
ber_tlv_tag_t tlv_tag, ber_tlv_len_t tlv_len,
|
||||
ber_tlv_len_t effective_size) {
|
||||
if(fin && !constr) {
|
||||
printf("</P>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
while(level-- > 0) fputs(indent_bytes, stdout); /* Print indent */
|
||||
printf(fin ? "</" : "<");
|
||||
|
||||
printf(constr ? ((tlv_len == -1) ? "I" : "C") : "P");
|
||||
|
||||
/* Print out the offset of this boundary, even if closing tag */
|
||||
if(!minimalistic) printf(" O=\"%zu\"", offset);
|
||||
|
||||
printf(" T=\"");
|
||||
ber_tlv_tag_fwrite(tlv_tag, stdout);
|
||||
printf("\"");
|
||||
|
||||
if(!fin || (tlv_len == -1 && !minimalistic))
|
||||
printf(" TL=\"%ld\"", (long)tlen);
|
||||
if(!fin) {
|
||||
if(tlv_len == -1)
|
||||
printf(" V=\"Indefinite\"");
|
||||
else
|
||||
printf(" V=\"%ld\"", (long)tlv_len);
|
||||
}
|
||||
|
||||
if(!minimalistic && BER_TAG_CLASS(tlv_tag) == ASN_TAG_CLASS_UNIVERSAL) {
|
||||
const char *str;
|
||||
ber_tlv_tag_t tvalue = BER_TAG_VALUE(tlv_tag);
|
||||
str = ASN_UNIVERSAL_TAG2STR(tvalue);
|
||||
if(str) printf(" A=\"%s\"", str);
|
||||
}
|
||||
|
||||
if(fin) {
|
||||
if(constr && !minimalistic) printf(" L=\"%ld\"", (long)effective_size);
|
||||
printf(">\n");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Print the value in binary form, or reformat for pretty-printing.
|
||||
*/
|
||||
static int
|
||||
print_V(const char *fname, FILE *fp, ber_tlv_tag_t tlv_tag,
|
||||
ber_tlv_len_t tlv_len) {
|
||||
asn_oid_arc_t *arcs = 0; /* Object identifier arcs */
|
||||
unsigned char *vbuf = 0;
|
||||
asn1p_expr_type_e etype = 0;
|
||||
asn1c_integer_t collector = 0;
|
||||
int special_format = 0;
|
||||
ssize_t i;
|
||||
|
||||
/* Figure out what type is it */
|
||||
if(BER_TAG_CLASS(tlv_tag) == ASN_TAG_CLASS_UNIVERSAL && pretty_printing) {
|
||||
ber_tlv_tag_t tvalue = BER_TAG_VALUE(tlv_tag);
|
||||
etype = ASN_UNIVERSAL_TAG2TYPE(tvalue);
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine how to print the value, either in its native binary form,
|
||||
* encoded with &xNN characters, or using pretty-printing.
|
||||
* The basic string types (including "useful types", like UTCTime)
|
||||
* are excempt from this determination logic, because their alphabets
|
||||
* are subsets of the XML's native UTF-8 encoding.
|
||||
*/
|
||||
switch(etype) {
|
||||
case ASN_BASIC_BOOLEAN:
|
||||
if(tlv_len == 1)
|
||||
special_format = 1;
|
||||
else
|
||||
etype = 0;
|
||||
break;
|
||||
case ASN_BASIC_INTEGER:
|
||||
case ASN_BASIC_ENUMERATED:
|
||||
if((size_t)tlv_len <= sizeof(collector))
|
||||
special_format = 1;
|
||||
else
|
||||
etype = 0;
|
||||
break;
|
||||
case ASN_BASIC_OBJECT_IDENTIFIER:
|
||||
case ASN_BASIC_RELATIVE_OID:
|
||||
if(tlv_len > 0 && tlv_len < 128 * 1024 /* VERY long OID! */) {
|
||||
arcs = MALLOC(sizeof(*arcs) * (tlv_len + 1));
|
||||
if(arcs) {
|
||||
vbuf = MALLOC(tlv_len + 1);
|
||||
/* Not checking is intentional */
|
||||
}
|
||||
}
|
||||
case ASN_BASIC_UTCTime:
|
||||
case ASN_BASIC_GeneralizedTime:
|
||||
case ASN_STRING_NumericString:
|
||||
case ASN_STRING_PrintableString:
|
||||
case ASN_STRING_VisibleString:
|
||||
case ASN_STRING_IA5String:
|
||||
case ASN_STRING_UTF8String:
|
||||
break; /* Directly compatible with UTF-8 */
|
||||
case ASN_STRING_BMPString:
|
||||
case ASN_STRING_UniversalString:
|
||||
break; /* Not directly compatible with UTF-8 */
|
||||
default:
|
||||
/* Conditionally compatible with UTF-8 */
|
||||
if(((etype & ASN_STRING_MASK) || (etype == ASN_BASIC_OCTET_STRING) ||
|
||||
/*
|
||||
* AUTOMATIC TAGS or IMPLICIT TAGS in effect,
|
||||
* Treat this primitive type as OCTET_STRING.
|
||||
*/
|
||||
(BER_TAG_CLASS(tlv_tag) != ASN_TAG_CLASS_UNIVERSAL
|
||||
&& pretty_printing))
|
||||
&& (tlv_len > 0 && tlv_len < 128 * 1024)) {
|
||||
vbuf = MALLOC(tlv_len + 1);
|
||||
/* Not checking is intentional */
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* If collection vbuf is present, defer printing the F flag. */
|
||||
if(!vbuf) printf(special_format ? " F>" : ">");
|
||||
|
||||
/*
|
||||
* Print the value in binary or text form,
|
||||
* or collect the bytes into vbuf.
|
||||
*/
|
||||
for(i = 0; i < tlv_len; i++) {
|
||||
int ch = fgetc(fp);
|
||||
if(ch == -1) {
|
||||
fprintf(stderr, "%s: Unexpected end of file (V)\n", fname);
|
||||
if(vbuf) FREEMEM(vbuf);
|
||||
if(arcs) FREEMEM(arcs);
|
||||
return -1;
|
||||
}
|
||||
switch(etype) {
|
||||
case ASN_BASIC_UTCTime:
|
||||
case ASN_BASIC_GeneralizedTime:
|
||||
case ASN_STRING_NumericString:
|
||||
case ASN_STRING_PrintableString:
|
||||
case ASN_STRING_VisibleString:
|
||||
case ASN_STRING_IA5String:
|
||||
case ASN_STRING_UTF8String:
|
||||
switch(ch) {
|
||||
default:
|
||||
if(((etype == ASN_STRING_UTF8String) || !(ch & 0x80))
|
||||
&& (ch >= 0x20)) {
|
||||
printf("%c", ch);
|
||||
break;
|
||||
}
|
||||
/* Fall through */
|
||||
case 0x3c:
|
||||
case 0x3e:
|
||||
case 0x26:
|
||||
printf("&#x%02x;", ch);
|
||||
}
|
||||
break;
|
||||
case ASN_BASIC_BOOLEAN:
|
||||
switch(ch) {
|
||||
case 0:
|
||||
printf("<false/>");
|
||||
break;
|
||||
case 0xff:
|
||||
printf("<true/>");
|
||||
break;
|
||||
default:
|
||||
printf("<true value=\"&#x%02x\"/>", ch);
|
||||
}
|
||||
break;
|
||||
case ASN_BASIC_INTEGER:
|
||||
case ASN_BASIC_ENUMERATED:
|
||||
if(i)
|
||||
collector = collector * 256 + ch;
|
||||
else
|
||||
collector = (int)(signed char)ch;
|
||||
break;
|
||||
default:
|
||||
if(vbuf) {
|
||||
vbuf[i] = ch;
|
||||
} else {
|
||||
printf("&#x%02x;", ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Do post-processing */
|
||||
switch(etype) {
|
||||
case ASN_BASIC_INTEGER:
|
||||
case ASN_BASIC_ENUMERATED:
|
||||
printf("%s", asn1p_itoa(collector));
|
||||
break;
|
||||
case ASN_BASIC_OBJECT_IDENTIFIER:
|
||||
if(vbuf) {
|
||||
OBJECT_IDENTIFIER_t oid = {0, 0};
|
||||
ssize_t arcno;
|
||||
|
||||
oid.buf = vbuf;
|
||||
oid.size = tlv_len;
|
||||
|
||||
arcno = OBJECT_IDENTIFIER_get_arcs(&oid, arcs, tlv_len + 1);
|
||||
if(arcno >= 0) {
|
||||
assert(arcno <= (tlv_len + 1));
|
||||
printf(" F>");
|
||||
for(i = 0; i < arcno; i++) {
|
||||
if(i) printf(".");
|
||||
printf("%" PRIu32, arcs[i]);
|
||||
}
|
||||
FREEMEM(vbuf);
|
||||
vbuf = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ASN_BASIC_RELATIVE_OID:
|
||||
if(vbuf) {
|
||||
RELATIVE_OID_t oid;
|
||||
int arcno;
|
||||
|
||||
oid.buf = vbuf;
|
||||
oid.size = tlv_len;
|
||||
|
||||
arcno = RELATIVE_OID_get_arcs(&oid, arcs, tlv_len);
|
||||
if(arcno >= 0) {
|
||||
assert(arcno <= tlv_len);
|
||||
printf(" F>");
|
||||
for(i = 0; i < arcno; i++) {
|
||||
if(i) printf(".");
|
||||
printf("%" PRIu32, arcs[i]);
|
||||
}
|
||||
FREEMEM(vbuf);
|
||||
vbuf = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the buffer was not consumed, print it out.
|
||||
* It might be an OCTET STRING or other primitive type,
|
||||
* which might actually be printable, but we need to figure it out.
|
||||
*/
|
||||
if(vbuf) {
|
||||
int binary;
|
||||
|
||||
/*
|
||||
* Check whether the data could be represented as text
|
||||
*/
|
||||
binary = -1 * (tlv_len >> 3); /* Threshold is 12.5% binary */
|
||||
for(i = 0; i < tlv_len; i++) {
|
||||
switch(vbuf[i]) {
|
||||
case 0x1b:
|
||||
binary = 1;
|
||||
break;
|
||||
case 0x09:
|
||||
case 0x0a:
|
||||
case 0x0d:
|
||||
continue;
|
||||
default:
|
||||
if(vbuf[i] < 0x20 || vbuf[i] >= 0x7f)
|
||||
if(++binary > 0) /* Way too many */
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
printf(">");
|
||||
for(i = 0; i < tlv_len; i++) {
|
||||
if(binary > 0 || vbuf[i] < 0x20 || vbuf[i] >= 0x7f
|
||||
|| vbuf[i] == 0x26 /* '&' */
|
||||
|| vbuf[i] == 0x3c /* '<' */
|
||||
|| vbuf[i] == 0x3e /* '>' */
|
||||
)
|
||||
printf("&#x%02x;", vbuf[i]);
|
||||
else
|
||||
printf("%c", vbuf[i]);
|
||||
}
|
||||
FREEMEM(vbuf);
|
||||
}
|
||||
|
||||
if(arcs) FREEMEM(arcs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
decode_tlv_from_string(const char *datastring) {
|
||||
unsigned char *data, *dp;
|
||||
size_t dsize; /* Data size */
|
||||
ssize_t len;
|
||||
ber_tlv_tag_t tlv_tag;
|
||||
ber_tlv_len_t tlv_len;
|
||||
const char *p;
|
||||
int half;
|
||||
|
||||
dsize = strlen(datastring) + 1;
|
||||
dp = data = CALLOC(1, dsize);
|
||||
assert(data);
|
||||
|
||||
for(half = 0, p = datastring; *p; p++) {
|
||||
/* clang-format off */
|
||||
switch(*p) {
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
*dp |= *p - '0'; break;
|
||||
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
|
||||
*dp |= *p - 'A' + 10; break;
|
||||
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
|
||||
*dp |= *p - 'a' + 10; break;
|
||||
case ' ': case '\t': case '\r': case '\n':
|
||||
continue;
|
||||
default:
|
||||
fprintf(stderr, "Unexpected symbols in data string:\n");
|
||||
fprintf(stderr, "%s\n", datastring);
|
||||
for(dp = data; datastring < p; datastring++, dp++)
|
||||
*dp = ' ';
|
||||
*dp = '\0';
|
||||
fprintf(stderr, "%s^ <- here\n", (char *)data);
|
||||
return -1;
|
||||
}
|
||||
/* clang-format on */
|
||||
if(half)
|
||||
dp++;
|
||||
else
|
||||
(*dp) <<= 4;
|
||||
half = !half;
|
||||
}
|
||||
|
||||
assert((size_t)(dp - data) <= dsize);
|
||||
dsize = dp - data;
|
||||
|
||||
printf("BER: ");
|
||||
for(dp = data; dp < data + dsize; dp++) printf("%02X", *dp);
|
||||
printf("\n");
|
||||
|
||||
len = ber_fetch_tag(data, dsize, &tlv_tag);
|
||||
switch(len) {
|
||||
case -1:
|
||||
fprintf(stderr, "TAG: Fatal error decoding tag\n");
|
||||
return -1;
|
||||
case 0:
|
||||
fprintf(stderr, "TAG: More data expected\n");
|
||||
return -1;
|
||||
default:
|
||||
printf("TAG: ");
|
||||
ber_tlv_tag_fwrite(tlv_tag, stdout);
|
||||
if(BER_TLV_CONSTRUCTED(data)) {
|
||||
printf(" (constructed)");
|
||||
} else if(dsize >= 2 && data[0] == 0 && data[1] == 0) {
|
||||
printf(" (end-of-content)");
|
||||
} else {
|
||||
printf(" (primitive)");
|
||||
}
|
||||
if(BER_TAG_CLASS(tlv_tag) == ASN_TAG_CLASS_UNIVERSAL) {
|
||||
const char *str;
|
||||
ber_tlv_tag_t tvalue = BER_TAG_VALUE(tlv_tag);
|
||||
str = ASN_UNIVERSAL_TAG2STR(tvalue);
|
||||
if(str) printf(" \"%s\"", str);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
if(dsize > (size_t)len) {
|
||||
len = ber_fetch_length(BER_TLV_CONSTRUCTED(data), data + len,
|
||||
dsize - len, &tlv_len);
|
||||
switch(len) {
|
||||
case -1:
|
||||
fprintf(stderr, "LEN: Fatal error decoding length\n");
|
||||
return -1;
|
||||
case 0:
|
||||
fprintf(stderr, "LEN: More data expected\n");
|
||||
return -1;
|
||||
default:
|
||||
if(tlv_len == (ber_tlv_len_t)-1)
|
||||
printf("LEN: Indefinite length encoding\n");
|
||||
else
|
||||
printf("LEN: %ld bytes\n", (long)tlv_len);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Dummy functions.
|
||||
*/
|
||||
asn_dec_rval_t
|
||||
ber_check_tags(const asn_codec_ctx_t *opt_codec_ctx,
|
||||
const asn_TYPE_descriptor_t *td, asn_struct_ctx_t *opt_ctx,
|
||||
const void *ptr, size_t size, int tag_mode, int last_tag_form,
|
||||
ber_tlv_len_t *last_length, int *opt_tlv_form) {
|
||||
asn_dec_rval_t rv = {0, 0};
|
||||
(void)opt_codec_ctx;
|
||||
(void)td;
|
||||
(void)opt_ctx;
|
||||
(void)ptr;
|
||||
(void)size;
|
||||
(void)tag_mode;
|
||||
(void)last_tag_form;
|
||||
(void)last_length;
|
||||
(void)opt_tlv_form;
|
||||
return rv;
|
||||
}
|
||||
|
||||
ssize_t
|
||||
der_write_tags(const asn_TYPE_descriptor_t *td, size_t slen, int tag_mode,
|
||||
int last_tag_form, ber_tlv_tag_t tag,
|
||||
asn_app_consume_bytes_f *cb, void *app_key) {
|
||||
(void)td;
|
||||
(void)slen;
|
||||
(void)tag_mode;
|
||||
(void)last_tag_form;
|
||||
(void)tag;
|
||||
(void)cb;
|
||||
(void)app_key;
|
||||
return -1;
|
||||
}
|
||||
|
||||
asn_dec_rval_t
|
||||
xer_decode_general(const asn_codec_ctx_t *opt_codec_ctx, asn_struct_ctx_t *ctx,
|
||||
void *struct_key, const char *xml_tag, const void *buf_ptr,
|
||||
size_t size,
|
||||
int (*otd)(void *struct_key, const void *chunk_buf,
|
||||
size_t chunk_size),
|
||||
ssize_t (*br)(void *struct_key, const void *chunk_buf,
|
||||
size_t chunk_size, int have_more)) {
|
||||
asn_dec_rval_t rv = {0, 0};
|
||||
(void)opt_codec_ctx;
|
||||
(void)ctx;
|
||||
(void)struct_key;
|
||||
(void)xml_tag;
|
||||
(void)buf_ptr;
|
||||
(void)size;
|
||||
(void)otd;
|
||||
(void)br;
|
||||
return rv;
|
||||
}
|
||||
|
||||
size_t
|
||||
xer_whitespace_span(const void *chunk_buf, size_t chunk_size) {
|
||||
(void)chunk_buf;
|
||||
(void)chunk_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
OCTET_STRING_compare(const asn_TYPE_descriptor_t *td, const void *a,
|
||||
const void *b) {
|
||||
(void)td;
|
||||
(void)a;
|
||||
(void)b;
|
||||
return 0;
|
||||
}
|
||||
|
||||
intmax_t
|
||||
asn_random_between(intmax_t a, intmax_t b) {
|
||||
(void)b;
|
||||
return a;
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue