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:
Nicolas Williams
2022-01-05 23:16:48 -06:00
parent 10fc7730d0
commit df3e08485b
18 changed files with 958 additions and 244 deletions

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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)

View File

@@ -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.

View File

@@ -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) \

View File

@@ -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
View 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 */

View File

@@ -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)
{

View File

@@ -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);

View File

@@ -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");

View File

@@ -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");
}

View File

@@ -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);

View File

@@ -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,

View File

@@ -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);

View File

@@ -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) {

View File

@@ -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

View File

@@ -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?:::