From 40d127109408ebef1dccbcd41c1c4b38efe83213 Mon Sep 17 00:00:00 2001 From: Nicolas Williams Date: Mon, 10 Jan 2022 18:09:07 -0600 Subject: [PATCH] asn1: Expand decoration w/ C types This commits allows `heim_object_t` as a type and causes the generated code to use the `heim_retain()` and `heim_release()` functions for copying and releasing values of such types. Also, now one can have more than one decoration per-type. --- lib/asn1/Makefile.am | 3 + lib/asn1/README.md | 137 +++++++++++++++++++++------------------ lib/asn1/asn1-template.h | 1 + lib/asn1/asn1_compile.1 | 67 +++++++++---------- lib/asn1/check-gen.c | 101 +++++++---------------------- lib/asn1/gen.c | 40 ++++++++---- lib/asn1/gen_copy.c | 14 ++-- lib/asn1/gen_free.c | 14 ++-- lib/asn1/gen_locl.h | 6 +- lib/asn1/gen_template.c | 64 +++++++++++------- lib/asn1/main.c | 130 +++++++++++++++++++++++++++---------- lib/asn1/template.c | 14 +++- lib/asn1/test.asn1 | 15 +---- lib/asn1/test.opt | 5 +- 14 files changed, 335 insertions(+), 276 deletions(-) diff --git a/lib/asn1/Makefile.am b/lib/asn1/Makefile.am index f41c34dca..1c1ec0c07 100644 --- a/lib/asn1/Makefile.am +++ b/lib/asn1/Makefile.am @@ -28,6 +28,7 @@ libasn1_la_LIBADD = \ libasn1template_la_LIBADD = \ libasn1base.la \ @LIB_com_err@ \ + $(LIB_heimbase) \ $(LIBADD_roken) BUILT_SOURCES = \ @@ -188,10 +189,12 @@ check_timegm_LDADD = $(check_der_LDADD) check_gen_template_LDADD = \ libasn1template.la \ + $(LIB_heimbase) \ $(LIB_roken) check_gen_LDADD = \ libasn1template.la \ + $(LIB_heimbase) \ $(LIB_roken) check_ber_LDADD = $(check_gen_LDADD) diff --git a/lib/asn1/README.md b/lib/asn1/README.md index 021557ae1..9efae6eac 100644 --- a/lib/asn1/README.md +++ b/lib/asn1/README.md @@ -14,7 +14,7 @@ annotations](/lib/asn1/README-X681.md). 5. [Limitations](#Limitations) 6. [Compiler Usage](#Compiler-usage) 7. [APIs Generated by the Compiler](#APIs-generated-by-the-compiler) - 8. [asn1_print Usage](#asn1_print-usage) + 8. [`asn1_print` Usage](#asn1_print-usage) 9. [Implementation](#implementation) 10. [Moving From C](#moving-from-c) @@ -166,11 +166,14 @@ In recent times the following features have been added: - Automatic open type traversal, using a subset of X.681/X.682/X.683 for expressing the requisite metadata. + - Decoration of ASN.1 types with "hidden" fields (ones that don't get encoded + or decoded) of ASN.1 or C types. + ## Futures - JER support? - - XDR/OER support? + - XDR/NDR/OER support? - Generate comparators? (lib/hx509 has a half-baked Certificate comparator) @@ -181,6 +184,10 @@ In recent times the following features have been added: - Most of X.690 is supported for decoding, with only DER supported for encoding. + - Dumping of ASN.1 modules as JSON (note: the JSON schema for ASN.1 modules is + not stable yet). This enables analysis using, e.g., `jq` or similar, and + even construction of alternative compilers using `jq` or similar. + - We have an `asn1_print` program that can decode DER from any exported types from any ASN.1 modules committed in Heimdal: @@ -781,9 +788,9 @@ In recent times the following features have been added: We'll fix this eventually.) - Unconstrained integer types have a large integer representation in C that is - not terribly useful in common cases. Range constraints on integer types - cause the compiler to use `int32_t`, `int64_t`, `uint32_t`, and/or - `uint64_t`. + not terribly useful in common cases. Range and member constraints on + integer types cause the compiler to use `int`, `int64_t`, `unsigned int`, + and/or `uint64_t` as appropriate. - The Heimdal ASN.1 compiler currently handles a large subset of X.680, and (in a branch) a small subset of X.681, X.682, and X.683, which manifests as @@ -800,7 +807,7 @@ In recent times the following features have been added: JER this will have the side-effect of printing the wrong type names in some cases because two or more types have the same templates and get deduped.) - - There is an _experimental_ ASN.1 -> JSON feature in the compiler. It + - There is an _experimental_ ASN.1 module -> JSON feature in the compiler. It currently dumps type and value definitions, but not class, or object set definitions. Even for types, it is not complete, and the JSON schema used is subject to change *WITHOUT NOTICE*. @@ -813,7 +820,8 @@ In recent times the following features have been added: ## Limitations - - `asn1_print`'s JSON support is not X.697 (JER) compatible. + - `libasn1`'s and, therefore, `asn1_print`'s JSON support is not X.697 (JER) + compatible. - Control over C types generated is very limited, mainly only for integer types. @@ -839,6 +847,12 @@ In recent times the following features have been added: values of `SET`, `SEQUENCE`, `SET OF`, and `SEQUENCE OF`), is not supported. Values of `CHOICE` types are also not supported. + - There is no way to substitute object sets at run-time. This means that + automatic decoding through open types will spend more CPU cycles than the + application might want, by decoding more types than the application might + care about. The ability to substitute object sets at run-time would require + a change to the APIs generated. + - ... ## Compiler Usage @@ -871,11 +885,8 @@ SYNOPSIS asn1_compile [--template] [--prefix-enum] [--enum-prefix=PREFIX] [--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] + [--decorate=DECORATION] [--one-code-file] [--gen-name=NAME] + [--option-file=FILE] [--original-order] [--no-parse-units] [--type-file=C-HEADER-FILE] [--version] [--help] [FILE.asn1 [NAME]] @@ -883,19 +894,19 @@ DESCRIPTION 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‐ + 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‐ + 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‐ + 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‐ + “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. @@ -907,7 +918,7 @@ DESCRIPTION • 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‐ + • decorate generated ‘struct’ types with fields that are nei- ther encoded nor decoded; etc. @@ -934,7 +945,7 @@ DESCRIPTION • 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‐ + • 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. @@ -956,19 +967,19 @@ DESCRIPTION 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‐ + 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 al‐ + 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 al‐ + This option should be removed because ENUMERATED types should al- ways have their labels prefixed. --encode-rfc1510-bit-string @@ -1000,54 +1011,48 @@ DESCRIPTION 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. + --decorate=ASN1-TYPE:FIELD-ASN1-TYPE:fname[?] + Add to the C struct generated for the given ASN.1 SET or SEQUENCE + type named ASN1-TYPE a “hidden” field named fname of the given + ASN.1 type FIELD-ASN1-TYPE, but do not encode or decode it. If + the fname ends in a question mark, then treat the field as OP- + TIONAL. 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‐ + 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. + --decorate=ASN1-TYPE:heim_object_t:fname + Add to the C struct generated for the given ASN.1 SET or SEQUENCE + type named ASN1-TYPE a “hidden” field named fname of C type + ‘heim_object_t’ values of which will be copied and released with + ‘heim_release()’ and ‘heim_retain()’ respectively. + + --decorate=ASN1-TYPE:void*:fname + Add to the C struct generated for the given ASN.1 SET or SEQUENCE + type named ASN1-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. + compiler for ASN1-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. + --decorate=ASN1-TYPE:FIELD-C-TYPE:fname[?]:[copyfn]:[freefn]:header + Add to the C struct generated for the given ASN.1 SET or SEQUENCE + type named ASN1-TYPE a “hidden” field named fname of the given + external C type FIELD-C-TYPE, declared in the given header but do + not encode or decode this field. If the fname ends in a question + mark, then treat the field as OPTIONAL. 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‐ + 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‐ + 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). @@ -1069,7 +1074,7 @@ DESCRIPTION --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‐ + ASN.1 module. By default the compiler generates types in a topo- logical sort order. --no-parse-units @@ -1085,9 +1090,7 @@ DESCRIPTION --help NOTES - Note that only one decoration per-SET/SEQUENCE type may be specified. - - Currently only the template backend supports automatic encoding and de‐ + 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. @@ -1303,13 +1306,19 @@ See: - Add JER support so we can convert between JER and DER? - - Add XDR support? + - Add XDR support? There are no ASN.1 Encoding Rules based on XDR, but it is + trivial to construct such for at least that subset of ASN.1 for which the + XDR syntax has equivalent semantics. - Add OER support? - - Add NDR support? + - Add NDR support? There are no ASN.1 Encoding Rules based on NDR, but it is + trivial to construct such for at least that subset of ASN.1 for which the + IDL syntax has equivalent semantics. - - Perhaps third parties will contribute more control over generate types? + - Perhaps third parties will contribute more control over generated types? + This may require separate publication of the Heimdal ASN.1 compiler from the + rest of Heimdal. ## Moving From C diff --git a/lib/asn1/asn1-template.h b/lib/asn1/asn1-template.h index 07c4461e3..7c2ace30a 100644 --- a/lib/asn1/asn1-template.h +++ b/lib/asn1/asn1-template.h @@ -150,6 +150,7 @@ #define A1_FLAG_OPTIONAL (0x01000000) #define A1_FLAG_IMPLICIT (0x02000000) #define A1_FLAG_DEFAULT (0x04000000) +#define A1_FLAG_HEIM_OBJ (0x08000000) #define A1_TAG_T(CLASS,TYPE,TAG) ((A1_OP_TAG) | (((CLASS) << 22) | ((TYPE) << 21) | (TAG))) #define A1_TAG_CLASS(x) (((x) >> 22) & 0x3) diff --git a/lib/asn1/asn1_compile.1 b/lib/asn1/asn1_compile.1 index da3934e1b..b9a70c397 100644 --- a/lib/asn1/asn1_compile.1 +++ b/lib/asn1/asn1_compile.1 @@ -48,9 +48,7 @@ .Op Fl Fl support-ber .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 decorate=DECORATION .Op Fl Fl one-code-file .Op Fl Fl gen-name=NAME .Op Fl Fl option-file=FILE @@ -240,19 +238,17 @@ which must be a or .Sq SEQUENCE OF type. -.It Fl Fl decorate=TYPE:FTYPE:fname[?] -Add to the C struct generated for the given ASN.1 type named -.Ar TYPE +.It Fl Fl decorate=ASN1-TYPE:FIELD-ASN1-TYPE:fname[?] +Add to the C struct generated for the given ASN.1 SET or SEQUENCE type +named +.Ar ASN1-TYPE a .Dq hidden field named .Ar fname of the given ASN.1 type -.Ar FTYPE , +.Ar FIELD-ASN1-TYPE , but do not encode or decode it. -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. @@ -262,9 +258,25 @@ 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 +.It Fl Fl decorate=ASN1-TYPE:heim_object_t:fname +Add to the C struct generated for the given ASN.1 SET or SEQUENCE type +named +.Ar ASN1-TYPE +a +.Dq hidden +field named +.Ar fname +of C type +.Sq heim_object_t +values of which will be copied and released with +.Sq heim_retain() +and +.Sq heim_release() +respectively. +.It Fl Fl decorate=ASN1-TYPE:void*:fname +Add to the C struct generated for the given ASN.1 SET or SEQUENCE type +named +.Ar ASN1-TYPE a .Dq hidden field named @@ -275,41 +287,27 @@ of type .Pp The destructor and copy constructor functions generated by this compiler for -.Ar TYPE +.Ar ASN1-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 +.It Fl Fl decorate=ASN1-TYPE:FIELD-C-TYPE:fname[?]:[copyfn]:[freefn]:header +Add to the C struct generated for the given ASN.1 SET or SEQUENCE type +named +.Ar ASN1-TYPE a .Dq hidden field named .Ar fname of the given external C type -.Ar FTYPE , +.Ar FIELD-C-TYPE , 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. @@ -364,9 +362,6 @@ for common type defintions. .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-gen.c b/lib/asn1/check-gen.c index 658d41233..e49ffd75a 100644 --- a/lib/asn1/check-gen.c +++ b/lib/asn1/check-gen.c @@ -1039,18 +1039,18 @@ static int test_decorated(void) { TESTNotDecorated tnd; - 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; + td.version3.v = 5; + td.privthing = &td; + td.privobj = heim_string_create("foo"); if ((td.version2 = malloc(sizeof(*td.version2))) == NULL) errx(1, "out of memory"); *td.version2 = 5; @@ -1085,90 +1085,35 @@ test_decorated(void) warnx("copy_TESTDecorated() did not work correctly (2)"); return 1; } + if (td.version3.v != td_copy.version3.v) { + warnx("copy_TESTDecorated() did not work correctly (3)"); + return 1; + } + if (td_copy.privthing != 0) { + warnx("copy_TESTDecorated() did not work correctly (4)"); + return 1; + } + if (td_copy.privobj != td.privobj) { + warnx("copy_TESTDecorated() did not work correctly (5)"); + return 1; + } + free_TESTDecorated(&td_copy); free_TESTDecorated(&td); if (td.version2) { - warnx("free_TESTDecorated() did not work correctly"); + warnx("free_TESTDecorated() did not work correctly (1)"); 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"); + if (td.version3.v != 0 || my_free_vers_called != 2) { + warnx("free_TESTDecorated() did not work correctly (2)"); return 1; } - ret = decode_TESTNotDecorated(ptr, len, &tnd, &size); - if (ret) { - warnx("could not decode a TESTDecorated2 struct as TESTNotDecorated2"); + if (td.privthing != 0) { + warnx("free_TESTDecorated() did not work correctly (3)"); 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"); + if (td.privobj != 0) { + warnx("free_TESTDecorated() did not work correctly (4)"); return 1; } return 0; diff --git a/lib/asn1/gen.c b/lib/asn1/gen.c index 1c040cc52..69cc79da1 100644 --- a/lib/asn1/gen.c +++ b/lib/asn1/gen.c @@ -1366,12 +1366,13 @@ define_type(int level, const char *name, const char *basename, Type *pt, Type *t case TSequence: { Member *m; struct decoration deco; + ssize_t more_deco = -1; + int decorated = 0; 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\"", @@ -1408,19 +1409,34 @@ 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 (deco.decorated) { + while (decorate_type(newbasename, &deco, &more_deco)) { + decorated++; space(level + 1); fprintf(headerfile, "%s %s%s;\n", deco.field_type, deco.opt ? "*" : "", deco.field_name); - fprintf(jsonfile, ",\"decorate\":{" + if (deco.first) + fprintf(jsonfile, ",\"decorate\":["); + fprintf(jsonfile, "%s{" "\"type\":\"%s\",\"name\":\"%s\",\"optional\":%s," - "\"external\":%s,\"copy_function\":\"%s\"," - "\"free_function\":\"%s\",\"header_name\":\"%s\"}", + "\"external\":%s,\"pointer\":%s,\"void_star\":%s," + "\"struct_star\":%s,\"heim_object\":%s," + "\"copy_function\":\"%s\"," + "\"free_function\":\"%s\",\"header_name\":%s%s%s" + "}", + deco.first ? "" : ",", 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); + deco.ptr ? "true" : "false", deco.void_star ? "true" : "false", + deco.struct_star ? "true" : "false", deco.heim_object ? "true" : "false", + deco.copy_function_name ? deco.copy_function_name : "", + deco.free_function_name ? deco.free_function_name : "", + deco.header_name && deco.header_name[0] == '"' ? "" : "\"", + deco.header_name ? deco.header_name : "", + deco.header_name && deco.header_name[0] == '"' ? "" : "\"" + ); } + if (decorated) + fprintf(jsonfile, "]"); space(level); fprintf (headerfile, "} %s;\n", name); free(deco.field_type); @@ -1637,13 +1653,13 @@ declare_type(const Symbol *s, Type *t, int typedefp) case TSet: case TSequence: { struct decoration deco; + ssize_t more_deco = -1; 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.void_star - && deco.header_name[0]) { - fprintf(headerfile, "#include %s\n", deco.header_name); + while (decorate_type(newbasename, &deco, &more_deco)) { + if (deco.header_name) + fprintf(headerfile, "#include %s\n", deco.header_name); free(deco.field_type); } break; @@ -1782,6 +1798,7 @@ static void generate_type_header (const Symbol *s) { Type *t = s->type; + if (!s->type) return; @@ -1850,6 +1867,7 @@ generate_type_header (const Symbol *s) if (is_export(s->name)) fprintf(symsfile, "ASN1_SYM_TYPE(\"%s\", \"%s\", %s)\n", s->name, s->gen_name, s->gen_name); + fprintf(headerfile, "typedef "); define_type(0, s->gen_name, s->gen_name, NULL, s->type, TRUE, preserve_type(s->name) ? TRUE : FALSE); diff --git a/lib/asn1/gen_copy.c b/lib/asn1/gen_copy.c index e10dc8814..133a4bbae 100644 --- a/lib/asn1/gen_copy.c +++ b/lib/asn1/gen_copy.c @@ -229,6 +229,7 @@ void generate_type_copy (const Symbol *s) { struct decoration deco; + ssize_t more_deco = -1; int preserve = preserve_type(s->name) ? TRUE : FALSE; int save_used_fail = used_fail; @@ -240,19 +241,20 @@ 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, &deco)) { - if (deco.ext && - (deco.copy_function_name == NULL || - deco.copy_function_name[0] == '\0')) { + while (decorate_type(s->gen_name, &deco, &more_deco)) { + if (deco.heim_object) { + fprintf(codefile, "(to)->%s = heim_retain((from)->%s);\n", + deco.field_name, deco.field_name); + } else if (deco.ext && deco.copy_function_name == NULL) { /* Decorated with field of external type but no copy function */ - if (deco.opt || deco.void_star) + if (deco.ptr) 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) { + if (deco.ptr) { fprintf(codefile, "if (from->%s) {\n", deco.field_name); fprintf(codefile, "(to)->%s = malloc(sizeof(*(to)->%s));\n", deco.field_name, deco.field_name); diff --git a/lib/asn1/gen_free.c b/lib/asn1/gen_free.c index 53f558ee3..bf6d397b8 100644 --- a/lib/asn1/gen_free.c +++ b/lib/asn1/gen_free.c @@ -179,6 +179,7 @@ void generate_type_free (const Symbol *s) { struct decoration deco; + ssize_t more_deco = -1; int preserve = preserve_type(s->name) ? TRUE : FALSE; fprintf (codefile, "void ASN1CALL\n" @@ -187,12 +188,13 @@ generate_type_free (const Symbol *s) s->gen_name, s->gen_name); free_type ("data", s->type, preserve); - if (decorate_type(s->gen_name, &deco)) { - if (deco.ext && - (deco.free_function_name == NULL || - deco.free_function_name[0] == '\0')) { + while (decorate_type(s->gen_name, &deco, &more_deco)) { + if (deco.heim_object) { + fprintf(codefile, "heim_retain((data)->%s);\n", deco.field_name); + fprintf(codefile, "(data)->%s = 0;\n", deco.field_name); + } else if (deco.ext && deco.free_function_name == NULL) { /* Decorated with field of external type but no free function */ - if (deco.opt || deco.void_star) + if (deco.ptr) fprintf(codefile, "(data)->%s = 0;\n", deco.field_name); else fprintf(codefile, @@ -200,7 +202,7 @@ generate_type_free (const Symbol *s) deco.field_name, deco.field_name); } else if (deco.ext) { /* Decorated with field of external type w/ free function */ - if (deco.opt) { + if (deco.ptr) { fprintf(codefile, "if ((data)->%s) {\n", deco.field_name); fprintf(codefile, "%s((data)->%s);\n", deco.free_function_name, deco.field_name); diff --git a/lib/asn1/gen_locl.h b/lib/asn1/gen_locl.h index 3ad8dfdc7..b46b9aa48 100644 --- a/lib/asn1/gen_locl.h +++ b/lib/asn1/gen_locl.h @@ -152,11 +152,15 @@ struct decoration { char *free_function_name; /* destructor function name */ char *header_name; /* header name */ unsigned int decorated:1; + unsigned int first:1; /* optional */ unsigned int opt:1; /* optional */ unsigned int ext:1; /* external */ + unsigned int ptr:1; /* external, pointer */ unsigned int void_star:1; /* external, void * */ + unsigned int struct_star:1; /* external, struct foo * */ + unsigned int heim_object:1; /* external, heim_object_t */ }; -int decorate_type(const char *, struct decoration *); +int decorate_type(const char *, struct decoration *, ssize_t *); 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 1a0ce412a..34bd44983 100644 --- a/lib/asn1/gen_template.c +++ b/lib/asn1/gen_template.c @@ -459,7 +459,7 @@ add_line_pointer(struct templatehead *t, } /* - * Add an entry to a template where the pointer firled is a string literal. + * Add an entry to a template where the pointer field is a string literal. */ static void add_line_string(struct templatehead *t, @@ -1065,6 +1065,7 @@ template_members(struct templatehead *temp, Field *typeidfield = NULL; Member *m; struct decoration deco; + ssize_t more_deco = -1; size_t i = 0, typeididx = 0, opentypeidx = 0; int is_array_of_open_type = 0; @@ -1106,21 +1107,24 @@ template_members(struct templatehead *temp, typeidfield, opentypefield, opentypemember, is_array_of_open_type); - if (decorate_type(basetype, &deco)) { + while (decorate_type(basetype, &deco, &more_deco)) { char *poffset2; poffset2 = partial_offset(basetype, deco.field_name, 1, isstruct); - if (deco.ext) { + if (deco.ext && deco.heim_object) { + add_line_string(temp, "0", poffset2, + "A1_OP_TYPE_DECORATE_EXTERN |A1_FLAG_HEIM_OBJ"); + } else 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) + basetype, deco.field_name) == -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" : ""); + deco.opt ? "|A1_FLAG_OPTIONAL" : ""); free(ptr); } else /* Decorated with a templated ASN.1 type */ @@ -1142,6 +1146,7 @@ template_members(struct templatehead *temp, Field *typeidfield = NULL; Member *m; struct decoration deco; + ssize_t more_deco = -1; size_t i = 0, typeididx = 0, opentypeidx = 0; int is_array_of_open_type = 0; @@ -1183,21 +1188,24 @@ template_members(struct templatehead *temp, typeidfield, opentypefield, opentypemember, is_array_of_open_type); - if (decorate_type(basetype, &deco)) { + while (decorate_type(basetype, &deco, &more_deco)) { char *poffset2; poffset2 = partial_offset(basetype, deco.field_name, 1, isstruct); - if (deco.ext) { + if (deco.ext && deco.heim_object) { + add_line_string(temp, "0", poffset2, + "A1_OP_TYPE_DECORATE_EXTERN |A1_FLAG_HEIM_OBJ"); + } else 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) + basetype, deco.field_name) == -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" : ""); + deco.opt ? "|A1_FLAG_OPTIONAL" : ""); free(ptr); } else /* Decorated with a templated ASN.1 type */ @@ -1521,28 +1529,36 @@ generate_template(const Symbol *s) FILE *f = get_code_file(); const char *dupname; struct decoration deco; + ssize_t more_deco = -1; if (use_extern(s)) { gen_extern_stubs(f, s->gen_name); return; } - if (decorate_type(s->gen_name, &deco) && deco.ext) { - if (deco.void_star && deco.header_name[0]) + while (decorate_type(s->gen_name, &deco, &more_deco)) { + if (!deco.ext) + continue; + if (deco.heim_object) + fprintf(f, "#include \n"); + else if (deco.void_star && deco.header_name) fprintf(f, "#include %s\n", deco.header_name); - 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); + if (!deco.heim_object) + 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_name, + 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); } diff --git a/lib/asn1/main.c b/lib/asn1/main.c index d59f07134..a0dd2dc71 100644 --- a/lib/asn1/main.c +++ b/lib/asn1/main.c @@ -42,38 +42,86 @@ static getarg_strings seq; static getarg_strings decorate; static int -strcmp4qsort(const void *ap, const void *bp) +strcmp4mergesort_r(const void *ap, const void *bp, void *d) { - return strcmp(*(const char **)ap, *(const char **)bp); + const char *a = *(const char **)ap; + const char *b = *(const char **)bp; + char sep = *(const char *)d; + int cmp; + + if (sep) { + const char *sepa = strchr(a, sep); + const char *sepb = strchr(b, sep); + size_t alen, blen; + + if (sepa == NULL) sepa = a + strlen(a); + if (sepb == NULL) sepb = b + strlen(b); + alen = sepa - a; + blen = sepb - b; + cmp = strncmp(a, b, alen > blen ? alen : blen); + if (cmp == 0) + cmp = alen - blen; + } else + cmp = strcmp(a, b); + if (cmp == 0) + return (uintptr_t)ap - (uintptr_t)bp; /* stable sort */ + return cmp; +} + +static int +prefix_check(const char *s, const char *p, size_t plen, char sep, int *cmp) +{ + if ((*cmp = strncmp(p, s, plen)) == 0 && s[plen] == sep) + return 1; + if (*cmp == 0) + *cmp = 1; + return 0; } static ssize_t -bsearch_strings(struct getarg_strings *strs, const char *s, - size_t prefix_len, char sep) +bsearch_strings(struct getarg_strings *strs, const char *p, + char sep, ssize_t *more) { ssize_t right = (ssize_t)strs->num_strings - 1; ssize_t left = 0; + ssize_t plen = 0; + int cmp; + + if (sep) + plen = strlen(p); if (strs->num_strings == 0) return -1; + if (sep && more && *more > -1) { + /* If *more > -1 we're continuing an iteration */ + if (*more > right) + return -1; + if (prefix_check(strs->strings[*more], p, plen, sep, &cmp)) + return (*more)++; + (*more)++; + 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); - } + if (sep) { + int cmp2; + + while (prefix_check(strs->strings[mid], p, plen, sep, &cmp) && + mid > 0 && + prefix_check(strs->strings[mid - 1], p, plen, sep, &cmp2)) + mid--; } else - cmp = strcmp(s, strs->strings[mid]); - if (cmp == 0) + cmp = strcmp(p, strs->strings[mid]); + if (cmp == 0) { + if (more) + *more = mid + 1; return mid; + } if (cmp < 0) - right = mid - 1; /* -1 if `s' is smaller than smallest in strs */ + right = mid - 1; /* -1 if `p' is smaller than smallest in strs */ else left = mid + 1; } @@ -83,13 +131,13 @@ bsearch_strings(struct getarg_strings *strs, const char *s, int preserve_type(const char *p) { - return bsearch_strings(&preserve, p, 0, '\0') > -1; + return bsearch_strings(&preserve, p, '\0', 0) > -1; } int seq_type(const char *p) { - return bsearch_strings(&seq, p, 0, '\0') > -1; + return bsearch_strings(&seq, p, '\0', 0) > -1; } /* @@ -126,23 +174,22 @@ split_str(const char *s, char sep, char ***fs) * functions to copy and free values of that type. */ int -decorate_type(const char *p, - struct decoration *deco) +decorate_type(const char *p, struct decoration *deco, ssize_t *more) { ssize_t i; - size_t plen = strlen(p); char **s[7]; char *junk = NULL; char *cp; + deco->first = *more == -1; deco->decorated = 0; deco->field_type = NULL; - if ((i = bsearch_strings(&decorate, p, plen, ':')) == -1) + if ((i = bsearch_strings(&decorate, p, ':', more)) == -1) return 0; deco->decorated = 1; - deco->opt = 0; - deco->ext = 0; + deco->opt = deco->ext = deco->ptr = 0; + deco->void_star = deco->struct_star = deco->heim_object = 0; deco->field_name = deco->copy_function_name = deco->free_function_name = deco->header_name = NULL; @@ -153,7 +200,7 @@ decorate_type(const char *p, s[4] = &deco->header_name; s[5] = &junk; s[6] = NULL; - split_str(decorate.strings[i] + plen + 1, ':', s); + split_str(decorate.strings[i] + strlen(p) + 1, ':', s); if (junk || deco->field_type[0] == '\0' || !deco->field_name || deco->field_name[0] == '\0' || deco->field_name[0] == '?') { @@ -164,10 +211,27 @@ decorate_type(const char *p, deco->opt = 1; *cp = '\0'; } - if (deco->copy_function_name) + if (strcmp(deco->field_type, "void*") == 0 || + strcmp(deco->field_type, "void *") == 0) { + deco->ext = deco->ptr = deco->void_star = 1; + deco->opt = 1; + deco->header_name = NULL; + } else if (strcmp(deco->field_type, "heim_object_t") == 0) { + deco->ptr = deco->heim_object = 1; + deco->header_name = ""; + } else if (strncmp(deco->field_type, "struct ", sizeof("struct ") - 1) == 0 && + deco->field_type[strlen(deco->field_type) - 1] == '*') + deco->ptr = deco->struct_star = 1; + if (deco->ptr || deco->copy_function_name) deco->ext = 1; - if (deco->ext && strcmp(deco->field_type, "void") == 0) - deco->opt = deco->void_star = 1; + if (deco->ext && deco->copy_function_name && !deco->copy_function_name[0]) + deco->copy_function_name = NULL; + if (deco->ext && deco->free_function_name && !deco->free_function_name[0]) + deco->free_function_name = NULL; + if (deco->header_name && !deco->header_name[0]) + deco->header_name = NULL; + if (deco->ptr) + deco->opt = 0; return 1; } @@ -389,14 +453,14 @@ main(int argc, char **argv) } if (preserve.num_strings) - qsort(preserve.strings, preserve.num_strings, sizeof(preserve.strings[0]), - (int (*)(const void *, const void *))strcmp4qsort); + mergesort_r(preserve.strings, preserve.num_strings, + sizeof(preserve.strings[0]), strcmp4mergesort_r, ""); if (seq.num_strings) - qsort(seq.strings, seq.num_strings, sizeof(seq.strings), - (int (*)(const void *, const void *))strcmp4qsort); + mergesort_r(seq.strings, seq.num_strings, sizeof(seq.strings), + strcmp4mergesort_r, ""); if (decorate.num_strings) - qsort(decorate.strings, decorate.num_strings, sizeof(decorate.strings[0]), - (int (*)(const void *, const void *))strcmp4qsort); + mergesort_r(decorate.strings, decorate.num_strings, + sizeof(decorate.strings[0]), strcmp4mergesort_r, ":"); init_generate(file, name); diff --git a/lib/asn1/template.c b/lib/asn1/template.c index d5f1256d5..c53790836 100644 --- a/lib/asn1/template.c +++ b/lib/asn1/template.c @@ -37,6 +37,7 @@ #include #include #include +#include #ifndef ENOTSUP /* Very old MSVC CRTs don't have ENOTSUP */ @@ -2278,8 +2279,13 @@ _asn1_free(const struct asn1_template *t, void *data) const struct asn1_type_func *f = t->ptr; (f->release)(el); } else { + /* A1_OP_TYPE_DECORATE_EXTERN */ const struct asn1_type_func *f = t->ptr; - if (f->release) + + if (t->tt & A1_FLAG_HEIM_OBJ) { + heim_release(*(void **)el); + *(void **)el = 0; + } else if (f && f->release) (f->release)(el); else memset(el, 0, f->size); @@ -2910,7 +2916,11 @@ _asn1_copy(const struct asn1_template *t, const void *from, void *to) ret = (f->copy)(fel, tel); } else { const struct asn1_type_func *f = t->ptr; - if (f->copy) + + /* A1_OP_TYPE_DECORATE_EXTERN */ + if (t->tt & A1_FLAG_HEIM_OBJ) + *(heim_object_t *)tel = heim_retain(*(void **)fel); + else if (f->copy) ret = (f->copy)(fel, tel); else memset(tel, 0, f->size); diff --git a/lib/asn1/test.asn1 b/lib/asn1/test.asn1 index 753b6ff2e..93360fb45 100644 --- a/lib/asn1/test.asn1 +++ b/lib/asn1/test.asn1 @@ -288,23 +288,12 @@ TESTExtensible ::= SEQUENCE { TESTDecorated ::= SEQUENCE { version TESTuint32 - -- 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) + -- gets decorated with varius fields (see test.opt) } TESTNotDecorated ::= SEQUENCE { version TESTuint32 - -- should have the same encoding as TESTDecorated, TESTDecorated2, and - -- TESTDecorated3 + -- should have the same encoding as TESTDecorated } END diff --git a/lib/asn1/test.opt b/lib/asn1/test.opt index c4754f43a..66cd491b0 100644 --- a/lib/asn1/test.opt +++ b/lib/asn1/test.opt @@ -1,4 +1,5 @@ --sequence=TESTSeqOf --decorate=TESTDecorated:TESTuint32:version2? ---decorate=TESTDecorated2:my_vers:version2:my_copy_vers:my_free_vers:"check-gen.h" ---decorate=TESTDecorated3:void:privthing?::: +--decorate=TESTDecorated:my_vers:version3:my_copy_vers:my_free_vers:"check-gen.h" +--decorate=TESTDecorated:void *:privthing +--decorate=TESTDecorated:heim_object_t:privobj