asn1: Add support for decoration w/ external types
This adds support for asn1_compile --decorate=... variation that causes decoration of an ASN.1 SET/SEQUENCE type with a field of a non-ASN.1 type. This means we can now have an ASN.1 type to represent a request that can then have a "hidden" field -- hidden in that it is neither encoded nor decoded. This field will be copied and freed when the decoration is of an ASN.1 type or of a external, C type that comes with copy constructor and destructor functions. Decoration with a `void *` field which is neither copied nor freed is also supported. We may end up using this to, for example, replace the `hdb_entry_ex` type by decorating `HDB_entry` with a C type that points to the `HDB` in which the entry was found or to which it should be written.
This commit is contained in:
@@ -468,14 +468,12 @@ x690sample_asn1_files: asn1_compile$(EXEEXT) $(srcdir)/x690sample.asn1
|
||||
test_template_asn1_files: asn1_compile$(EXEEXT) $(srcdir)/test.asn1
|
||||
$(ASN1_COMPILE) --one-code-file \
|
||||
--template \
|
||||
--sequence=TESTSeqOf \
|
||||
--decorate='TESTDecorated:TESTuint32:version2?' \
|
||||
--option-file=$(srcdir)/test.opt \
|
||||
$(srcdir)/test.asn1 test_template_asn1 || (rm -f test_template_asn1_files ; exit 1)
|
||||
|
||||
test_asn1_files: asn1_compile$(EXEEXT) $(srcdir)/test.asn1
|
||||
$(ASN1_COMPILE) --one-code-file \
|
||||
--decorate='TESTDecorated:TESTuint32:version2?' \
|
||||
--sequence=TESTSeqOf \
|
||||
--option-file=$(srcdir)/test.opt \
|
||||
$(srcdir)/test.asn1 test_asn1 || (rm -f test_asn1_files ; exit 1)
|
||||
|
||||
|
||||
@@ -506,6 +504,7 @@ EXTRA_DIST = \
|
||||
setchgpw2.asn1 \
|
||||
x690sample.asn1 \
|
||||
test.asn1 \
|
||||
test.opt \
|
||||
test.gen \
|
||||
version-script.map
|
||||
|
||||
|
@@ -396,8 +396,8 @@ $(OBJ)\asn1_test_asn1.c $(OBJ)\test_asn1.h: $(BINDIR)\asn1_compile.exe test.asn1
|
||||
cd $(OBJ)
|
||||
$(BINDIR)\asn1_compile.exe \
|
||||
--template \
|
||||
--decorate="TESTDecorated:TESTuint32:version2?" \
|
||||
--one-code-file --sequence=TESTSeqOf \
|
||||
--option-file=$(SRCDIR)/test.opt \
|
||||
--one-code-file \
|
||||
$(SRCDIR)\test.asn1 test_asn1 \
|
||||
|| ($(RM) $(OBJ)\test_asn1.h ; exit /b 1)
|
||||
cd $(SRCDIR)
|
||||
@@ -406,9 +406,8 @@ $(OBJ)\asn1_test_template_asn1.c $(OBJ)\test_template_asn1.h: $(BINDIR)\asn1_com
|
||||
cd $(OBJ)
|
||||
$(BINDIR)\asn1_compile.exe \
|
||||
--template \
|
||||
--decorate="TESTDecorated:TESTuint32:version2?" \
|
||||
--option-file=$(SRCDIR)/test.opt \
|
||||
--one-code-file \
|
||||
--sequence=TESTSeqOf \
|
||||
$(SRCDIR)\test.asn1 test_template_asn1 \
|
||||
|| ($(RM) $(OBJ)\test_template_asn1.h ; exit /b 1)
|
||||
cd $(SRCDIR)
|
||||
|
@@ -861,104 +861,237 @@ functions for adding or removing items from the named type when it is a
|
||||
|
||||
See the manual page `asn1_compile.1`:
|
||||
|
||||
```
|
||||
ASN1_COMPILE(1) HEIMDAL General Commands Manual ASN1_COMPILE(1)
|
||||
```text
|
||||
ASN1_COMPILE(1) BSD General Commands Manual ASN1_COMPILE(1)
|
||||
|
||||
NAME
|
||||
asn1_compile — compile ASN.1 modules
|
||||
|
||||
SYNOPSIS
|
||||
asn1_compile [--template] [--prefix-enum] [--enum-prefix=PREFIX]
|
||||
[--encode-rfc1510-bit-string] [--decode-dce-ber]
|
||||
[--support-ber] [--preserve-binary=TYPE-NAME]
|
||||
[--sequence=TYPE-NAME] [--one-code-file] [--gen-name=NAME]
|
||||
[--decorate=TYPE-NAME:FIELD-TYPE:field-name[?]]
|
||||
[--option-file=FILE] [--original-order] [--no-parse-units]
|
||||
[--type-file=C-HEADER-FILE] [--version] [--help]
|
||||
[FILE.asn1 [NAME]]
|
||||
[--encode-rfc1510-bit-string] [--decode-dce-ber]
|
||||
[--support-ber] [--preserve-binary=TYPE] [--sequence=TYPE]
|
||||
[--decorate=TYPE:FTYPE:fname[?]]
|
||||
[--decorate=TYPE:void:fname:::]
|
||||
[--decorate=TYPE:FTYPE:fname[?]:[copyfn]:[freefn]:header]
|
||||
[--one-code-file] [--gen-name=NAME] [--option-file=FILE]
|
||||
[--original-order] [--no-parse-units]
|
||||
[--type-file=C-HEADER-FILE] [--version] [--help]
|
||||
[FILE.asn1 [NAME]]
|
||||
|
||||
DESCRIPTION
|
||||
asn1_compile Compiles an ASN.1 module into C source code and header
|
||||
asn1_compile compiles an ASN.1 module into C source code and header
|
||||
files.
|
||||
|
||||
A fairly large subset of ASN.1 as specified in X.680, and the ASN.1 In‐
|
||||
formation Object System as specified in X.681, X.682, and X.683 is sup‐
|
||||
ported, with support for the Distinguished Encoding Rules (DER), partial
|
||||
Basic Encoding Rules (BER) support, and experimental JSON support (encod‐
|
||||
ing only at this time).
|
||||
|
||||
See the compiler's README files for details about the C code and inter‐
|
||||
faces it generates.
|
||||
|
||||
The Information Object System support includes automatic codec support
|
||||
for encoding and decoding through “open types” which are also known as
|
||||
“typed holes”. See RFC 5912 for examples of how to use the ASN.1 Infor‐
|
||||
mation Object System via X.681/X.682/X.683 annotations. See the com‐
|
||||
piler's README files for more information on ASN.1 Information Object
|
||||
System support.
|
||||
|
||||
Extensions specific to Heimdal are generally not syntactic in nature but
|
||||
rather command-line options to this program. For example, one can use
|
||||
command-line options to:
|
||||
• enable decoding of BER-encoded values;
|
||||
• enable RFC1510-style handling of ‘BIT STRING’ types;
|
||||
• enable saving of as-received encodings of specific types
|
||||
for the purpose of signature validation;
|
||||
• generate add/remove utility functions for array types;
|
||||
• decorate generated ‘struct’ types with fields that are nei‐
|
||||
ther encoded nor decoded;
|
||||
etc.
|
||||
|
||||
ASN.1 x.680 features supported:
|
||||
• most primitive types (except BMPString and REAL);
|
||||
• all constructed types, including SET and SET OF;
|
||||
• explicit and implicit tagging.
|
||||
|
||||
Size and range constraints on the ‘INTEGER’ type cause the compiler to
|
||||
generate appropriate C types such as ‘int’, ‘unsigned int’, ‘int64_t’,
|
||||
‘uint64_t’. Unconstrained ‘INTEGER’ is treated as ‘heim_integer’, which
|
||||
represents an integer of arbitrary size.
|
||||
|
||||
Caveats and ASN.1 x.680 features not supported:
|
||||
• JSON encoding support is not quite X.697 (JER) compatible.
|
||||
Its JSON schema is subject to change without notice.
|
||||
• Control over C types generated is very limited, mainly only
|
||||
for integer types.
|
||||
• When using the template backend, `SET { .. }` types are
|
||||
currently not sorted by tag as they should be, but if the
|
||||
module author sorts them by hand then correct DER will be
|
||||
produced.
|
||||
• ‘AUTOMATIC TAGS’ is not supported.
|
||||
• The REAL type is not supported.
|
||||
• The EmbeddedPDV type is not supported.
|
||||
• The BMPString type is not supported.
|
||||
• The IA5String is not properly supported, as it's essen‐
|
||||
tially treated as a UTF8String with a different tag.
|
||||
• All supported non-octet strings are treated as like the
|
||||
UTF8String type.
|
||||
• Only types can be imported into ASN.1 modules at this time.
|
||||
• Only simple value syntax is supported. Constructed value
|
||||
syntax (i.e., values of SET, SEQUENCE, SET OF, and SEQUENCE
|
||||
OF types), is not supported. Values of `CHOICE` types are
|
||||
also not supported.
|
||||
|
||||
Options supported:
|
||||
|
||||
--template
|
||||
Use the “template” backend instead of the “codegen” backend
|
||||
(which is the default backend). The template backend generates
|
||||
“templates” which are akin to bytecode, and which are interpreted
|
||||
at run-time. The codegen backend generates C code for all func-
|
||||
tions directly, with no template interpretation. The template
|
||||
backend scales better than the codegen backend because as we add
|
||||
support for more encoding rules the templates stay mostly the
|
||||
same, thus scaling linearly with size of module. Whereas the
|
||||
codegen backend scales linear with the product of module size and
|
||||
number of encoding rules supported. More importantly, currently
|
||||
only the template backend supports automatic decoding of open
|
||||
types via X.681/X.682/X.683 annotations.
|
||||
Use the “template” backend instead of the “codegen” backend
|
||||
(which is the default backend).
|
||||
|
||||
The template backend generates “templates” which are akin to
|
||||
bytecode, and which are interpreted at run-time.
|
||||
|
||||
The codegen backend generates C code for all functions directly,
|
||||
with no template interpretation.
|
||||
|
||||
The template backend scales better than the codegen backend be‐
|
||||
cause as we add support for more encoding rules and more opera‐
|
||||
tions (we may add value comparators) the templates stay mostly
|
||||
the same, thus scaling linearly with size of module. Whereas the
|
||||
codegen backend scales linear with the product of module size and
|
||||
number of encoding rules supported.
|
||||
|
||||
--prefix-enum
|
||||
This option should be removed because ENUMERATED types should
|
||||
always have their labels prefixed.
|
||||
This option should be removed because ENUMERATED types should al‐
|
||||
ways have their labels prefixed.
|
||||
|
||||
--enum-prefix=PREFIX
|
||||
This option should be removed because ENUMERATED types should
|
||||
always have their labels prefixed.
|
||||
This option should be removed because ENUMERATED types should al‐
|
||||
ways have their labels prefixed.
|
||||
|
||||
--encode-rfc1510-bit-string
|
||||
Use RFC1510, non-standard handling of “BIT STRING” types.
|
||||
Use RFC1510, non-standard handling of “BIT STRING” types.
|
||||
|
||||
--decode-dce-ber
|
||||
|
||||
--support-ber
|
||||
|
||||
--preserve-binary=TYPE-NAME
|
||||
Generate ‘_save’ fields in structs to preserve the original
|
||||
encoding of some sub-value. This is useful for cryptographic
|
||||
applications to avoid having to re-encode values to check signa-
|
||||
tures, etc.
|
||||
--preserve-binary=TYPE
|
||||
Generate a field named ‘_save’ in the C struct generated for the
|
||||
named TYPE. This field is used to preserve the original encoding
|
||||
of the value of the TYPE.
|
||||
|
||||
--sequence=TYPE-NAME
|
||||
Generate add/remove functions for ‘SET OF’ and ‘SEQUENCE OF’
|
||||
types.
|
||||
This is useful for cryptographic applications so that they can
|
||||
check signatures of encoded values as-received without having to
|
||||
re-encode those values.
|
||||
|
||||
--decorate=TYPE-NAME:FIELD-TYPE:field-name[?]
|
||||
Add to the TYPE-NAME SET or SEQUENCE type a field of the given
|
||||
FIELD-TYPE and field-name, but do not encode or decode this
|
||||
field. If the field-name ends in a question mark, then treat the
|
||||
field as OPTIONAL for the purposes of copy/free function stubs.
|
||||
This is useful for adding fields to existing types that can be
|
||||
used for internal bookkeeping but which do not affect interoper-
|
||||
ability because they are not encoded.
|
||||
For example, the TBSCertificate type should have values preserved
|
||||
so that Certificate validation can check the signatureValue over
|
||||
the tbsCertificate's value as-received.
|
||||
|
||||
The alternative of encoding a value to check a signature of it is
|
||||
brittle. For types where non-canonical encodings (such as BER)
|
||||
are allowed, this alternative is bound to fail. Thus the point
|
||||
of this option.
|
||||
|
||||
--sequence=TYPE
|
||||
Generate add/remove functions for the named ASN.1 TYPE which must
|
||||
be a ‘SET OF’ or ‘SEQUENCE OF’ type.
|
||||
|
||||
--decorate=TYPE:FTYPE:fname[?]
|
||||
Add to the C struct generated for the given ASN.1 type named TYPE
|
||||
a “hidden” field named fname of the given ASN.1 type FTYPE, but
|
||||
do not encode or decode it. The TYPE must be a SET or SEQUENCE
|
||||
type. If the fname ends in a question mark, then treat the field
|
||||
as OPTIONAL.
|
||||
|
||||
This is useful for adding fields to existing types that can be
|
||||
used for internal bookkeeping but which do not affect interoper‐
|
||||
ability because they are neither encoded nor decoded. For exam‐
|
||||
ple, one might decorate a request type with state needed during
|
||||
processing of the request.
|
||||
|
||||
--decorate=TYPE:void:fname:::
|
||||
Add to the C struct generated for the given ASN.1 type named TYPE
|
||||
a “hidden” field named fname of type ‘void *’ (but do not encode
|
||||
or decode it.
|
||||
|
||||
The destructor and copy constructor functions generated by this
|
||||
compiler for TYPE will set this field to the ‘NULL’ pointer.
|
||||
|
||||
This is useful for adding fields to existing types that can be
|
||||
used for internal bookkeeping but which do not affect interoper‐
|
||||
ability because they are neither encoded nor decoded. For exam‐
|
||||
ple, one might decorate a request type with state needed during
|
||||
processing of the request.
|
||||
|
||||
--decorate=TYPE:FTYPE:fname[?]:copyfn:freefn:header
|
||||
Add to the C struct generated for the given ASN.1 type named TYPE
|
||||
a “hidden” field named fname of the given external C type FTYPE,
|
||||
declared in the given header but do not encode or decode this
|
||||
field. The TYPE must be a SET or SEQUENCE type. If the fname
|
||||
ends in a question mark, then treat the field as OPTIONAL.
|
||||
|
||||
This is useful for adding fields to existing types that can be
|
||||
used for internal bookkeeping but which do not affect interoper‐
|
||||
ability because they are neither encoded nor decoded. For exam‐
|
||||
ple, one might decorate a request type with state needed during
|
||||
processing of the request.
|
||||
|
||||
The header must include double quotes or angle brackets. The
|
||||
copyfn must be the name of a copy constructor function that takes
|
||||
a pointer to a source value of the type, and a pointer to a des‐
|
||||
tination value of the type, in that order, and which returns zero
|
||||
on success or else a system error code on failure. The freefn
|
||||
must be the name of a destructor function that takes a pointer to
|
||||
a value of the type and which releases resources referenced by
|
||||
that value, but does not free the value itself (the run-time al‐
|
||||
locates this value as needed from the C heap). The freefn should
|
||||
also reset the value to a pristine state (such as all zeros).
|
||||
|
||||
If the copyfn and freefn are empty strings, then the decoration
|
||||
field will neither be copied nor freed by the functions generated
|
||||
for the TYPE.
|
||||
|
||||
NOTE: At this time only one decoration may be specified per type.
|
||||
|
||||
--one-code-file
|
||||
Generate a single source code file. Otherwise a separate code
|
||||
file will be generated for every type.
|
||||
Generate a single source code file. Otherwise a separate code
|
||||
file will be generated for every type.
|
||||
|
||||
--gen-name=NAME
|
||||
Use NAME to form the names of the files generated.
|
||||
Use NAME to form the names of the files generated.
|
||||
|
||||
--option-file=FILE
|
||||
Take additional command-line options from FILE.
|
||||
Take additional command-line options from FILE.
|
||||
|
||||
--original-order
|
||||
Attempt to preserve the original order of type definition in the
|
||||
ASN.1 module. By default the compiler generates types in a topo-
|
||||
logical sort order.
|
||||
Attempt to preserve the original order of type definition in the
|
||||
ASN.1 module. By default the compiler generates types in a topo‐
|
||||
logical sort order.
|
||||
|
||||
--no-parse-units
|
||||
Do not generate to-int / from-int functions for enumeration
|
||||
types.
|
||||
Do not generate to-int / from-int functions for enumeration
|
||||
types.
|
||||
|
||||
--type-file=C-HEADER-FILE
|
||||
Generate an include of the named header file that might be needed
|
||||
for common type defintions.
|
||||
Generate an include of the named header file that might be needed
|
||||
for common type defintions.
|
||||
|
||||
--version
|
||||
|
||||
--help
|
||||
|
||||
HEIMDAL February 22, 2021 HEIMDAL
|
||||
NOTES
|
||||
Note that only one decoration per-SET/SEQUENCE type may be specified.
|
||||
|
||||
Currently only the template backend supports automatic encoding and de‐
|
||||
coding of open types via the ASN.1 Information Object System and
|
||||
X.681/X.682/X.683 annotations.
|
||||
|
||||
HEIMDAL February 22, 2021 HEIMDAL
|
||||
```
|
||||
|
||||
## APIs Generated by the Compiler
|
||||
@@ -1076,9 +1209,9 @@ absence and non-NULL indicating presence.
|
||||
|
||||
And so on.
|
||||
|
||||
## asn1_print Usage
|
||||
## `asn1_print` Usage
|
||||
|
||||
```
|
||||
```text
|
||||
ASN1_PRINT(1) BSD General Commands Manual ASN1_PRINT(1)
|
||||
|
||||
NAME
|
||||
|
@@ -42,43 +42,16 @@
|
||||
* TBD:
|
||||
*
|
||||
* - For OER also encode number of optional/default/extension elements into
|
||||
* header entry's ptr field, not just the number of entries that follow it
|
||||
* header entry's ptr field, not just the number of entries that follow it.
|
||||
*
|
||||
* - For JER/GSER/whatver, and probably for not-C-coded template interpreters,
|
||||
* we'll need to have an entry type for the names of structures and their
|
||||
* fields.
|
||||
* - For JER we'll need to encode encoding options (encode as array, encode as
|
||||
* object, etc.)
|
||||
*
|
||||
* - For auto open types we need a new opcode, let's call it
|
||||
* A1_OP_OPENTYPE_OBJSET, and we need to encode into its entry:
|
||||
* a) the index of the template entry for the type ID field, and
|
||||
* b) the index of the template entry for the open type field,
|
||||
* c) 1 bit to indicate whether the object set is sorted by type ID value,
|
||||
* d) a pointer to the object set's template.
|
||||
* With that we can then find the struct offsets of those, and also their
|
||||
* types (since we can find their template entries).
|
||||
* The object set entries should be encoded into two template entries each:
|
||||
* one pointing to the value of the type ID field for that object (unless
|
||||
* the value is an integer, in which case the ptr should be the integer
|
||||
* value directly), and the other pointing to the template for the type
|
||||
* identified by the type ID. These will need an opcode each...
|
||||
* A1_OP_OPENTYPE_ID and A1_OP_OPENTYPE.
|
||||
* We should also end the object set with an A1_OP_OPENTYPE_OBJSET entry so
|
||||
* that iterating backwards can be fast. Unless... unless we don't inline
|
||||
* the object set and its objects but point to the object set's template.
|
||||
* Also, for extensible object sets we can point to the object set's name,
|
||||
* and we can then have a function to get an object set template by name,
|
||||
* one to release that, and one to add an object to the object set (there's
|
||||
* no need to remove objects from object sets, which helps with thread-
|
||||
* safety). And then we don't need (c) either.
|
||||
* The decoder will then not see these entries until after decoding the type
|
||||
* ID and open type field (as its outer type, so OCTET STRING, BIT STRING,
|
||||
* or HEIM_ANY) and so it will be able to find those values in the struct at
|
||||
* their respective offsets.
|
||||
* The encoder and decoder both need to identify the relevant object in the
|
||||
* object set, either by linear search or binary search if they are sorted
|
||||
* by type ID value, then interpret the template for the identified type.
|
||||
* The encoder needs to place the encoding into the normal location for it
|
||||
* in the struct, then it can execute the normal template entry for it.
|
||||
* - For open types we'll need to be able to indicate what encoding rules the
|
||||
* type uses.
|
||||
*
|
||||
* - We have too many bits for tags (20) and probably not enough for ops (4
|
||||
* bits, and we've used all but one).
|
||||
*/
|
||||
|
||||
/* header:
|
||||
@@ -155,21 +128,23 @@
|
||||
* 28..31 op
|
||||
*/
|
||||
|
||||
#define A1_OP_MASK (0xf0000000)
|
||||
#define A1_OP_TYPE (0x10000000)
|
||||
#define A1_OP_TYPE_EXTERN (0x20000000)
|
||||
#define A1_OP_TAG (0x30000000)
|
||||
#define A1_OP_PARSE (0x40000000)
|
||||
#define A1_OP_SEQOF (0x50000000)
|
||||
#define A1_OP_SETOF (0x60000000)
|
||||
#define A1_OP_BMEMBER (0x70000000)
|
||||
#define A1_OP_CHOICE (0x80000000)
|
||||
#define A1_OP_DEFVAL (0x90000000)
|
||||
#define A1_OP_OPENTYPE_OBJSET (0xa0000000)
|
||||
#define A1_OP_OPENTYPE_ID (0xb0000000)
|
||||
#define A1_OP_OPENTYPE (0xc0000000)
|
||||
#define A1_OP_NAME (0xd0000000)
|
||||
#define A1_OP_TYPE_DECORATE (0xe0000000)
|
||||
#define A1_OP_MASK (0xf0000000)
|
||||
#define A1_OP_TYPE (0x10000000) /* templated type */
|
||||
#define A1_OP_TYPE_EXTERN (0x20000000) /* templated type (imported) */
|
||||
#define A1_OP_TAG (0x30000000) /* a tag */
|
||||
#define A1_OP_PARSE (0x40000000) /* primitive type */
|
||||
#define A1_OP_SEQOF (0x50000000) /* sequence of */
|
||||
#define A1_OP_SETOF (0x60000000) /* set of */
|
||||
#define A1_OP_BMEMBER (0x70000000) /* BIT STRING member */
|
||||
#define A1_OP_CHOICE (0x80000000) /* CHOICE */
|
||||
#define A1_OP_DEFVAL (0x90000000) /* def. value */
|
||||
#define A1_OP_OPENTYPE_OBJSET (0xa0000000) /* object set for open type */
|
||||
#define A1_OP_OPENTYPE_ID (0xb0000000) /* open type id field */
|
||||
#define A1_OP_OPENTYPE (0xc0000000) /* open type field */
|
||||
#define A1_OP_NAME (0xd0000000) /* symbol */
|
||||
#define A1_OP_TYPE_DECORATE (0xe0000000) /* decoration w/ templated type */
|
||||
#define A1_OP_TYPE_DECORATE_EXTERN (0xf0000000) /* decoration w/ some C type */
|
||||
/* 0x00.. is still free */
|
||||
|
||||
#define A1_FLAG_MASK (0x0f000000)
|
||||
#define A1_FLAG_OPTIONAL (0x01000000)
|
||||
|
@@ -46,9 +46,11 @@
|
||||
.Op Fl Fl encode-rfc1510-bit-string
|
||||
.Op Fl Fl decode-dce-ber
|
||||
.Op Fl Fl support-ber
|
||||
.Op Fl Fl preserve-binary=TYPE-NAME
|
||||
.Op Fl Fl sequence=TYPE-NAME
|
||||
.Op Fl Fl decorate=TYPE-NAME:FIELD-TYPE:field-name[?]
|
||||
.Op Fl Fl preserve-binary=TYPE
|
||||
.Op Fl Fl sequence=TYPE
|
||||
.Op Fl Fl decorate=TYPE:FTYPE:fname[?]
|
||||
.Op Fl Fl decorate=TYPE:void:fname:::
|
||||
.Op Fl Fl decorate=TYPE:FTYPE:fname[?]:[copyfn]:[freefn]:header
|
||||
.Op Fl Fl one-code-file
|
||||
.Op Fl Fl gen-name=NAME
|
||||
.Op Fl Fl option-file=FILE
|
||||
@@ -61,7 +63,117 @@
|
||||
.Ek
|
||||
.Sh DESCRIPTION
|
||||
.Nm
|
||||
Compiles an ASN.1 module into C source code and header files.
|
||||
compiles an ASN.1 module into C source code and header files.
|
||||
.Pp
|
||||
A fairly large subset of ASN.1 as specified in X.680, and the
|
||||
ASN.1 Information Object System as specified in X.681, X.682, and
|
||||
X.683 is supported, with support for the Distinguished Encoding
|
||||
Rules (DER), partial Basic Encoding Rules (BER) support, and
|
||||
experimental JSON support (encoding only at this time).
|
||||
.Pp
|
||||
See the compiler's README files for details about the C code and
|
||||
interfaces it generates.
|
||||
.Pp
|
||||
The Information Object System support includes automatic codec
|
||||
support for encoding and decoding through
|
||||
.Dq open types
|
||||
which are also known as
|
||||
.Dq typed holes .
|
||||
See RFC 5912 for examples of how to use the ASN.1
|
||||
Information Object System via X.681/X.682/X.683 annotations. See
|
||||
the compiler's README files for more information on ASN.1
|
||||
Information Object System support.
|
||||
.Pp
|
||||
Extensions specific to Heimdal are generally not syntactic in
|
||||
nature but rather command-line options to this program.
|
||||
For example, one can use command-line options to:
|
||||
.Bl -bullet -compact -width Ds -offset indent
|
||||
.It
|
||||
enable decoding of BER-encoded values;
|
||||
.It
|
||||
enable RFC1510-style handling of
|
||||
.Sq BIT STRING
|
||||
types;
|
||||
.It
|
||||
enable saving of as-received encodings of specific types for the
|
||||
purpose of signature validation;
|
||||
.It
|
||||
generate add/remove utility functions for array types;
|
||||
.It
|
||||
decorate generated
|
||||
.Sq struct
|
||||
types with fields that are neither encoded nor decoded;
|
||||
.El
|
||||
etc.
|
||||
.Pp
|
||||
ASN.1 x.680 features supported:
|
||||
.Bl -bullet -compact -width Ds -offset indent
|
||||
.It
|
||||
most primitive types (except BMPString and REAL);
|
||||
.It
|
||||
all constructed types, including SET and SET OF;
|
||||
.It
|
||||
explicit and implicit tagging.
|
||||
.El
|
||||
.Pp
|
||||
Size and range constraints on the
|
||||
.Sq INTEGER
|
||||
type cause the compiler to generate appropriate C types such as
|
||||
.Sq int ,
|
||||
.Sq unsigned int ,
|
||||
.Sq int64_t ,
|
||||
.Sq uint64_t .
|
||||
Unconstrained
|
||||
.Sq INTEGER
|
||||
is treated as
|
||||
.Sq heim_integer ,
|
||||
which represents an integer of arbitrary size.
|
||||
.Pp
|
||||
Caveats and ASN.1 x.680 features not supported:
|
||||
.Bl -bullet -compact -width Ds -offset indent
|
||||
.It
|
||||
JSON encoding support is not quite X.697 (JER) compatible.
|
||||
Its JSON schema is subject to change without notice.
|
||||
.It
|
||||
Control over C types generated is very limited, mainly only for
|
||||
integer types.
|
||||
.It
|
||||
When using the template backend, `SET { .. }` types are currently
|
||||
not sorted by tag as they should be, but if the module author
|
||||
sorts them by hand then correct DER will be produced.
|
||||
.It
|
||||
.Sq AUTOMATIC TAGS
|
||||
is not supported.
|
||||
.It
|
||||
The
|
||||
.Va REAL
|
||||
type is not supported.
|
||||
.It
|
||||
The
|
||||
.Va EmbeddedPDV
|
||||
type is not supported.
|
||||
.It
|
||||
The
|
||||
.Va BMPString
|
||||
type is not supported.
|
||||
.It
|
||||
The
|
||||
.Va IA5String
|
||||
is not properly supported, as it's essentially treated as a
|
||||
.Va UTF8String
|
||||
with a different tag.
|
||||
.It
|
||||
All supported non-octet strings are treated as like the
|
||||
.Va UTF8String
|
||||
type.
|
||||
.It
|
||||
Only types can be imported into ASN.1 modules at this time.
|
||||
.It
|
||||
Only simple value syntax is supported.
|
||||
Constructed value syntax (i.e., values of SET, SEQUENCE, SET OF,
|
||||
and SEQUENCE OF types), is not supported.
|
||||
Values of `CHOICE` types are also not supported.
|
||||
.El
|
||||
.Pp
|
||||
Options supported:
|
||||
.Bl -tag -width Ds
|
||||
@@ -71,20 +183,21 @@ Use the
|
||||
backend instead of the
|
||||
.Dq codegen
|
||||
backend (which is the default backend).
|
||||
.Pp
|
||||
The template backend generates
|
||||
.Dq templates
|
||||
which are akin to bytecode, and which are interpreted at
|
||||
run-time.
|
||||
.Pp
|
||||
The codegen backend generates C code for all functions directly,
|
||||
with no template interpretation.
|
||||
.Pp
|
||||
The template backend scales better than the codegen backend
|
||||
because as we add support for more encoding rules the templates
|
||||
stay mostly the same, thus scaling linearly with size of module.
|
||||
because as we add support for more encoding rules and more
|
||||
operations (we may add value comparators) the templates stay
|
||||
mostly the same, thus scaling linearly with size of module.
|
||||
Whereas the codegen backend scales linear with the product of
|
||||
module size and number of encoding rules supported.
|
||||
More importantly, currently only the template backend supports
|
||||
automatic decoding of open types via X.681/X.682/X.683
|
||||
annotations.
|
||||
.It Fl Fl prefix-enum
|
||||
This option should be removed because ENUMERATED types should
|
||||
always have their labels prefixed.
|
||||
@@ -97,34 +210,135 @@ Use RFC1510, non-standard handling of
|
||||
types.
|
||||
.It Fl Fl decode-dce-ber
|
||||
.It Fl Fl support-ber
|
||||
.It Fl Fl preserve-binary=TYPE-NAME
|
||||
Generate
|
||||
.It Fl Fl preserve-binary=TYPE
|
||||
Generate a field named
|
||||
.Sq _save
|
||||
fields in structs to preserve the original encoding of some
|
||||
sub-value.
|
||||
This is useful for cryptographic applications to avoid having to
|
||||
re-encode values to check signatures, etc.
|
||||
.It Fl Fl sequence=TYPE-NAME
|
||||
Generate add/remove functions for
|
||||
in the C struct generated for the named
|
||||
.Ar TYPE .
|
||||
This field is used to preserve the original encoding of the value
|
||||
of the
|
||||
.Ar TYPE .
|
||||
.Pp
|
||||
This is useful for cryptographic applications so that they can
|
||||
check signatures of encoded values as-received without having to
|
||||
re-encode those values.
|
||||
.Pp
|
||||
For example, the TBSCertificate type should have values preserved
|
||||
so that Certificate validation can check the signatureValue over
|
||||
the tbsCertificate's value as-received.
|
||||
.Pp
|
||||
The alternative of encoding a value to check a signature of it is
|
||||
brittle.
|
||||
For types where non-canonical encodings (such as BER) are
|
||||
allowed, this alternative is bound to fail.
|
||||
Thus the point of this option.
|
||||
.It Fl Fl sequence=TYPE
|
||||
Generate add/remove functions for the named ASN.1
|
||||
.Ar TYPE
|
||||
which must be a
|
||||
.Sq SET OF
|
||||
and
|
||||
or
|
||||
.Sq SEQUENCE OF
|
||||
types.
|
||||
.It Fl Fl decorate=TYPE-NAME:FIELD-TYPE:field-name[?]
|
||||
Add to the
|
||||
.Va TYPE-NAME
|
||||
SET or SEQUENCE type a field of the given
|
||||
.Va FIELD-TYPE
|
||||
and
|
||||
.Va field-name ,
|
||||
but do not encode or decode this field.
|
||||
type.
|
||||
.It Fl Fl decorate=TYPE:FTYPE:fname[?]
|
||||
Add to the C struct generated for the given ASN.1 type named
|
||||
.Ar TYPE
|
||||
a
|
||||
.Dq hidden
|
||||
field named
|
||||
.Ar fname
|
||||
of the given ASN.1 type
|
||||
.Ar FTYPE ,
|
||||
but do not encode or decode it.
|
||||
The
|
||||
.Ar TYPE
|
||||
must be a SET or SEQUENCE type.
|
||||
If the
|
||||
.Va field-name
|
||||
ends in a question mark, then treat the field as OPTIONAL for
|
||||
the purposes of copy/free function stubs.
|
||||
This is useful for adding fields to existing types that can be used
|
||||
for internal bookkeeping but which do not affect interoperability
|
||||
because they are not encoded.
|
||||
.Ar fname
|
||||
ends in a question mark, then treat the field as OPTIONAL.
|
||||
.Pp
|
||||
This is useful for adding fields to existing types that can be
|
||||
used for internal bookkeeping but which do not affect
|
||||
interoperability because they are neither encoded nor decoded.
|
||||
For example, one might decorate a request type with state needed
|
||||
during processing of the request.
|
||||
.It Fl Fl decorate=TYPE:void:fname:::
|
||||
Add to the C struct generated for the given ASN.1 type named
|
||||
.Ar TYPE
|
||||
a
|
||||
.Dq hidden
|
||||
field named
|
||||
.Ar fname
|
||||
of type
|
||||
.Sq void *
|
||||
(but do not encode or decode it.
|
||||
.Pp
|
||||
The destructor and copy constructor functions generated by this
|
||||
compiler for
|
||||
.Ar TYPE
|
||||
will set this field to the
|
||||
.Sq NULL
|
||||
pointer.
|
||||
.Pp
|
||||
This is useful for adding fields to existing types that can be
|
||||
used for internal bookkeeping but which do not affect
|
||||
interoperability because they are neither encoded nor decoded.
|
||||
For example, one might decorate a request type with state needed
|
||||
during processing of the request.
|
||||
.It Fl Fl decorate=TYPE:FTYPE:fname[?]:copyfn:freefn:header
|
||||
Add to the C struct generated for the given ASN.1 type named
|
||||
.Ar TYPE
|
||||
a
|
||||
.Dq hidden
|
||||
field named
|
||||
.Ar fname
|
||||
of the given external C type
|
||||
.Ar FTYPE ,
|
||||
declared in the given
|
||||
.Ar header
|
||||
but do not encode or decode this field.
|
||||
The
|
||||
.Ar TYPE
|
||||
must be a SET or SEQUENCE type.
|
||||
If the
|
||||
.Ar fname
|
||||
ends in a question mark, then treat the field as OPTIONAL.
|
||||
.Pp
|
||||
This is useful for adding fields to existing types that can be
|
||||
used for internal bookkeeping but which do not affect
|
||||
interoperability because they are neither encoded nor decoded.
|
||||
For example, one might decorate a request type with state needed
|
||||
during processing of the request.
|
||||
.Pp
|
||||
The
|
||||
.Ar header
|
||||
must include double quotes or angle brackets.
|
||||
The
|
||||
.Ar copyfn
|
||||
must be the name of a copy constructor function that takes a
|
||||
pointer to a source value of the type, and a pointer to a
|
||||
destination value of the type, in that order, and which returns
|
||||
zero on success or else a system error code on failure.
|
||||
The
|
||||
.Ar freefn
|
||||
must be the name of a destructor function that takes a pointer to
|
||||
a value of the type and which releases resources referenced by
|
||||
that value, but does not free the value itself (the run-time
|
||||
allocates this value as needed from the C heap).
|
||||
The
|
||||
.Ar freefn
|
||||
should also reset the value to a pristine state (such as all
|
||||
zeros).
|
||||
.Pp
|
||||
If the
|
||||
.Ar copyfn
|
||||
and
|
||||
.Ar freefn
|
||||
are empty strings, then the decoration field will neither be
|
||||
copied nor freed by the functions generated for the
|
||||
.Ar TYPE .
|
||||
.Pp
|
||||
NOTE: At this time only one decoration may be specified per type.
|
||||
.It Fl Fl one-code-file
|
||||
Generate a single source code file.
|
||||
Otherwise a separate code file will be generated for every type.
|
||||
@@ -149,3 +363,10 @@ for common type defintions.
|
||||
.It Fl Fl version
|
||||
.It Fl Fl help
|
||||
.El
|
||||
.Sh NOTES
|
||||
Note that only one decoration per-SET/SEQUENCE type may be
|
||||
specified.
|
||||
.Pp
|
||||
Currently only the template backend supports automatic encoding
|
||||
and decoding of open types via the ASN.1 Information Object
|
||||
System and X.681/X.682/X.683 annotations.
|
||||
|
@@ -34,7 +34,8 @@
|
||||
*/
|
||||
|
||||
#define IF_OPT_COMPARE(ac,bc,e) \
|
||||
if (((ac)->e == NULL && (bc)->e != NULL) || (((ac)->e != NULL && (bc)->e == NULL))) return 1; if ((ac)->e)
|
||||
if (((ac)->e == NULL && (bc)->e != NULL) || (((ac)->e != NULL && (bc)->e == NULL))) return 1; \
|
||||
if ((ac)->e)
|
||||
#define COMPARE_OPT_STRING(ac,bc,e) \
|
||||
do { if (strcmp(*(ac)->e, *(bc)->e) != 0) return 1; } while(0)
|
||||
#define COMPARE_OPT_OCTET_STRING(ac,bc,e) \
|
||||
|
@@ -51,6 +51,24 @@
|
||||
|
||||
#include "check-common.h"
|
||||
|
||||
static int my_copy_vers_called;
|
||||
static int my_free_vers_called;
|
||||
|
||||
int
|
||||
my_copy_vers(const my_vers *from, my_vers *to)
|
||||
{
|
||||
my_copy_vers_called++;
|
||||
*to = *from;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
my_free_vers(my_vers *v)
|
||||
{
|
||||
my_free_vers_called++;
|
||||
v->v = -1;
|
||||
}
|
||||
|
||||
static char *lha_principal[] = { "lha" };
|
||||
static char *lharoot_princ[] = { "lha", "root" };
|
||||
static char *datan_princ[] = { "host", "nutcracker.e.kth.se" };
|
||||
@@ -1021,12 +1039,15 @@ static int
|
||||
test_decorated(void)
|
||||
{
|
||||
TESTNotDecorated tnd;
|
||||
TESTDecorated td;
|
||||
TESTDecorated3 td3, td3_copy;
|
||||
TESTDecorated2 td2, td2_copy;
|
||||
TESTDecorated td, td_copy;
|
||||
size_t len, size;
|
||||
void *ptr;
|
||||
int ret;
|
||||
|
||||
memset(&td, 0, sizeof(td));
|
||||
memset(&td2, 0, sizeof(td2));
|
||||
memset(&tnd, 0, sizeof(tnd));
|
||||
|
||||
td.version = 3;
|
||||
@@ -1043,6 +1064,7 @@ test_decorated(void)
|
||||
warnx("could not decode a TESTDecorated struct as TESTNotDecorated");
|
||||
return 1;
|
||||
}
|
||||
free(ptr);
|
||||
if (size != len) {
|
||||
warnx("TESTDecorated encoded size mismatch");
|
||||
return 1;
|
||||
@@ -1051,11 +1073,104 @@ test_decorated(void)
|
||||
warnx("TESTDecorated did not decode as a TESTNotDecorated correctly");
|
||||
return 1;
|
||||
}
|
||||
if (copy_TESTDecorated(&td, &td_copy)) {
|
||||
warnx("copy_TESTDecorated() failed");
|
||||
return 1;
|
||||
}
|
||||
if (td.version != td_copy.version) {
|
||||
warnx("copy_TESTDecorated() did not work correctly (1)");
|
||||
return 1;
|
||||
}
|
||||
if (td_copy.version2 == NULL || *td.version2 != *td_copy.version2) {
|
||||
warnx("copy_TESTDecorated() did not work correctly (2)");
|
||||
return 1;
|
||||
}
|
||||
free_TESTDecorated(&td_copy);
|
||||
free_TESTDecorated(&td);
|
||||
if (td.version2) {
|
||||
warnx("free_TESTDecorated() did not work correctly");
|
||||
return 1;
|
||||
}
|
||||
|
||||
td2.version = 3;
|
||||
td2.version2.v = 5;
|
||||
ASN1_MALLOC_ENCODE(TESTDecorated2, ptr, len, &td2, &size, ret);
|
||||
if (ret) {
|
||||
warnx("could not encode a TESTDecorated2 struct");
|
||||
return 1;
|
||||
}
|
||||
ret = decode_TESTNotDecorated(ptr, len, &tnd, &size);
|
||||
if (ret) {
|
||||
warnx("could not decode a TESTDecorated2 struct as TESTNotDecorated2");
|
||||
return 1;
|
||||
}
|
||||
free(ptr);
|
||||
if (size != len) {
|
||||
warnx("TESTDecorated2 encoded size mismatch");
|
||||
return 1;
|
||||
}
|
||||
if (td2.version != tnd.version) {
|
||||
warnx("TESTDecorated2 did not decode as a TESTNotDecorated correctly");
|
||||
return 1;
|
||||
}
|
||||
if (copy_TESTDecorated2(&td2, &td2_copy)) {
|
||||
warnx("copy_TESTDecorated2() failed");
|
||||
return 1;
|
||||
}
|
||||
if (td2.version != td2_copy.version || !my_copy_vers_called) {
|
||||
warnx("copy_TESTDecorated2() did not work correctly (1)");
|
||||
return 1;
|
||||
}
|
||||
if (td2.version2.v != td2_copy.version2.v) {
|
||||
warnx("copy_TESTDecorated2() did not work correctly (2)");
|
||||
return 1;
|
||||
}
|
||||
free_TESTDecorated2(&td2_copy);
|
||||
free_TESTDecorated2(&td2);
|
||||
if (td2.version2.v != 0 || my_free_vers_called != 2) {
|
||||
warnx("free_TESTDecorated2() did not work correctly");
|
||||
return 1;
|
||||
}
|
||||
|
||||
td3.version = 3;
|
||||
td3.privthing = &td;
|
||||
ASN1_MALLOC_ENCODE(TESTDecorated3, ptr, len, &td3, &size, ret);
|
||||
if (ret) {
|
||||
warnx("could not encode a TESTDecorated3 struct");
|
||||
return 1;
|
||||
}
|
||||
ret = decode_TESTNotDecorated(ptr, len, &tnd, &size);
|
||||
if (ret) {
|
||||
warnx("could not decode a TESTDecorated3 struct as TESTNotDecorated3");
|
||||
return 1;
|
||||
}
|
||||
free(ptr);
|
||||
if (size != len) {
|
||||
warnx("TESTDecorated3 encoded size mismatch");
|
||||
return 1;
|
||||
}
|
||||
if (td3.version != tnd.version) {
|
||||
warnx("TESTDecorated3 did not decode as a TESTNotDecorated correctly");
|
||||
return 1;
|
||||
}
|
||||
if (copy_TESTDecorated3(&td3, &td3_copy)) {
|
||||
warnx("copy_TESTDecorated3() failed");
|
||||
return 1;
|
||||
}
|
||||
if (td3.version != td3_copy.version) {
|
||||
warnx("copy_TESTDecorated3() did not work correctly (1)");
|
||||
return 1;
|
||||
}
|
||||
if (td3_copy.privthing != 0) {
|
||||
warnx("copy_TESTDecorated3() did not work correctly (2)");
|
||||
return 1;
|
||||
}
|
||||
free_TESTDecorated3(&td3_copy);
|
||||
free_TESTDecorated3(&td3);
|
||||
if (td3.privthing != 0) {
|
||||
warnx("free_TESTDecorated3() did not work correctly");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1521,7 +1636,7 @@ static int
|
||||
check_seq(void)
|
||||
{
|
||||
TESTSeqOf seq;
|
||||
TESTInteger i;
|
||||
TESTInteger i = 0;
|
||||
int ret;
|
||||
|
||||
seq.val = NULL;
|
||||
|
9
lib/asn1/check-gen.h
Normal file
9
lib/asn1/check-gen.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#ifndef _CHECK_GEN_H
|
||||
#define _CHECK_GEN_H
|
||||
typedef struct my_vers_s {
|
||||
int v;
|
||||
} my_vers;
|
||||
|
||||
int my_copy_vers(const my_vers *, my_vers *);
|
||||
void my_free_vers(my_vers *);
|
||||
#endif /* _CHECK_GEN_H */
|
@@ -48,6 +48,19 @@
|
||||
#include "check-common.h"
|
||||
#include "der_locl.h"
|
||||
|
||||
int
|
||||
my_copy_vers(const my_vers *from, my_vers *to)
|
||||
{
|
||||
*to = *from;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
my_free_vers(my_vers *v)
|
||||
{
|
||||
v->v = -1;
|
||||
}
|
||||
|
||||
static int
|
||||
cmp_dummy (void *a, void *b)
|
||||
{
|
||||
|
@@ -1365,12 +1365,13 @@ define_type(int level, const char *name, const char *basename, Type *pt, Type *t
|
||||
case TSet:
|
||||
case TSequence: {
|
||||
Member *m;
|
||||
char *ft, *fn;
|
||||
int deco_opt;
|
||||
struct decoration deco;
|
||||
|
||||
getnewbasename(&newbasename, typedefp || level == 0, basename, name);
|
||||
|
||||
space(level);
|
||||
|
||||
(void) decorate_type(newbasename, &deco);
|
||||
fprintf (headerfile, "struct %s {\n", newbasename);
|
||||
fprintf(jsonfile, "\"ttype\":\"%s\",\"extensible\":%s,"
|
||||
"\"ctype\":\"struct %s\"",
|
||||
@@ -1407,15 +1408,22 @@ define_type(int level, const char *name, const char *basename, Type *pt, Type *t
|
||||
fprintf(jsonfile, ",\"opentype\":");
|
||||
define_open_type(level, newbasename, name, basename, t, t);
|
||||
}
|
||||
if (decorate_type(newbasename, &ft, &fn, &deco_opt)) {
|
||||
if (deco.decorated) {
|
||||
space(level + 1);
|
||||
fprintf(headerfile, "%s %s%s;\n", ft, deco_opt ? "*" : "", fn);
|
||||
fprintf(jsonfile, ",\"decorate\":{\"type\":\"%s\",\"name\":\"%s\", \"optional\":%s}", ft, fn, deco_opt ? "true" : "false");
|
||||
free(ft);
|
||||
free(fn);
|
||||
fprintf(headerfile, "%s %s%s;\n", deco.field_type,
|
||||
deco.opt ? "*" : "", deco.field_name);
|
||||
fprintf(jsonfile, ",\"decorate\":{"
|
||||
"\"type\":\"%s\",\"name\":\"%s\",\"optional\":%s,"
|
||||
"\"external\":%s,\"copy_function\":\"%s\","
|
||||
"\"free_function\":\"%s\",\"header_name\":\"%s\"}",
|
||||
deco.field_type, deco.field_name,
|
||||
deco.opt ? "true" : "false", deco.ext ? "true" : "false",
|
||||
deco.copy_function_name, deco.free_function_name,
|
||||
deco.header_name);
|
||||
}
|
||||
space(level);
|
||||
fprintf (headerfile, "} %s;\n", name);
|
||||
free(deco.field_type);
|
||||
break;
|
||||
}
|
||||
case TSetOf:
|
||||
@@ -1627,10 +1635,18 @@ declare_type(const Symbol *s, Type *t, int typedefp)
|
||||
|
||||
switch (t->type) {
|
||||
case TSet:
|
||||
case TSequence:
|
||||
case TSequence: {
|
||||
struct decoration deco;
|
||||
|
||||
getnewbasename(&newbasename, TRUE, s->gen_name, s->gen_name);
|
||||
fprintf(headerfile, "struct %s %s;\n", newbasename, s->gen_name);
|
||||
if (decorate_type(newbasename, &deco) && deco.header_name
|
||||
&& deco.header_name[0]) {
|
||||
fprintf(headerfile, "#include %s\n", deco.header_name);
|
||||
free(deco.field_type);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TSetOf:
|
||||
case TSequenceOf:
|
||||
getnewbasename(&newbasename, TRUE, s->gen_name, s->gen_name);
|
||||
|
@@ -228,10 +228,9 @@ copy_type (const char *from, const char *to, const Type *t, int preserve)
|
||||
void
|
||||
generate_type_copy (const Symbol *s)
|
||||
{
|
||||
struct decoration deco;
|
||||
int preserve = preserve_type(s->name) ? TRUE : FALSE;
|
||||
int save_used_fail = used_fail;
|
||||
int deco_opt;
|
||||
char *ft, *fn;
|
||||
|
||||
used_fail = 0;
|
||||
|
||||
@@ -241,18 +240,41 @@ generate_type_copy (const Symbol *s)
|
||||
"memset(to, 0, sizeof(*to));\n",
|
||||
s->gen_name, s->gen_name, s->gen_name);
|
||||
copy_type ("from", "to", s->type, preserve);
|
||||
if (decorate_type(s->gen_name, &ft, &fn, &deco_opt)) {
|
||||
if (deco_opt) {
|
||||
fprintf(codefile, "if (from->%s) {\n", fn);
|
||||
fprintf(codefile, "(to)->%s = malloc(sizeof(*(to)->%s));\n", fn, fn);
|
||||
fprintf(codefile, "if (copy_%s((from)->%s, (to)->%s)) goto fail;\n", ft, fn, fn);
|
||||
if (decorate_type(s->gen_name, &deco)) {
|
||||
if (deco.ext &&
|
||||
(deco.copy_function_name == NULL ||
|
||||
deco.copy_function_name[0] == '\0')) {
|
||||
/* Decorated with field of external type but no copy function */
|
||||
if (deco.opt || deco.void_star)
|
||||
fprintf(codefile, "(to)->%s = 0;\n", deco.field_name);
|
||||
else
|
||||
fprintf(codefile, "memset(&(to)->%s, 0, sizeof((to)->%s));\n",
|
||||
deco.field_name, deco.field_name);
|
||||
} else if (deco.ext) {
|
||||
/* Decorated with field of external type w/ copy function */
|
||||
if (deco.opt) {
|
||||
fprintf(codefile, "if (from->%s) {\n", deco.field_name);
|
||||
fprintf(codefile, "(to)->%s = malloc(sizeof(*(to)->%s));\n",
|
||||
deco.field_name, deco.field_name);
|
||||
fprintf(codefile, "if (%s((from)->%s, (to)->%s)) goto fail;\n",
|
||||
deco.copy_function_name, deco.field_name, deco.field_name);
|
||||
fprintf(codefile, "}\n");
|
||||
} else {
|
||||
fprintf(codefile, "if (%s(&(from)->%s, &(to)->%s)) goto fail;\n",
|
||||
deco.copy_function_name, deco.field_name, deco.field_name);
|
||||
}
|
||||
} else if (deco.opt) {
|
||||
/* Decorated with optional field of ASN.1 type */
|
||||
fprintf(codefile, "if (from->%s) {\n", deco.field_name);
|
||||
fprintf(codefile, "(to)->%s = malloc(sizeof(*(to)->%s));\n", deco.field_name, deco.field_name);
|
||||
fprintf(codefile, "if (copy_%s((from)->%s, (to)->%s)) goto fail;\n", deco.field_type, deco.field_name, deco.field_name);
|
||||
fprintf(codefile, "}\n");
|
||||
} else {
|
||||
fprintf(codefile, "if (copy_%s(&(from)->%s, &(to)->%s)) goto fail;\n", ft, fn, fn);
|
||||
/* Decorated with required field of ASN.1 type */
|
||||
fprintf(codefile, "if (copy_%s(&(from)->%s, &(to)->%s)) goto fail;\n", deco.field_type, deco.field_name, deco.field_name);
|
||||
}
|
||||
used_fail++;
|
||||
free(ft);
|
||||
free(fn);
|
||||
free(deco.field_type);
|
||||
}
|
||||
fprintf (codefile, "return 0;\n");
|
||||
|
||||
|
@@ -178,9 +178,8 @@ free_type (const char *name, const Type *t, int preserve)
|
||||
void
|
||||
generate_type_free (const Symbol *s)
|
||||
{
|
||||
struct decoration deco;
|
||||
int preserve = preserve_type(s->name) ? TRUE : FALSE;
|
||||
int deco_opt;
|
||||
char *ft, *fn;
|
||||
|
||||
fprintf (codefile, "void ASN1CALL\n"
|
||||
"free_%s(%s *data)\n"
|
||||
@@ -188,18 +187,46 @@ generate_type_free (const Symbol *s)
|
||||
s->gen_name, s->gen_name);
|
||||
|
||||
free_type ("data", s->type, preserve);
|
||||
if (decorate_type(s->gen_name, &ft, &fn, &deco_opt)) {
|
||||
if (deco_opt) {
|
||||
fprintf(codefile, "if ((data)->%s) {\n", fn);
|
||||
fprintf(codefile, "free_%s((data)->%s);\n", ft, fn);
|
||||
fprintf(codefile, "free((data)->%s);\n", fn);
|
||||
fprintf(codefile, "(data)->%s = NULL;\n", fn);
|
||||
if (decorate_type(s->gen_name, &deco)) {
|
||||
if (deco.ext &&
|
||||
(deco.free_function_name == NULL ||
|
||||
deco.free_function_name[0] == '\0')) {
|
||||
/* Decorated with field of external type but no free function */
|
||||
if (deco.opt || deco.void_star)
|
||||
fprintf(codefile, "(data)->%s = 0;\n", deco.field_name);
|
||||
else
|
||||
fprintf(codefile,
|
||||
"memset(&(data)->%s, 0, sizeof((data)->%s));\n",
|
||||
deco.field_name, deco.field_name);
|
||||
} else if (deco.ext) {
|
||||
/* Decorated with field of external type w/ free function */
|
||||
if (deco.opt) {
|
||||
fprintf(codefile, "if ((data)->%s) {\n", deco.field_name);
|
||||
fprintf(codefile, "%s((data)->%s);\n",
|
||||
deco.free_function_name, deco.field_name);
|
||||
fprintf(codefile, "(data)->%s = 0;\n", deco.field_name);
|
||||
fprintf(codefile, "}\n");
|
||||
} else {
|
||||
fprintf(codefile, "%s(&(data)->%s);\n",
|
||||
deco.free_function_name, deco.field_name);
|
||||
fprintf(codefile,
|
||||
"memset(&(data)->%s, 0, sizeof((data)->%s));\n",
|
||||
deco.field_name, deco.field_name);
|
||||
}
|
||||
} else if (deco.opt) {
|
||||
/* Decorated with optional field of ASN.1 type */
|
||||
fprintf(codefile, "if ((data)->%s) {\n", deco.field_name);
|
||||
fprintf(codefile, "free_%s((data)->%s);\n",
|
||||
deco.field_type, deco.field_name);
|
||||
fprintf(codefile, "free((data)->%s);\n", deco.field_name);
|
||||
fprintf(codefile, "(data)->%s = NULL;\n", deco.field_name);
|
||||
fprintf(codefile, "}\n");
|
||||
} else {
|
||||
fprintf(codefile, "free_%s(&(data)->%s);\n", ft, fn);
|
||||
/* Decorated with required field of ASN.1 type */
|
||||
fprintf(codefile, "free_%s(&(data)->%s);\n",
|
||||
deco.field_type, deco.field_name);
|
||||
}
|
||||
free(ft);
|
||||
free(fn);
|
||||
free(deco.field_type);
|
||||
}
|
||||
fprintf (codefile, "}\n\n");
|
||||
}
|
||||
|
@@ -144,7 +144,19 @@ int is_tagged_type(const Type *);
|
||||
|
||||
int preserve_type(const char *);
|
||||
int seq_type(const char *);
|
||||
int decorate_type(const char *, char **, char **, int *);
|
||||
|
||||
struct decoration {
|
||||
char *field_type; /* C type name */
|
||||
char *field_name; /* C struct field name */
|
||||
char *copy_function_name; /* copy constructor function name */
|
||||
char *free_function_name; /* destructor function name */
|
||||
char *header_name; /* header name */
|
||||
unsigned int decorated:1;
|
||||
unsigned int opt:1; /* optional */
|
||||
unsigned int ext:1; /* external */
|
||||
unsigned int void_star:1; /* external, void * */
|
||||
};
|
||||
int decorate_type(const char *, struct decoration *);
|
||||
|
||||
void generate_header_of_codefile(const char *);
|
||||
void close_codefile(void);
|
||||
|
@@ -449,7 +449,10 @@ add_line_pointer(struct templatehead *t,
|
||||
errx(1, "malloc");
|
||||
va_end(ap);
|
||||
|
||||
q = add_line(t, "{ %s, %s, asn1_%s }", tt, offset, ptr);
|
||||
if (ptr[0] == '&')
|
||||
q = add_line(t, "{ %s, %s, %s }", tt, offset, ptr);
|
||||
else
|
||||
q = add_line(t, "{ %s, %s, asn1_%s }", tt, offset, ptr);
|
||||
q->tt = tt;
|
||||
q->offset = strdup(offset);
|
||||
q->ptr = strdup(ptr);
|
||||
@@ -1061,10 +1064,9 @@ template_members(struct templatehead *temp,
|
||||
Field *opentypefield = NULL;
|
||||
Field *typeidfield = NULL;
|
||||
Member *m;
|
||||
struct decoration deco;
|
||||
size_t i = 0, typeididx = 0, opentypeidx = 0;
|
||||
int is_array_of_open_type = 0;
|
||||
int deco_opt;
|
||||
char *ft, *fn;
|
||||
|
||||
if (isstruct && t->actual_parameter)
|
||||
get_open_type_defn_fields(t, &typeidmember, &opentypemember,
|
||||
@@ -1104,15 +1106,29 @@ template_members(struct templatehead *temp,
|
||||
typeidfield, opentypefield, opentypemember,
|
||||
is_array_of_open_type);
|
||||
|
||||
if (decorate_type(basetype, &ft, &fn, &deco_opt)) {
|
||||
if (decorate_type(basetype, &deco)) {
|
||||
char *poffset2;
|
||||
|
||||
poffset2 = partial_offset(basetype, fn, 1, isstruct);
|
||||
add_line_pointer(temp, ft, poffset2, "A1_OP_TYPE_DECORATE %s",
|
||||
deco_opt ? "|A1_FLAG_OPTIONAL" : "");
|
||||
poffset2 = partial_offset(basetype, deco.field_name, 1, isstruct);
|
||||
|
||||
if (deco.ext) {
|
||||
char *ptr = NULL;
|
||||
|
||||
/* Decorated with external C type */
|
||||
if (asprintf(&ptr, "&asn1_extern_%s_%s",
|
||||
basetype, deco.field_type) == -1 || ptr == NULL)
|
||||
err(1, "out of memory");
|
||||
add_line_pointer(temp, ptr, poffset2,
|
||||
"A1_OP_TYPE_DECORATE_EXTERN %s",
|
||||
deco.opt && !deco.void_star ? "|A1_FLAG_OPTIONAL" : "");
|
||||
free(ptr);
|
||||
} else
|
||||
/* Decorated with a templated ASN.1 type */
|
||||
add_line_pointer(temp, deco.field_type, poffset2,
|
||||
"A1_OP_TYPE_DECORATE %s",
|
||||
deco.opt ? "|A1_FLAG_OPTIONAL" : "");
|
||||
free(poffset2);
|
||||
free(ft);
|
||||
free(fn);
|
||||
free(deco.field_type);
|
||||
}
|
||||
|
||||
if (isstruct)
|
||||
@@ -1125,10 +1141,9 @@ template_members(struct templatehead *temp,
|
||||
Field *opentypefield = NULL;
|
||||
Field *typeidfield = NULL;
|
||||
Member *m;
|
||||
struct decoration deco;
|
||||
size_t i = 0, typeididx = 0, opentypeidx = 0;
|
||||
int is_array_of_open_type = 0;
|
||||
int deco_opt;
|
||||
char *ft, *fn;
|
||||
|
||||
if (isstruct && t->actual_parameter)
|
||||
get_open_type_defn_fields(t, &typeidmember, &opentypemember,
|
||||
@@ -1168,15 +1183,29 @@ template_members(struct templatehead *temp,
|
||||
typeidfield, opentypefield, opentypemember,
|
||||
is_array_of_open_type);
|
||||
|
||||
if (decorate_type(basetype, &ft, &fn, &deco_opt)) {
|
||||
if (decorate_type(basetype, &deco)) {
|
||||
char *poffset2;
|
||||
|
||||
poffset2 = partial_offset(basetype, fn, 1, isstruct);
|
||||
add_line_pointer(temp, ft, poffset2, "A1_OP_TYPE_DECORATE %s",
|
||||
deco_opt ? "|A1_FLAG_OPTIONAL" : "");
|
||||
poffset2 = partial_offset(basetype, deco.field_name, 1, isstruct);
|
||||
|
||||
if (deco.ext) {
|
||||
char *ptr = NULL;
|
||||
|
||||
/* Decorated with external C type */
|
||||
if (asprintf(&ptr, "&asn1_extern_%s_%s",
|
||||
basetype, deco.field_type) == -1 || ptr == NULL)
|
||||
err(1, "out of memory");
|
||||
add_line_pointer(temp, ptr, poffset2,
|
||||
"A1_OP_TYPE_DECORATE_EXTERN %s",
|
||||
deco.opt && !deco.void_star ? "|A1_FLAG_OPTIONAL" : "");
|
||||
free(ptr);
|
||||
} else
|
||||
/* Decorated with a templated ASN.1 type */
|
||||
add_line_pointer(temp, deco.field_type, poffset2,
|
||||
"A1_OP_TYPE_DECORATE %s",
|
||||
deco.opt ? "|A1_FLAG_OPTIONAL" : "");
|
||||
free(poffset2);
|
||||
free(ft);
|
||||
free(fn);
|
||||
free(deco.field_type);
|
||||
}
|
||||
|
||||
if (isstruct)
|
||||
@@ -1491,12 +1520,30 @@ generate_template(const Symbol *s)
|
||||
{
|
||||
FILE *f = get_code_file();
|
||||
const char *dupname;
|
||||
struct decoration deco;
|
||||
|
||||
if (use_extern(s)) {
|
||||
gen_extern_stubs(f, s->gen_name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (decorate_type(s->gen_name, &deco) && deco.ext) {
|
||||
fprintf(f,
|
||||
"static const struct asn1_type_func asn1_extern_%s_%s = {\n"
|
||||
"\t(asn1_type_encode)0,\n"
|
||||
"\t(asn1_type_decode)0,\n"
|
||||
"\t(asn1_type_length)0,\n"
|
||||
"\t(asn1_type_copy)%s,\n"
|
||||
"\t(asn1_type_release)%s,\n"
|
||||
"\t(asn1_type_print)0,\n"
|
||||
"\tsizeof(%s)\n"
|
||||
"};\n", s->gen_name, deco.field_type,
|
||||
deco.copy_function_name && deco.copy_function_name[0] ? deco.copy_function_name : "0",
|
||||
deco.free_function_name && deco.free_function_name[0] ? deco.free_function_name : "0",
|
||||
deco.void_star ? "void *" : deco.field_type);
|
||||
free(deco.field_type);
|
||||
}
|
||||
|
||||
generate_template_type(s->gen_name, &dupname, s->name, s->gen_name, NULL, s->type, 0, 0, 1);
|
||||
|
||||
fprintf(f,
|
||||
|
177
lib/asn1/main.c
177
lib/asn1/main.c
@@ -41,60 +41,134 @@ static getarg_strings preserve;
|
||||
static getarg_strings seq;
|
||||
static getarg_strings decorate;
|
||||
|
||||
static int
|
||||
strcmp4qsort(const void *ap, const void *bp)
|
||||
{
|
||||
return strcmp(*(const char **)ap, *(const char **)bp);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
bsearch_strings(struct getarg_strings *strs, const char *s,
|
||||
size_t prefix_len, char sep)
|
||||
{
|
||||
ssize_t right = (ssize_t)strs->num_strings - 1;
|
||||
ssize_t left = 0;
|
||||
|
||||
if (strs->num_strings == 0)
|
||||
return -1;
|
||||
|
||||
while (left <= right) {
|
||||
ssize_t mid = left + (right - left) / 2;
|
||||
int cmp;
|
||||
|
||||
if (prefix_len) {
|
||||
cmp = strncmp(s, strs->strings[mid], prefix_len);
|
||||
if (cmp == 0 && sep) {
|
||||
if (strs->strings[mid][prefix_len] == sep)
|
||||
return mid;
|
||||
cmp = strncmp(&sep, &strs->strings[mid][prefix_len], 1);
|
||||
}
|
||||
} else
|
||||
cmp = strcmp(s, strs->strings[mid]);
|
||||
if (cmp == 0)
|
||||
return mid;
|
||||
if (cmp < 0)
|
||||
right = mid - 1; /* -1 if `s' is smaller than smallest in strs */
|
||||
else
|
||||
left = mid + 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
preserve_type(const char *p)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < preserve.num_strings; i++)
|
||||
if (strcmp(preserve.strings[i], p) == 0)
|
||||
return 1;
|
||||
return 0;
|
||||
return bsearch_strings(&preserve, p, 0, '\0') > -1;
|
||||
}
|
||||
|
||||
int
|
||||
seq_type(const char *p)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < seq.num_strings; i++)
|
||||
if (strcmp(seq.strings[i], p) == 0)
|
||||
return 1;
|
||||
return 0;
|
||||
return bsearch_strings(&seq, p, 0, '\0') > -1;
|
||||
}
|
||||
|
||||
int
|
||||
decorate_type(const char *p, char **field_type, char **field_name, int *opt)
|
||||
/*
|
||||
* Split `s' on `sep' and fill fs[] with pointers to the substrings.
|
||||
*
|
||||
* Only the first substring is to be freed -- the rest share the same
|
||||
* allocation.
|
||||
*
|
||||
* The last element may contain `sep' chars if there are more fields in `s'
|
||||
* than output locations in `fs[]'.
|
||||
*/
|
||||
static void
|
||||
split_str(const char *s, char sep, char ***fs)
|
||||
{
|
||||
size_t plen = strlen(p);
|
||||
size_t i;
|
||||
|
||||
*field_type = NULL;
|
||||
*field_name = NULL;
|
||||
*opt = 0;
|
||||
|
||||
for (i = 0; i < decorate.num_strings; i++) {
|
||||
const char *r;
|
||||
fs[0][0] = estrdup(s);
|
||||
for (i = 1; fs[i]; i++) {
|
||||
char *q;
|
||||
|
||||
if (strncmp(decorate.strings[i], p, plen) != 0)
|
||||
continue;
|
||||
if (decorate.strings[i][plen] != ':')
|
||||
errx(1, "--decorate argument missing field type");
|
||||
|
||||
p = &decorate.strings[i][plen + 1];
|
||||
if ((r = strchr(p, ':')) == NULL)
|
||||
errx(1, "--decorate argument missing field name");
|
||||
r++;
|
||||
*field_type = estrdup(p);
|
||||
*(strchr(*field_type, ':')) = '\0';
|
||||
*field_name = estrdup(r);
|
||||
if ((q = strchr(*field_name, '?'))) {
|
||||
*q = '\0';
|
||||
*opt = 1;
|
||||
}
|
||||
return 1;
|
||||
if ((q = strchr(fs[i-1][0], sep)) == NULL)
|
||||
break;
|
||||
*(q++) = '\0';
|
||||
fs[i][0] = q;
|
||||
}
|
||||
return 0;
|
||||
for (; fs[i]; i++)
|
||||
fs[i][0] = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* If `p' is "decorated" with a not-to-be-encoded-or-decoded field,
|
||||
* output the field's typename and fieldname, whether it's optional, whether
|
||||
* it's an ASN.1 type or an "external" type, and if external the names of
|
||||
* functions to copy and free values of that type.
|
||||
*/
|
||||
int
|
||||
decorate_type(const char *p,
|
||||
struct decoration *deco)
|
||||
{
|
||||
ssize_t i;
|
||||
size_t plen = strlen(p);
|
||||
char **s[7];
|
||||
char *junk = NULL;
|
||||
char *cp;
|
||||
|
||||
deco->decorated = 0;
|
||||
deco->field_type = NULL;
|
||||
if ((i = bsearch_strings(&decorate, p, plen, ':')) == -1)
|
||||
return 0;
|
||||
|
||||
deco->decorated = 1;
|
||||
deco->opt = 0;
|
||||
deco->ext = 0;
|
||||
deco->field_name = deco->copy_function_name = deco->free_function_name =
|
||||
deco->header_name = NULL;
|
||||
|
||||
s[0] = &deco->field_type;
|
||||
s[1] = &deco->field_name;
|
||||
s[2] = &deco->copy_function_name;
|
||||
s[3] = &deco->free_function_name;
|
||||
s[4] = &deco->header_name;
|
||||
s[5] = &junk;
|
||||
s[6] = NULL;
|
||||
split_str(decorate.strings[i] + plen + 1, ':', s);
|
||||
|
||||
if (junk || deco->field_type[0] == '\0' || !deco->field_name ||
|
||||
deco->field_name[0] == '\0' || deco->field_name[0] == '?') {
|
||||
errx(1, "Invalidate type decoration specification: --decorate=\"%s\"",
|
||||
decorate.strings[i]);
|
||||
}
|
||||
if ((cp = strchr(deco->field_name, '?'))) {
|
||||
deco->opt = 1;
|
||||
*cp = '\0';
|
||||
}
|
||||
if (deco->copy_function_name)
|
||||
deco->ext = 1;
|
||||
if (deco->ext && strcmp(deco->field_type, "void") == 0)
|
||||
deco->opt = deco->void_star = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const char *
|
||||
@@ -147,11 +221,11 @@ struct getargs args[] = {
|
||||
{ "preserve-binary", 0, arg_strings, &preserve,
|
||||
"Names of types for which to generate _save fields, saving original "
|
||||
"encoding, in containing structures (useful for signature "
|
||||
"verification)", "TYPE-NAME" },
|
||||
"verification)", "TYPE" },
|
||||
{ "sequence", 0, arg_strings, &seq,
|
||||
"Generate add/remove functions for SEQUENCE OF types", "TYPE-NAME" },
|
||||
"Generate add/remove functions for SEQUENCE OF types", "TYPE" },
|
||||
{ "decorate", 0, arg_strings, &decorate,
|
||||
"Generate private field for SEQUENCE/SET type", "TYPE-NAME:FIELD_TYPE:field_name[?]" },
|
||||
"Generate private field for SEQUENCE/SET type", "DECORATION" },
|
||||
{ "one-code-file", 0, arg_flag, &one_code_file, NULL, NULL },
|
||||
{ "gen-name", 0, arg_string, &name,
|
||||
"Name of generated module", "NAME" },
|
||||
@@ -166,7 +240,7 @@ struct getargs args[] = {
|
||||
"Do not generate roken-style units", NULL },
|
||||
{ "type-file", 0, arg_string, &type_file_string,
|
||||
"Name of a C header file to generate includes of for base types",
|
||||
"C-HEADER-FILE" },
|
||||
"FILE" },
|
||||
{ "version", 0, arg_flag, &version_flag, NULL, NULL },
|
||||
{ "help", 0, arg_flag, &help_flag, NULL, NULL }
|
||||
};
|
||||
@@ -175,7 +249,17 @@ int num_args = sizeof(args) / sizeof(args[0]);
|
||||
static void
|
||||
usage(int code)
|
||||
{
|
||||
if (code)
|
||||
dup2(STDERR_FILENO, STDOUT_FILENO);
|
||||
else
|
||||
dup2(STDOUT_FILENO, STDERR_FILENO);
|
||||
arg_printusage(args, num_args, NULL, "[asn1-file [name]]");
|
||||
fprintf(stderr,
|
||||
"\nA DECORATION is one of:\n\n"
|
||||
"\tTYPE:FTYPE:fname[?]\n"
|
||||
"\tTYPE:FTYPE:fname[?]:[copy_function]:[free_function]:header\n"
|
||||
"\tTYPE:void:fname:::\n"
|
||||
"\nSee the manual page.\n");
|
||||
exit(code);
|
||||
}
|
||||
|
||||
@@ -304,6 +388,15 @@ main(int argc, char **argv)
|
||||
#endif
|
||||
}
|
||||
|
||||
if (preserve.num_strings)
|
||||
qsort(preserve.strings, preserve.num_strings, sizeof(preserve.strings[0]),
|
||||
(int (*)(const void *, const void *))strcmp4qsort);
|
||||
if (seq.num_strings)
|
||||
qsort(seq.strings, seq.num_strings, sizeof(seq.strings),
|
||||
(int (*)(const void *, const void *))strcmp4qsort);
|
||||
if (decorate.num_strings)
|
||||
qsort(decorate.strings, decorate.num_strings, sizeof(decorate.strings[0]),
|
||||
(int (*)(const void *, const void *))strcmp4qsort);
|
||||
|
||||
init_generate(file, name);
|
||||
|
||||
|
@@ -774,6 +774,7 @@ _asn1_decode(const struct asn1_template *t, unsigned flags,
|
||||
return ret;
|
||||
break;
|
||||
}
|
||||
case A1_OP_TYPE_DECORATE_EXTERN: break;
|
||||
case A1_OP_TYPE_DECORATE: break;
|
||||
case A1_OP_NAME: break;
|
||||
case A1_OP_DEFVAL:
|
||||
@@ -1418,6 +1419,7 @@ _asn1_encode(const struct asn1_template *t, unsigned char *p, size_t len, const
|
||||
}
|
||||
case A1_OP_NAME: break;
|
||||
case A1_OP_DEFVAL: break;
|
||||
case A1_OP_TYPE_DECORATE_EXTERN: break;
|
||||
case A1_OP_TYPE_DECORATE: break;
|
||||
case A1_OP_TYPE:
|
||||
case A1_OP_TYPE_EXTERN: {
|
||||
@@ -1994,6 +1996,7 @@ _asn1_length(const struct asn1_template *t, const void *data)
|
||||
}
|
||||
case A1_OP_NAME: break;
|
||||
case A1_OP_DEFVAL: break;
|
||||
case A1_OP_TYPE_DECORATE_EXTERN: break;
|
||||
case A1_OP_TYPE_DECORATE: break;
|
||||
case A1_OP_TYPE:
|
||||
case A1_OP_TYPE_EXTERN: {
|
||||
@@ -2256,6 +2259,7 @@ _asn1_free(const struct asn1_template *t, void *data)
|
||||
}
|
||||
case A1_OP_NAME: break;
|
||||
case A1_OP_DEFVAL: break;
|
||||
case A1_OP_TYPE_DECORATE_EXTERN:
|
||||
case A1_OP_TYPE_DECORATE:
|
||||
case A1_OP_TYPE:
|
||||
case A1_OP_TYPE_EXTERN: {
|
||||
@@ -2270,9 +2274,15 @@ _asn1_free(const struct asn1_template *t, void *data)
|
||||
|
||||
if ((t->tt & A1_OP_MASK) == A1_OP_TYPE || (t->tt & A1_OP_MASK) == A1_OP_TYPE_DECORATE) {
|
||||
_asn1_free(t->ptr, el);
|
||||
} else {
|
||||
} else if ((t->tt & A1_OP_MASK) == A1_OP_TYPE_EXTERN) {
|
||||
const struct asn1_type_func *f = t->ptr;
|
||||
(f->release)(el);
|
||||
} else {
|
||||
const struct asn1_type_func *f = t->ptr;
|
||||
if (f->release)
|
||||
(f->release)(el);
|
||||
else
|
||||
memset(el, 0, f->size);
|
||||
}
|
||||
if (t->tt & A1_FLAG_OPTIONAL) {
|
||||
free(el);
|
||||
@@ -2545,6 +2555,7 @@ _asn1_print(const struct asn1_template *t,
|
||||
break;
|
||||
case A1_OP_NAME: break;
|
||||
case A1_OP_DEFVAL: break;
|
||||
case A1_OP_TYPE_DECORATE_EXTERN: break;
|
||||
case A1_OP_TYPE_DECORATE: break; /* We could probably print this though */
|
||||
case A1_OP_TYPE:
|
||||
case A1_OP_TYPE_EXTERN: {
|
||||
@@ -2863,6 +2874,7 @@ _asn1_copy(const struct asn1_template *t, const void *from, void *to)
|
||||
}
|
||||
case A1_OP_NAME: break;
|
||||
case A1_OP_DEFVAL: break;
|
||||
case A1_OP_TYPE_DECORATE_EXTERN:
|
||||
case A1_OP_TYPE_DECORATE:
|
||||
case A1_OP_TYPE:
|
||||
case A1_OP_TYPE_EXTERN: {
|
||||
@@ -2893,9 +2905,15 @@ _asn1_copy(const struct asn1_template *t, const void *from, void *to)
|
||||
if ((t->tt & A1_OP_MASK) == A1_OP_TYPE ||
|
||||
(t->tt & A1_OP_MASK) == A1_OP_TYPE_DECORATE) {
|
||||
ret = _asn1_copy(t->ptr, fel, tel);
|
||||
} else if ((t->tt & A1_OP_MASK) == A1_OP_TYPE_EXTERN) {
|
||||
const struct asn1_type_func *f = t->ptr;
|
||||
ret = (f->copy)(fel, tel);
|
||||
} else {
|
||||
const struct asn1_type_func *f = t->ptr;
|
||||
ret = (f->copy)(fel, tel);
|
||||
if (f->copy)
|
||||
ret = (f->copy)(fel, tel);
|
||||
else
|
||||
memset(tel, 0, f->size);
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
|
@@ -288,12 +288,23 @@ TESTExtensible ::= SEQUENCE {
|
||||
|
||||
TESTDecorated ::= SEQUENCE {
|
||||
version TESTuint32
|
||||
-- gets decorated
|
||||
-- gets decorated with an ASN.1 type (see test.opt)
|
||||
}
|
||||
|
||||
TESTDecorated2 ::= SEQUENCE {
|
||||
version TESTuint32
|
||||
-- gets decorated with a C type (see test.opt)
|
||||
}
|
||||
|
||||
TESTDecorated3 ::= SEQUENCE {
|
||||
version TESTuint32
|
||||
-- gets decorated with a void * field (see test.opt)
|
||||
}
|
||||
|
||||
TESTNotDecorated ::= SEQUENCE {
|
||||
version TESTuint32
|
||||
-- should have the same encoding as TESTDecorated
|
||||
-- should have the same encoding as TESTDecorated, TESTDecorated2, and
|
||||
-- TESTDecorated3
|
||||
}
|
||||
|
||||
END
|
||||
|
@@ -1 +1,4 @@
|
||||
--sequence=TESTSeqOf
|
||||
--decorate=TESTDecorated:TESTuint32:version2?
|
||||
--decorate=TESTDecorated2:my_vers:version2:my_copy_vers:my_free_vers:"check-gen.h"
|
||||
--decorate=TESTDecorated3:void:privthing?:::
|
||||
|
Reference in New Issue
Block a user