diff --git a/lib/asn1/Makefile.am b/lib/asn1/Makefile.am index d2eecf7fe..f41c34dca 100644 --- a/lib/asn1/Makefile.am +++ b/lib/asn1/Makefile.am @@ -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 diff --git a/lib/asn1/NTMakefile b/lib/asn1/NTMakefile index 859068251..19255c6cf 100644 --- a/lib/asn1/NTMakefile +++ b/lib/asn1/NTMakefile @@ -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) diff --git a/lib/asn1/README.md b/lib/asn1/README.md index a00dde722..021557ae1 100644 --- a/lib/asn1/README.md +++ b/lib/asn1/README.md @@ -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 diff --git a/lib/asn1/asn1-template.h b/lib/asn1/asn1-template.h index e75734ea2..07c4461e3 100644 --- a/lib/asn1/asn1-template.h +++ b/lib/asn1/asn1-template.h @@ -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) diff --git a/lib/asn1/asn1_compile.1 b/lib/asn1/asn1_compile.1 index 74cf314f1..da3934e1b 100644 --- a/lib/asn1/asn1_compile.1 +++ b/lib/asn1/asn1_compile.1 @@ -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. diff --git a/lib/asn1/check-common.h b/lib/asn1/check-common.h index 97d118997..6ea1f8126 100644 --- a/lib/asn1/check-common.h +++ b/lib/asn1/check-common.h @@ -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) \ diff --git a/lib/asn1/check-gen.c b/lib/asn1/check-gen.c index f49f5e8ed..658d41233 100644 --- a/lib/asn1/check-gen.c +++ b/lib/asn1/check-gen.c @@ -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; diff --git a/lib/asn1/check-gen.h b/lib/asn1/check-gen.h new file mode 100644 index 000000000..df8c4747b --- /dev/null +++ b/lib/asn1/check-gen.h @@ -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 */ diff --git a/lib/asn1/check-template.c b/lib/asn1/check-template.c index 21132c8d1..ef5bd6990 100644 --- a/lib/asn1/check-template.c +++ b/lib/asn1/check-template.c @@ -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) { diff --git a/lib/asn1/gen.c b/lib/asn1/gen.c index fffacffbc..47c2f2ecf 100644 --- a/lib/asn1/gen.c +++ b/lib/asn1/gen.c @@ -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); diff --git a/lib/asn1/gen_copy.c b/lib/asn1/gen_copy.c index 243aa2b0c..e10dc8814 100644 --- a/lib/asn1/gen_copy.c +++ b/lib/asn1/gen_copy.c @@ -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"); diff --git a/lib/asn1/gen_free.c b/lib/asn1/gen_free.c index 6c9424cbf..53f558ee3 100644 --- a/lib/asn1/gen_free.c +++ b/lib/asn1/gen_free.c @@ -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"); } diff --git a/lib/asn1/gen_locl.h b/lib/asn1/gen_locl.h index ccef2acd2..3ad8dfdc7 100644 --- a/lib/asn1/gen_locl.h +++ b/lib/asn1/gen_locl.h @@ -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); diff --git a/lib/asn1/gen_template.c b/lib/asn1/gen_template.c index cfd414b1d..1becbd24d 100644 --- a/lib/asn1/gen_template.c +++ b/lib/asn1/gen_template.c @@ -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, diff --git a/lib/asn1/main.c b/lib/asn1/main.c index 1186648f6..d59f07134 100644 --- a/lib/asn1/main.c +++ b/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); diff --git a/lib/asn1/template.c b/lib/asn1/template.c index ff1357810..d5f1256d5 100644 --- a/lib/asn1/template.c +++ b/lib/asn1/template.c @@ -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) { diff --git a/lib/asn1/test.asn1 b/lib/asn1/test.asn1 index a76152712..753b6ff2e 100644 --- a/lib/asn1/test.asn1 +++ b/lib/asn1/test.asn1 @@ -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 diff --git a/lib/asn1/test.opt b/lib/asn1/test.opt index 500ee4ec8..c4754f43a 100644 --- a/lib/asn1/test.opt +++ b/lib/asn1/test.opt @@ -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?:::