Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:Ledest:erlang:24
erlang
5191-asn1-Improve-partial-decode-support.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File 5191-asn1-Improve-partial-decode-support.patch of Package erlang
From 5fa67cbd7e02b9eb880451578aa6546bf79e7798 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= <bjorn@erlang.org> Date: Tue, 17 Oct 2023 10:20:38 +0200 Subject: [PATCH] asn1: Improve partial decode support This commit fixes multiple bugs in the "exclusive decode" and "selective decode" features for the BER backend of the ASN.1 compiler. Errors in the configuration file now result error messages, instead of crashing the ASN.1 or Erlang compiler. The undocumented option `{asn1config,ModuleName}` has been removed. --- lib/asn1/doc/src/GUI.asn1 | 31 + lib/asn1/doc/src/GUI.asn1config | 6 + lib/asn1/doc/src/Seq.asn | 37 - lib/asn1/doc/src/Seq.asn1config | 3 - lib/asn1/doc/src/asn1_spec.xmlsrc | 718 +++++------------- lib/asn1/src/Makefile | 1 + lib/asn1/src/asn1.app.src | 2 +- lib/asn1/src/asn1_db.erl | 4 +- lib/asn1/src/asn1ct.erl | 558 ++------------ .../src/asn1ct_constructed_ber_bin_v2.erl | 26 +- lib/asn1/src/asn1ct_gen.erl | 119 +-- lib/asn1/src/asn1ct_gen_ber_bin_v2.erl | 66 +- lib/asn1/src/asn1ct_partial_decode.erl | 396 ++++++++++ lib/asn1/src/asn1rtt_ber.erl | 122 +-- lib/asn1/test/asn1_SUITE.erl | 10 +- .../asn1_SUITE_data/BasicOCSPResponse.ber | Bin 0 -> 1577 bytes .../MEDIA-GATEWAY-CONTROL.asn1config | 12 +- .../test/asn1_SUITE_data/OCSP-2013-88.asn1 | 149 ++++ .../asn1_SUITE_data/OCSP-2013-88.asn1config | 23 + .../asn1_SUITE_data/PartialDecSeq.asn1config | 33 +- .../test/asn1_SUITE_data/PartialDecSeq2.asn | 17 + .../asn1_SUITE_data/PartialDecSeq2.asn1config | 19 +- .../asn1_SUITE_data/PartialDecSeq3.asn1config | 9 +- .../asn1_SUITE_data/TCAPPackage.asn1config | 27 +- lib/asn1/test/asn1_app_SUITE.erl | 3 +- lib/asn1/test/error_SUITE.erl | 119 ++- .../test/test_partial_incomplete_decode.erl | 275 +++++-- lib/asn1/test/test_selective_decode.erl | 14 +- .../test/test_special_decode_performance.erl | 2 +- 29 files changed, 1443 insertions(+), 1358 deletions(-) create mode 100644 lib/asn1/doc/src/GUI.asn1 create mode 100644 lib/asn1/doc/src/GUI.asn1config delete mode 100644 lib/asn1/doc/src/Seq.asn delete mode 100644 lib/asn1/doc/src/Seq.asn1config create mode 100644 lib/asn1/src/asn1ct_partial_decode.erl create mode 100644 lib/asn1/test/asn1_SUITE_data/BasicOCSPResponse.ber create mode 100644 lib/asn1/test/asn1_SUITE_data/OCSP-2013-88.asn1 create mode 100644 lib/asn1/test/asn1_SUITE_data/OCSP-2013-88.asn1config diff --git a/lib/asn1/doc/src/GUI.asn1 b/lib/asn1/doc/src/GUI.asn1 new file mode 100644 index 0000000000..b05ecb2170 --- /dev/null +++ b/lib/asn1/doc/src/GUI.asn1 @@ -0,0 +1,31 @@ +GUI DEFINITIONS AUTOMATIC TAGS ::= BEGIN + + Action ::= SEQUENCE { + number INTEGER DEFAULT 15, + handle Handle DEFAULT {number 12, on TRUE} + } + + Key ::= Button + Handle ::= Key + + Button ::= SEQUENCE { + number INTEGER, + on BOOLEAN + } + + Window ::= CHOICE { + vsn INTEGER, + status Status + } + + Status ::= SEQUENCE { + state INTEGER, + buttonList SEQUENCE OF Button, + enabled BOOLEAN OPTIONAL, + actions CHOICE { + possibleActions SEQUENCE OF Action, + noOfActions INTEGER + } + } + +END diff --git a/lib/asn1/doc/src/GUI.asn1config b/lib/asn1/doc/src/GUI.asn1config new file mode 100644 index 0000000000..ebcb629558 --- /dev/null +++ b/lib/asn1/doc/src/GUI.asn1config @@ -0,0 +1,6 @@ +{exclusive_decode, + {'GUI', + [{decode_Window_exclusive, + ['Window',[{status,[{buttonList,parts},{actions,undecoded}]}]]}, + {decode_Button_exclusive, + ['Button',[{number,undecoded}]]}]}}. diff --git a/lib/asn1/doc/src/Seq.asn b/lib/asn1/doc/src/Seq.asn deleted file mode 100644 index 2f2c48cf02..0000000000 --- a/lib/asn1/doc/src/Seq.asn +++ /dev/null @@ -1,37 +0,0 @@ -GUI DEFINITIONS AUTOMATIC TAGS ::= - -BEGIN - -Action ::= SEQUENCE - { - number INTEGER DEFAULT 15, - handle [0] Handle DEFAULT {number 12, on TRUE} - } - -Key ::= [11] EXPLICIT Button -Handle ::= [12] Key -Button ::= SEQUENCE - { - number INTEGER, - on BOOLEAN - } - -Window ::= CHOICE - { - vsn INTEGER, - status E - } - -Status ::= SEQUENCE - { - state INTEGER, - buttonList SEQUENCE OF Button, - enabled BOOLEAN OPTIONAL, - actions CHOICE { - possibleActions SEQUENCE OF Action, - noOfActions INTEGER - } - } - - -END diff --git a/lib/asn1/doc/src/Seq.asn1config b/lib/asn1/doc/src/Seq.asn1config deleted file mode 100644 index 571cf4cd32..0000000000 --- a/lib/asn1/doc/src/Seq.asn1config +++ /dev/null @@ -1,3 +0,0 @@ -{exclusive_decode,{'GUI', - [{decode_Window_exclusive,['Window',[{status,[{buttonList,parts},{actions,undecoded}]}]]}, - {decode_Button_exclusive,['Button',[{number,undecoded}]]}]}}. diff --git a/lib/asn1/doc/src/asn1_spec.xmlsrc b/lib/asn1/doc/src/asn1_spec.xmlsrc index fd30d34a34..a0986ed087 100644 --- a/lib/asn1/doc/src/asn1_spec.xmlsrc +++ b/lib/asn1/doc/src/asn1_spec.xmlsrc @@ -30,19 +30,18 @@ <file>asn1_spec.xml</file> </header> <marker id="SpecializedDecodes"></marker> - <p>When performance is of highest priority and you are interested in - a limited part of the ASN.1 encoded message before deciding what - to do with the rest of it, an option is to decode only this small - part. The situation can be a server that has to decide the - addressee of a message. The addressee can be interested in - the entire message, but the server can be a bottleneck that you want - to spare any unnecessary load.</p> - <p> Instead of making two <em>complete decodes</em> (the normal case of + <p>When performance is of highest priority and one is interested in + a limited part of the ASN.1 encoded message before deciding what to + do with the rest of it, an option is to decode only a part of the + message. This situation can be a server that has to decide the + addressee of a message. The addressee can be interested in the + entire message, but the server can be a bottleneck that you want to + spare any unnecessary load.</p> + <p>Instead of making two <em>complete decodes</em> (the normal case of decode), one in the server and one in the addressee, it is only - necessary to make one <em>specialized decode</em>(in the server) - and another complete decode(in the addressee). This section - describes the following two specialized decodes, which support - to solve this and similar problems:</p> + necessary to make one <em>specialized decode</em> (in the server) + and another complete decode (in the addressee). This section + describes the following specialized decode functionality:</p> <list type="bulleted"> <item><em>Exclusive decode</em></item> <item><em>Selected decode</em></item> @@ -52,16 +51,11 @@ <section> <title>Exclusive Decode</title> - <p>The basic idea with exclusive - decode is to specify which parts of the message you want to - exclude from being decoded. These parts remain encoded and are - returned in the value structure as binaries. They can be decoded - in turn by passing them to a certain <c>decode_part/2</c> - function. The performance gain is high for large messages. - You can do an exclusive decode and later one or more - decodes of the parts, or a second complete decode instead of two or - more complete decodes. - </p> + <p>The basic idea with exclusive decode is to specify which parts + of the message you want to exclude from being decoded. These parts + remain encoded and are returned in the value structure as + binaries. The undecoded parts can be decoded later by calling the + <c>decode_part/2</c> function.</p> <section> <title>Procedure</title> @@ -105,17 +99,17 @@ <c>decode_exclusive</c> and an ASN.1 encoded message <c>Bin</c> is to be exclusive decoded, the call is as follows:</p> <pre> -{ok,Excl_Message} = 'MyModule':decode_exclusive(Bin) </pre> +{ok,ExclMessage} = 'MyModule':decode_exclusive(Bin)</pre> <marker id="UndecodedPart"></marker> - <p>The result <c>Excl_Message</c> has the same structure as a + <p>The result <c>ExclMessage</c> has the same structure as a complete decode would have, except for the parts of the top type that were not decoded. The undecoded parts are on their places - in the structure on format <c>{Type_Key,Undecoded_Value}</c>. + in the structure on format <c>{TypeKey,UndecodedValue}</c>. </p> <p>Each undecoded part that is to be decoded must be fed into function <c>decode_part/2</c> as follows:</p> <pre> -{ok,Part_Message} = 'MyModule':decode_part(Type_Key,Undecoded_Value)</pre> +{ok,PartMessage} = 'MyModule':decode_part(TypeKey, UndecodedValue)</pre> </section> <section> @@ -124,54 +118,62 @@ <p>This instruction is written in the configuration file in the following format:</p> <pre> -Exclusive_Decode_Instruction = {exclusive_decode,{Module_Name,Decode_Instructions}}. +ExclusiveDecodeInstruction = {exclusive_decode,{ModuleName,DecodeInstructions}}. -Module_Name = atom() +ModuleName = atom() -Decode_Instructions = [Decode_Instruction]+ +DecodeInstructions = [DecodeInstruction]+ -Decode_Instruction = {Exclusive_Decode_Function_Name,Type_List} +DecodeInstruction = {ExclusiveDecodeFunctionName,TypeList} -Exclusive_Decode_Function_Name = atom() +ExclusiveDecodeFunctionName = atom() -Type_List = [Top_Type,Element_List] +TypeList = [TopType,ElementList] -Element_List = [Element]+ +ElementList = [Element]+ Element = {Name,parts} | {Name,undecoded} | - {Name,Element_List} + {Name,ElementList} -Top_Type = atom() +TopType = atom() Name = atom()</pre> <p>The instruction must be a valid Erlang term ended by a dot. </p> - <p>In <c>Type_List</c> the "path" from the top type to each - undecoded subcomponents is described. The top type of the path is - an atom, the name of it. The action on each component/type that - follows is described by one of - <c>{Name,parts}, {Name,undecoded}, {Name,Element_List}</c>.</p> - <p>The use and effect of the actions are as follows: - </p> + <p>In <c>TypeList</c> the path from the top type to each + undecoded subcomponent is described. <c>TopType</c> is the name + of a top-level type in the ASN.1 specification. The action for + each component in <c>ElementList</c> is described by one of:</p> + <list type="bulleted"> - <item><c>{Name,undecoded}</c> - Tells that the element is left - undecoded during the exclusive decode. The type of <c>Name</c> - can be any ASN.1 type. The value of element <c>Name</c> is - returned as a tuple (as mentioned in the previous section) in - the value structure of the top type.</item> - <item><c>{Name,parts}</c> - The type of <c>Name</c> can be one of - <c>SEQUENCE OF</c> or <c>SET OF</c>. The action implies that - the different components of <c>Name</c> are left undecoded. The - value of <c>Name</c> is returned as a tuple (as mentioned in - the previous section) where the second element is a list of - binaries. This is because the representation of a <c>SEQUENCE OF</c> - or a <c>SET OF</c> in Erlang is a list of its internal type. Any - of the elements in this list or the entire list can be decoded by - function <c>decode_part</c>.</item> - <item><c>{Name,Element_List}</c> - This action is used when one or - more of the subtypes of <c>Name</c> is exclusive decoded.</item> + <item><c>{Name,parts}</c></item> + <item><c>{Name,undecoded}</c></item> + <item><c>{Name,ElementList}</c></item> </list> + <p>The use and effect of the actions are as follows:</p> + <taglist> + <tag><c>{Name,undecoded}</c></tag> + <item>Leaves the element undecoded. The type of <c>Name</c> can + be any ASN.1 type. The value of element <c>Name</c> is + returned as a tuple (as mentioned in the previous section) in + the value structure of the top type.</item> + + <tag><c>{Name,parts}</c></tag> + <item>The type of <c>Name</c> must be either <c>SEQUENCE OF</c> + or <c>SET OF</c>. The action implies that the different + components of <c>Name</c> are left undecoded. The value of + <c>Name</c> is returned as a tuple (as mentioned in the + previous section) where the second element is a list of + binaries. This is because the representation of a <c>SEQUENCE + OF</c> or a <c>SET OF</c> in Erlang is a list of its internal + type. Any of the elements in this list or the entire list can + be decoded by function <c>decodepart</c>.</item> + + <tag><c>{Name,ElementList}</c></tag> + <item>This action is used when one or more of the subtypes of + <c>Name</c> is exclusively decoded.</item> + </taglist> <p><c>Name</c> in these actions can be a component name of a <c>SEQUENCE OF</c> or a <c>SET OF</c>, or a name of an alternative in a <c>CHOICE</c>. @@ -183,12 +185,12 @@ Name = atom()</pre> <p>In this examples, the definitions from the following ASN.1 specification are used:</p> <marker id="Asn1spec"></marker> - <codeinclude file="Seq.asn" tag="" type="none"></codeinclude> + <codeinclude file="GUI.asn1" tag="" type="none"></codeinclude> <p>If <c>Button</c> is a top type and it is needed to exclude - component <c>number</c> from decode, <c>Type_List</c> in the + component <c>number</c> from decode, <c>TypeList</c> in the instruction in the configuration file is <c>['Button',[{number,undecoded}]]</c>. If you call the decode - function <c>decode_Button_exclusive</c>, <c>Decode_Instruction</c> is + function <c>decode_Button_exclusive</c>, <c>DecodeInstruction</c> is <c>{decode_Button_exclusive,['Button',[{number,undecoded}]]}</c>. </p> <p>Another top type is <c>Window</c> whose subcomponent @@ -196,7 +198,7 @@ Name = atom()</pre> <c>buttonList</c> are to be left undecoded. For this type, the function is named <c>decode__Window_exclusive</c>. The complete <c>Exclusive_Decode_Instruction</c> configuration is as follows:</p> - <codeinclude file="Seq.asn1config" tag="" type="none"></codeinclude> + <codeinclude file="GUI.asn1config" tag="" type="none"></codeinclude> <p>The following figure shows the bytes of a <c>Window:status</c> message. The components <c>buttonList</c> and <c>actions</c> are excluded from decode. Only <c>state</c> and <c>enabled</c> are decoded @@ -206,115 +208,91 @@ Name = atom()</pre> <icaption>Bytes of a Window:status Message</icaption> </image> <p></p> - <p>Compiling <c>GUI.asn</c> including the configuration file is done - as follows:</p> + <p>Here follows an example of how the module. Note that option <c>no_ok_wrapper</c> + is used to make the make example more concise.</p> <pre> -unix> erlc -bber +asn1config GUI.asn - -erlang> asn1ct:compile('GUI', [ber,asn1config]).</pre> - <p>The module can be used as follows:</p> - <pre> -1> Button_Msg = {'Button',123,true}. -{'Button',123,true} -2> {ok,Button_Bytes} = 'GUI':encode('Button',Button_Msg). -{ok,[<<48>>, - [6], - [<<128>>, - [1], - 123], - [<<129>>, - [1], - 255]]} -3> {ok,Exclusive_Msg_Button} = 'GUI':decode_Button_exclusive(list_to_binary(Button_Bytes)). -{ok,{'Button',{'Button_number',<<28,1,123>>}, - true}} -4> 'GUI':decode_part('Button_number',<<128,1,123>>). -{ok,123} -5> Window_Msg = -{'Window',{status,{'Status',35, - [{'Button',3,true}, - {'Button',4,false}, - {'Button',5,true}, - {'Button',6,true}, - {'Button',7,false}, - {'Button',8,true}, - {'Button',9,true}, - {'Button',10,false}, - {'Button',11,true}, - {'Button',12,true}, - {'Button',13,false}, - {'Button',14,true}], - false, - {possibleActions,[{'Action',16,{'Button',17,true}}]}}}}. -{'Window',{status,{'Status',35, - [{'Button',3,true}, - {'Button',4,false}, - {'Button',5,true}, - {'Button',6,true}, - {'Button',7,false}, - {'Button',8,true}, - {'Button',9,true}, - {'Button',10,false}, - {'Button',11,true}, - {'Button',12,true}, - {'Button',13,false}, - {'Button',14,true}], - false, - {possibleActions,[{'Action',16,{'Button',17,true}}]}}}} -6> {ok,Window_Bytes}='GUI':encode('Window',Window_Msg). -{ok,[<<161>>, - [127], - [<<128>>, ... - - -8> {ok,{status,{'Status',Int,{Type_Key_SeqOf,Val_SEQOF}, -BoolOpt,{Type_Key_Choice,Val_Choice}}}}= -'GUI':decode_Window_status_exclusive(list_to_binary(Window_Bytes)). -{ok,{status,{'Status',35, - {'Status_buttonList',[<<48,6,128,1,3,129,1,255>>, - <<48,6,128,1,4,129,1,0>>, - <<48,6,128,1,5,129,1,255>>, - <<48,6,128,1,6,129,1,255>>, - <<48,6,128,1,7,129,1,0>>, - <<48,6,128,1,8,129,1,255>>, - <<48,6,128,1,9,129,1,255>>, - <<48,6,128,1,10,129,1,0>>, - <<48,6,128,1,11,129,1,255>>, - <<48,6,128,1,12,129,1,255>>, - <<48,6,128,1,13,129,1,0>>, - <<48,6,128,1,14,129,1,255>>]}, - false, - {'Status_actions', -<<163,21,160,19,48,17,2,1,16,160,12,172,10,171,8,48,6,128,1,...>>}}}} -10> 'GUI':decode_part(Type_Key_SeqOf,Val_SEQOF). -{ok,[{'Button',3,true}, - {'Button',4,false}, - {'Button',5,true}, - {'Button',6,true}, - {'Button',7,false}, - {'Button',8,true}, - {'Button',9,true}, - {'Button',10,false}, - {'Button',11,true}, - {'Button',12,true}, - {'Button',13,false}, - {'Button',14,true}]} -11> 'GUI':decode_part(Type_Key_SeqOf,hd(Val_SEQOF)). -{ok,{'Button',3,true}} -12> 'GUI':decode_part(Type_Key_Choice,Val_Choice). -{ok,{possibleActions,[{'Action',16,{'Button',17,true}}]}}</pre> +1> <input>asn1ct:compile('GUI', [ber,asn1config,no_ok_wrapper]).</input> +ok +2> <input>rr('GUI').</input> +['Action','Button','Status'] +3> <input>ButtonMsg = #'Button'{number=123,on=true}.</input> +#'Button'{number = 123,on = true} +4> <input>ButtonBytes = 'GUI':encode('Button', ButtonMsg).</input> +<<48,6,128,1,123,129,1,255>> +5> <input>ExclusiveMsgButton = 'GUI':decode_Button_exclusive(ButtonBytes).</input> +#'Button'{number = {'Button_number',<<128,1,123>>}, + on = true} +6> <input>{UndecKey,UndecBytes} = ExclusiveMsgButton#'Button'.number.</input> +{'Button_number',<<128,1,123>>} +7> <input>'GUI':decode_part(UndecKey, UndecBytes).</input> +123 +8> <input>WindowMsg = +{status,{'Status',35, + [{'Button',3,true}, + {'Button',4,false}, + {'Button',5,true}, + {'Button',6,true}, + {'Button',7,false}], + false, + {possibleActions,[{'Action',16,{'Button',17,true}}]}}}.</input> +{status,#'Status'{state = 35, + buttonList = [#'Button'{number = 3,on = true}, + #'Button'{number = 4,on = false}, + #'Button'{number = 5,on = true}, + #'Button'{number = 6,on = true}, + #'Button'{number = 7,on = false}], + enabled = false, + actions = {possibleActions,[#'Action'{number = 16, + handle = #'Button'{number = 17,on = true}}]}}} +9> <input>WindowBytes = 'GUI':encode('Window', WindowMsg).</input> +<<161,65,128,1,35,161,40,48,6,128,1,3,129,1,255,48,6,128, + 1,4,129,1,0,48,6,128,1,5,129,...>> +10> <input>{status,#'Status'{buttonList={UndecWindowKey,UndecWindowParts}}} = +'GUI':decode_Window_exclusive(WindowBytes).</input> +{status,#'Status'{state = 35, + buttonList = {'Status_buttonList',[<<48,6,128,1,3,129,1, + 255>>, + <<48,6,128,1,4,129,1,0>>, + <<48,6,128,1,5,129,1,255>>, + <<48,6,128,1,6,129,1,255>>, + <<48,6,128,1,7,129,1,0>>]}, + enabled = false, + actions = {'Status_actions',<<163,15,160,13,48,11,128, + 1,16,161,6,128,1,17,129, + 1,255>>}}} +11> <input>'GUI':decode_part(UndecWindowKey, UndecWindowParts).</input> +[#'Button'{number = 3,on = true}, + #'Button'{number = 4,on = false}, + #'Button'{number = 5,on = true}, + #'Button'{number = 6,on = true}, + #'Button'{number = 7,on = false}] +12> <input>'GUI':decode_part(UndecWindowKey, hd(UndecWindowParts)).</input> +#'Button'{number = 3,on = true} +13> <input>{status,#'Status'{actions={ChoiceKey,ChoiceUndec}}} = v(10).</input> +{status,#'Status'{state = 35, + buttonList = {'Status_buttonList',[<<48,6,128,1,3,129,1, + 255>>, + <<48,6,128,1,4,129,1,0>>, + <<48,6,128,1,5,129,1,255>>, + <<48,6,128,1,6,129,1,255>>, + <<48,6,128,1,7,129,1,0>>]}, + enabled = false, + actions = {'Status_actions',<<163,15,160,13,48,11,128, + 1,16,161,6,128,1,17,129, + 1,255>>}}} +14> <input>'GUI':decode_part(ChoiceKey, ChoiceUndec).</input> +{possibleActions,[#'Action'{number = 16, + handle = #'Button'{number = 17,on = true}}]}</pre> </section> </section> <section> <title>Selective Decode</title> - <p>This specialized decode decodes a subtype of a - constructed value and is the fastest method to extract a - subvalue. This decode is typically used when you want to - inspect, for example, a version number, to be able to decide what - to do with the entire value. The result is returned as - <c>{ok,Value}</c> or <c>{error,Reason}</c>. - </p> + <p>Selective decode decodes a single subtype of a constructed + value. This is the fastest method to extract a subvalue. Selective + decode is typically used when one want to inspect, for example, a + version number to be able to decide what to do with the entire + value.</p> <section> <title>Procedure</title> @@ -343,13 +321,11 @@ BoolOpt,{Type_Key_Choice,Val_Choice}}}}= <section> <title>User Interface</title> <p>The only new user interface function is the one provided by the - user in the configuration file. The function is started by - the <c>ModuleName:FunctionName</c> notation. - </p> + user in the configuration file.</p> <p>For example, if the configuration file includes the specification <c>{selective_decode,{'ModuleName',[{selected_decode_Window,TypeList}]}}</c> do the selective decode by - <c>{ok,Result}='ModuleName':selected_decode_Window(EncodedBinary).</c></p> + <c>{ok,Result} = 'ModuleName':selected_decode_Window(EncodedBinary).</c></p> </section> <section> @@ -358,42 +334,42 @@ BoolOpt,{Type_Key_Choice,Val_Choice}}}}= <p>One or more selective decode functions can be described in a configuration file. Use the following notation:</p> <pre> -Selective_Decode_Instruction = {selective_decode,{Module_Name,Decode_Instructions}}. +SelectiveDecodeInstruction = {selective_decode,{ModuleName,DecodeInstructions}}. -Module_Name = atom() +ModuleName = atom() -Decode_Instructions = [Decode_Instruction]+ +DecodeInstructions = [DecodeInstruction]+ -Decode_Instruction = {Selective_Decode_Function_Name,Type_List} +DecodeInstruction = {SelectiveDecodeFunctionName,TypeList} -Selective_Decode_Function_Name = atom() +SelectiveDecodeFunctionName = atom() -Type_List = [Top_Type|Element_List] +TypeList = [TopType|ElementList] -Element_List = Name|List_Selector +ElementList = Name|ListSelector Name = atom() -List_Selector = [integer()]</pre> +ListSelector = [integer()]</pre> <p>The instruction must be a valid Erlang term ended by a dot. </p> <list type="bulleted"> - <item><c>Module_Name</c> is the same as the name of the ASN.1 + <item><c>ModuleName</c> is the same as the name of the ASN.1 specification, but without the extension.</item> - <item><c>Decode_Instruction</c> is a tuple with your chosen + <item><c>DecodeInstruction</c> is a tuple with your chosen function name and the components from the top type that leads to the single type you want to decode. Ensure to choose a name of your function that is not the same as any of the generated functions.</item> - <item> The first element of <c>Type_List</c> is the top type of the - encoded message. In <c>Element_List</c>, it is followed by + <item> The first element of <c>TypeList</c> is the top type of the + encoded message. In <c>ElementList</c>, it is followed by each of the component names that leads to selected type.</item> - <item>Each name in <c>Element_List</c> must be a constructed type + <item>Each name in <c>ElementList</c> must be a constructed type except the last name, which can be any type.</item> - <item><c>List_Selector</c> makes it possible to choose one of the + <item><c>ListSelector</c> makes it possible to choose one of the encoded components in a <c>SEQUENCE OF</c> or a <c>SET OF</c>. It is also possible to go further in that component and pick a - subtype of that to decode. So, in the <c>Type_List</c>: + subtype of that to decode. So, in the <c>TypeList</c>: <c>['Window',status,buttonList,[1],number]</c>, component <c>buttonList</c> must be of type <c>SEQUENCE OF</c> or <c>SET OF</c>.</item> @@ -407,7 +383,7 @@ List_Selector = [integer()]</pre> </section> <section> - <title>Another Example</title> + <title>Example</title> <p>In this example, the same ASN.1 specification as in Section <seeguide marker="#Asn1spec">Writing an Exclusive Decode Instruction</seeguide> is used. The following is a valid selective decode instruction:</p> @@ -438,15 +414,14 @@ List_Selector = [integer()]</pre> value 4711 is to be picked by <c>selected_decode_Action</c>. In an Erlang terminal it looks as follows:</p> <pre> -ValAction = {'Action',17,{'Button',4711,false}}. +1> <input>asn1ct:compile('GUI', [ber,asn1config,no_ok_wrapper]).</input> +ok +2> <input>ValAction = {'Action',17,{'Button',4711,false}}.</input> {'Action',17,{'Button',4711,false}} -7> {ok,Bytes}='GUI':encode('Action',ValAction). -... -8> BinBytes = list_to_binary(Bytes). +3> <input>Bytes = 'GUI':encode('Action',ValAction).</input> <<48,18,2,1,17,160,13,172,11,171,9,48,7,128,2,18,103,129,1,0>> -9> 'GUI':selected_decode_Action(BinBytes). -{ok,4711} -10></pre> +4> <input>'GUI':selected_decode_Action(Bytes).</input> +4711</pre> <p>The third instruction, <c>['Window',status,actions,possibleActions,[1],handle,number]</c>, works as follows:</p> @@ -482,7 +457,7 @@ ValAction = {'Action',17,{'Button',4711,false}}. <c>selected_decode_Window1</c> decodes the intended subvalue of value <c>Val</c>:</p> <pre> -1> Val = {'Window',{status,{'Status',12, +1> <input>Val = {status,{'Status',12, [{'Button',13,true}, {'Button',14,false}, {'Button',15,true}, @@ -490,355 +465,14 @@ ValAction = {'Action',17,{'Button',4711,false}}. true, {possibleActions,[{'Action',17,{'Button',18,false}}, {'Action',19,{'Button',20,true}}, - {'Action',21,{'Button',22,false}}]}}}} -2> {ok,Bytes}='GUI':encode('Window',Val). -... -3> Bin = list_to_binary(Bytes). -<<161,101,128,1,12,161,32,48,6,128,1,13,129,1,255,48,6,128,1,14,129,1,0,48,6,128,1,15,129,...>> -4> 'GUI':selected_decode_Window1(Bin). -{ok,13} -5> 'GUI':selected_decode_Window2(Bin). -{ok,18}</pre> - <p>Notice that the value fed into the selective decode - functions must be a binary. - </p> - </section> - </section> - - <section> - <title>Performance</title> - <p>To give an indication on the possible performance gain using - the specialized decodes, some measures have been performed. The - relative figures in the outcome between selective, exclusive, and - complete decode (the normal case) depend on the structure of - the type, the size of the message, and on what level the - selective and exclusive decodes are specified. - </p> - - <section> - <title>ASN.1 Specifications, Messages, and Configuration</title> - <p>The specifications <seeguide marker="#Asn1spec">GUI</seeguide> and - <url href="http://www.itu.int/ITU-T/asn1/database/itu-t/h/h248/2002/MEDIA-GATEWAY-CONTROL.html">MEDIA-GATEWAY-CONTROL</url> - were used in the test. - </p> - <p>For the <c>GUI</c> specification the configuration was as follows:</p> - <pre> -{selective_decode, - {'GUI', - [{selected_decode_Window1, - ['Window', - status,buttonList, - [1], - number]}, - {selected_decode_Window2, - ['Window', - status, - actions, - possibleActions, - [1], - handle,number]}]}}. - {exclusive_decode, - {'GUI', - [{decode_Window_status_exclusive, - ['Window', - [{status, - [{buttonList,parts}, - {actions,undecoded}]}]]}]}}.</pre> - <p>The <c>MEDIA-GATEWAY-CONTROL</c> configuration was as follows:</p> - <pre> -{exclusive_decode, - {'MEDIA-GATEWAY-CONTROL', - [{decode_MegacoMessage_exclusive, - ['MegacoMessage', - [{authHeader,undecoded}, - {mess, - [{mId,undecoded}, - {messageBody,undecoded}]}]]}]}}. -{selective_decode, - {'MEDIA-GATEWAY-CONTROL', - [{decode_MegacoMessage_selective, - ['MegacoMessage',mess,version]}]}}.</pre> - <p>The corresponding values were as follows:</p> - <pre> -{'Window',{status,{'Status',12, - [{'Button',13,true}, - {'Button',14,false}, - {'Button',15,true}, - {'Button',16,false}, - {'Button',13,true}, - {'Button',14,false}, - {'Button',15,true}, - {'Button',16,false}, - {'Button',13,true}, - {'Button',14,false}, - {'Button',15,true}, - {'Button',16,false}], - true, - {possibleActions, - [{'Action',17,{'Button',18,false}}, - {'Action',19,{'Button',20,true}}, - {'Action',21,{'Button',22,false}}, - {'Action',17,{'Button',18,false}}, - {'Action',19,{'Button',20,true}}, - {'Action',21,{'Button',22,false}}, - {'Action',17,{'Button',18,false}}, - {'Action',19,{'Button',20,true}}, - {'Action',21,{'Button',22,false}}, - {'Action',17,{'Button',18,false}}, - {'Action',19,{'Button',20,true}}, - {'Action',21,{'Button',22,false}}, - {'Action',17,{'Button',18,false}}, - {'Action',19,{'Button',20,true}}, - {'Action',21,{'Button',22,false}}, - {'Action',17,{'Button',18,false}}, - {'Action',19,{'Button',20,true}}, - {'Action',21,{'Button',22,false}}]}}}} - - -{'MegacoMessage',asn1_NOVALUE, - {'Message',1, - {ip4Address, - {'IP4Address',[125,125,125,111],55555}}, - {transactions, - [{transactionReply, - {'TransactionReply',50007,asn1_NOVALUE, - {actionReplies, - [{'ActionReply',0,asn1_NOVALUE,asn1_NOVALUE, - [{auditValueReply,{auditResult,{'AuditResult', - {'TerminationID',[],[255,255,255]}, - [{mediaDescriptor, - {'MediaDescriptor',asn1_NOVALUE, - {multiStream, - [{'StreamDescriptor',1, - {'StreamParms', - {'LocalControlDescriptor', - sendRecv, - asn1_NOVALUE, - asn1_NOVALUE, - [{'PropertyParm', - [0,11,0,7], - [[52,48]], - asn1_NOVALUE}]}, - {'LocalRemoteDescriptor', - [[{'PropertyParm', - [0,0,176,1], - [[48]], - asn1_NOVALUE}, - {'PropertyParm', - [0,0,176,8], - [[73,78,32,73,80,52,32,49,50,53,46,49, - 50,53,46,49,50,53,46,49,49,49]], - asn1_NOVALUE}, - {'PropertyParm', - [0,0,176,15], - [[97,117,100,105,111,32,49,49,49,49,32, - 82,84,80,47,65,86,80,32,32,52]], - asn1_NOVALUE}, - {'PropertyParm', - [0,0,176,12], - [[112,116,105,109,101,58,51,48]], - asn1_NOVALUE}]]}, - {'LocalRemoteDescriptor', - [[{'PropertyParm', - [0,0,176,1], - [[48]], - asn1_NOVALUE}, - {'PropertyParm', - [0,0,176,8], - [[73,78,32,73,80,52,32,49,50,52,46,49,50, - 52,46,49,50,52,46,50,50,50]], - asn1_NOVALUE}, - {'PropertyParm', - [0,0,176,15], - [[97,117,100,105,111,32,50,50,50,50,32,82, - 84,80,47,65,86,80,32,32,52]], - asn1_NOVALUE}, - {'PropertyParm', - [0,0,176,12], - [[112,116,105,109,101,58,51,48]], - asn1_NOVALUE}]]}}}]}}}, - {packagesDescriptor, - [{'PackagesItem',[0,11],1}, - {'PackagesItem',[0,11],1}]}, - {statisticsDescriptor, - [{'StatisticsParameter',[0,12,0,4],[[49,50,48,48]]}, - {'StatisticsParameter',[0,11,0,2],[[54,50,51,48,48]]}, - {'StatisticsParameter',[0,12,0,5],[[55,48,48]]}, - {'StatisticsParameter',[0,11,0,3],[[52,53,49,48,48]]}, - {'StatisticsParameter',[0,12,0,6],[[48,46,50]]}, - {'StatisticsParameter',[0,12,0,7],[[50,48]]}, - {'StatisticsParameter',[0,12,0,8],[[52,48]]}]}]}}}]}]}}}]}}}</pre> - <p>The size of the encoded values was 458 bytes for <c>GUI</c> and 464 - bytes for <c>MEDIA-GATEWAY-CONTROL</c>. - </p> - </section> - - <section> - <title>Results</title> - <p>The ASN.1 specifications in the test were compiled with options - <c>ber_bin, optimize, driver</c> and <c>asn1config</c>. Omitting - option <c>driver</c> gives - higher values for <c>decode</c> and <c>decode_part</c>. These tests have - not been rerun using NIFs, but are expected to perform about 5% better - than the linked-in driver. - </p> - <p>The test program runs 10000 decodes on the value, resulting - in an output with the elapsed time in microseconds for the - total number of decodes. - </p> - <table> - <row> - <cell align="left" valign="top"><em>Function</em></cell> - <cell align="left" valign="top"><em>Time</em> (microseconds)</cell> - <cell align="left" valign="top"><em>Decode Type</em></cell> - <cell align="left" valign="top"><em>ASN.1 Specification</em></cell> - <cell align="left" valign="top"><em>% of Time versus Complete Decode</em></cell> - </row> - <row> - <cell align="left" valign="middle"><c>decode_MegacoMessage_selective/1</c></cell> - <cell align="left" valign="middle"><c>374045</c></cell> - <cell align="left" valign="middle"><c>Selective</c></cell> - <cell align="left" valign="middle"><c>MEDIA-GATEWAY-CONTROL</c></cell> - <cell align="left" valign="middle"><em>8.3</em></cell> - </row> - <row> - <cell align="left" valign="middle"><c>decode_MegacoMessage_exclusive/1</c></cell> - <cell align="left" valign="middle"><c>621107</c></cell> - <cell align="left" valign="middle"><c>Exclusive</c></cell> - <cell align="left" valign="middle"><c>MEDIA-GATEWAY-CONTROL</c></cell> - <cell align="left" valign="middle"><em>13.8</em></cell> - </row> - <row> - <cell align="left" valign="middle"><c>decode/2</c></cell> - <cell align="left" valign="middle"><c>4507457</c></cell> - <cell align="left" valign="middle"><c>Complete</c></cell> - <cell align="left" valign="middle"><c>MEDIA-GATEWAY-CONTROL</c></cell> - <cell align="left" valign="middle"><em>100</em></cell> - </row> - <row> - <cell align="left" valign="middle"><c>selected_decode_Window1/1</c></cell> - <cell align="left" valign="middle"><c>449585</c></cell> - <cell align="left" valign="middle"><c>Selective</c></cell> - <cell align="left" valign="middle"><c>GUI</c></cell> - <cell align="left" valign="middle"><em>7.6</em></cell> - </row> - <row> - <cell align="left" valign="middle"><c>selected_decode_Window2/1</c></cell> - <cell align="left" valign="middle"><c>890666</c></cell> - <cell align="left" valign="middle"><c>Selective</c></cell> - <cell align="left" valign="middle"><c>GUI</c></cell> - <cell align="left" valign="middle"><em>15.1</em></cell> - </row> - <row> - <cell align="left" valign="middle"><c>decode_Window_status_exclusive/1</c></cell> - <cell align="left" valign="middle"><c>1251878</c></cell> - <cell align="left" valign="middle"><c>Exclusive</c></cell> - <cell align="left" valign="middle"><c>GUI</c></cell> - <cell align="left" valign="middle"><em>21.3</em></cell> - </row> - <row> - <cell align="left" valign="middle"><c>decode/2</c></cell> - <cell align="left" valign="middle"><c>5889197</c></cell> - <cell align="left" valign="middle"><c>Complete</c></cell> - <cell align="left" valign="middle"><c>GUI</c></cell> - <cell align="left" valign="middle"><em>100</em></cell> - </row> - <tcaption>Results of Complete, Exclusive, and Selective Decode</tcaption> - </table> - <p>It is also of interest to know the relation is between - a complete decode, an exclusive decode followed by - <c>decode_part</c> of the excluded parts, and a selective decode - followed by a complete decode. Some situations can be compared to - this simulation, for example, inspect a subvalue and later inspect - the entire value. The following table shows figures from this - test. The number of loops and the time unit are the same as in the - previous test. - </p> - <table> - <row> - <cell align="left" valign="top"><em>Actions</em></cell> - <cell align="left" valign="top"><em>Function</em> </cell> - <cell align="left" valign="top"><em>Time</em> (microseconds)</cell> - <cell align="left" valign="top"><em>ASN.1 Specification</em></cell> - <cell align="left" valign="top"><em>% of Time vs. Complete Decode</em></cell> - </row> - <row> - <cell align="left" valign="middle"><c>Complete</c></cell> - <cell align="left" valign="middle"><c>decode/2</c></cell> - <cell align="left" valign="middle"><c>4507457</c></cell> - <cell align="left" valign="middle"><c>MEDIA-GATEWAY-CONTROL</c></cell> - <cell align="left" valign="middle"><em>100</em></cell> - </row> - <row> - <cell align="left" valign="middle"><c>Selective and Complete</c></cell> - <cell align="left" valign="middle"><c>decode_­MegacoMessage_­selective/1</c></cell> - <cell align="left" valign="middle"><c>4881502</c></cell> - <cell align="left" valign="middle"><c>MEDIA-GATEWAY-CONTROL</c></cell> - <cell align="left" valign="middle"><em>108.3</em></cell> - </row> - <row> - <cell align="left" valign="middle"><c>Exclusive and decode_part</c></cell> - <cell align="left" valign="middle"><c>decode_­MegacoMessage_­exclusive/1</c></cell> - <cell align="left" valign="middle"><c>5481034</c></cell> - <cell align="left" valign="middle"><c>MEDIA-GATEWAY-CONTROL</c></cell> - <cell align="left" valign="middle"><em>112.3</em></cell> - </row> - <row> - <cell align="left" valign="middle"><c>Complete</c></cell> - <cell align="left" valign="middle"><c>decode/2</c></cell> - <cell align="left" valign="middle"><c>5889197</c></cell> - <cell align="left" valign="middle"><c>GUI</c></cell> - <cell align="left" valign="middle"><em>100</em></cell> - </row> - <row> - <cell align="left" valign="middle"><c>Selective and Complete</c></cell> - <cell align="left" valign="middle"><c>selected_­decode_­Window1/1</c></cell> - <cell align="left" valign="middle"><c>6337636</c></cell> - <cell align="left" valign="middle"><c>GUI</c></cell> - <cell align="left" valign="middle"><em>107.6</em></cell> - </row> - <row> - <cell align="left" valign="middle"><c>Selective and Complete</c></cell> - <cell align="left" valign="middle"><c>selected_­decode_­Window2/1</c></cell> - <cell align="left" valign="middle"><c>6795319</c></cell> - <cell align="left" valign="middle"><c>GUI</c></cell> - <cell align="left" valign="middle"><em>115.4</em></cell> - </row> - <row> - <cell align="left" valign="middle"><c>Exclusive and decode_part</c></cell> - <cell align="left" valign="middle"><c>decode_­Window_­status_­exclusive/1</c></cell> - <cell align="left" valign="middle"><c>6249200</c></cell> - <cell align="left" valign="middle"><c>GUI</c></cell> - <cell align="left" valign="middle"><em>106.1</em></cell> - </row> - <tcaption>Results of Complete, Exclusive + decode_part, and Selective + complete decodes</tcaption> - </table> - <p>Other ASN.1 types and values can differ much from these - figures. It is therefore important that you, in every case where - you intend to use either of these decodes, perform some tests - that show if you will benefit your purpose. - </p> - </section> - - <section> - <title>Final Remarks</title> - <list type="bulleted"> - <item>The gain of using selective and exclusive decode instead of a - complete decode is greater the bigger the value and the - less deep in the structure you have to decode.</item> - <item>Use selective decode instead of exclusive decode if you are - interested in only a single subvalue.</item> - <item>Exclusive decode followed by - <c>decode_part</c> decodes is attractive if the parts are sent - to different servers for decoding, or if you in some cases are not - interested in all parts.</item> - <item>The fastest selective decode is when the decoded type is a - primitive type and not so deep in the structure of the top - type. <c>selected_decode_Window2</c> decodes a high constructed - value, which explains why this operation is relatively slow.</item> - <item>It can vary from case to case which combination of - selective/complete decode or exclusive/part decode is the fastest.</item> - </list> + {'Action',21,{'Button',22,false}}]}}}.</input> +2> <input>Bin = 'GUI':encode('Window',Val).</input> +<<161,89,128,1,12,161,32,48,6,128,1,13,129,1,255,48,6,128, + 1,14,129,1,0,48,6,128,1,15,129,...>> +4> <input>'GUI':selected_decode_Window1(Bin).</input> +13 +5> <input>'GUI':selected_decode_Window2(Bin).</input> +18</pre> </section> </section> </chapter> diff --git a/lib/asn1/src/Makefile b/lib/asn1/src/Makefile index 9e13d02c8a..06329840c4 100644 --- a/lib/asn1/src/Makefile +++ b/lib/asn1/src/Makefile @@ -66,6 +66,7 @@ CT_MODULES= \ asn1ct_tok \ asn1ct_parser2 \ asn1ct_table \ + asn1ct_partial_decode \ $(EVAL_CT_MODULES) RT_MODULES= \ diff --git a/lib/asn1/src/asn1.app.src b/lib/asn1/src/asn1.app.src index 12a6de88bc..793b70df4e 100644 --- a/lib/asn1/src/asn1.app.src +++ b/lib/asn1/src/asn1.app.src @@ -10,5 +10,5 @@ ]}, {env, []}, {applications, [kernel, stdlib]}, - {runtime_dependencies, ["stdlib-3.13","kernel-7.0","erts-11.0"]} + {runtime_dependencies, ["stdlib-5.0","kernel-9.0","erts-14.0"]} ]}. diff --git a/lib/asn1/src/asn1_db.erl b/lib/asn1/src/asn1_db.erl index c7799d0762..a8101edb27 100644 --- a/lib/asn1/src/asn1_db.erl +++ b/lib/asn1/src/asn1_db.erl @@ -106,7 +106,9 @@ loop(#state{parent = Parent, monitor = MRef, table = Table, loop(State); {save, OutFile, Mod} -> Mtab = ets:lookup_element(Table, Mod, 2), - TempFile = OutFile ++ ".#temp", + TempFile = OutFile ++ + integer_to_list(erlang:unique_integer([positive])) ++ + ".#temp", ok = ets:tab2file(Mtab, TempFile), ok = file:rename(TempFile, OutFile), loop(State); diff --git a/lib/asn1/src/asn1ct.erl b/lib/asn1/src/asn1ct.erl index 3788f77789..54436e5b97 100644 --- a/lib/asn1/src/asn1ct.erl +++ b/lib/asn1/src/asn1ct.erl @@ -42,14 +42,13 @@ maybe_rename_function/3,current_sindex/0, set_current_sindex/1,maybe_saved_sindex/2, parse_and_save/2,verbose/3,warning/3,warning/4,error/3,format_error/1]). +-export([save_config/2,save_gen_state/2,save_gen_state/3]). -export([get_bit_string_format/0,use_legacy_types/0]). -include("asn1_records.hrl"). -include_lib("stdlib/include/erl_compile.hrl"). -include_lib("kernel/include/file.hrl"). --import(asn1ct_gen_ber_bin_v2,[encode_tag_val/3,decode_class/1]). - -ifndef(vsn). -define(vsn,"0.0.1"). -endif. @@ -59,24 +58,6 @@ -define(dupl_equaldefs,2). -define(dupl_eqdefs_uniquedefs,?dupl_equaldefs bor ?dupl_uniquedefs). --define(CONSTRUCTED, 2#00100000). - -%% macros used for partial decode commands --define(CHOOSEN,choosen). --define(SKIP,skip). --define(SKIP_OPTIONAL,skip_optional). - -%% macros used for partial incomplete decode commands --define(MANDATORY,mandatory). --define(DEFAULT,default). --define(OPTIONAL,opt). --define(OPTIONAL_UNDECODED,opt_undec). --define(PARTS,parts). --define(UNDECODED,undec). --define(ALTERNATIVE,alt). --define(ALTERNATIVE_UNDECODED,alt_undec). --define(ALTERNATIVE_PARTS,alt_parts). - %% Removed functions -removed({decode,'_',"use Mod:decode/2 instead"}). @@ -259,8 +240,12 @@ abs_listing(#st{code={M,_},outfile=OutFile}) -> generate_pass(#st{code=Code,outfile=OutFile,erule=Erule,opts=Opts}=St0) -> St = St0#st{code=undefined}, %Reclaim heap space - generate(Code, OutFile, Erule, Opts), - {ok,St}. + case generate(Code, OutFile, Erule, Opts) of + ok -> + {ok,St}; + {error,Errors} -> + {error,St#st{error=Errors}} + end. compile_pass(#st{outfile=OutFile,opts=Opts0}=St) -> asn1_db:dbstop(), %Reclaim memory. @@ -335,11 +320,21 @@ clean_errors(Errors) when is_list(Errors) -> {Structured,Structured ++ AdHoc}; clean_errors(AdHoc) -> {[],AdHoc}. -print_structured_errors([_|_]=Errors) -> - _ = [io:format("~ts:~w: ~ts\n", [F,L,M:format_error(E)]) || - {structured_error,{F,L},M,E} <- Errors], - ok; -print_structured_errors(_) -> ok. +print_structured_errors(Errors) when is_list(Errors) -> + _ = [print_structured_error(F, M, E) || + {structured_error,F,M,E} <- Errors], + ok. + +print_structured_error(F, M, Error) -> + Formatted = M:format_error(Error), + case F of + none -> + io:format("~ts\n", [Formatted]); + {File,none} -> + io:format("~ts: ~ts\n", [File,Formatted]); + {File,Line} when is_integer(Line) -> + io:format("~ts:~p: ~ts\n", [File,Line,Formatted]) + end. compile1(File, #st{opts=Opts}=St0) -> compiler_verbose(File, Opts), @@ -864,21 +859,17 @@ generate({M,CodeTuple}, OutFile, EncodingRule, Options) -> check_maps_option(Gen), %% create decoding function names and taglists for partial decode - try - specialized_decode_prepare(Gen, M) - catch - throw:{error, Reason} -> - warning("Error in configuration file: ~n~p~n", - [Reason], Options, - "Error in configuration file") - end, - - asn1ct_gen:pgen(OutFile, Gen, Code), - cleanup_bit_string_format(), - erase(tlv_format), % used in ber - erase(class_default_type),% used in ber - asn1ct_table:delete(check_functions), - ok. + case specialized_decode_prepare(Gen, M) of + {error,_}=Error -> + Error; + ok -> + asn1ct_gen:pgen(OutFile, Gen, Code), + cleanup_bit_string_format(), + erase(tlv_format), % used in ber + erase(class_default_type), % used in ber + asn1ct_table:delete(check_functions), + ok + end. init_gen_record(EncodingRule, Options) -> Erule = case EncodingRule of @@ -1446,205 +1437,27 @@ prepare_bytes(Bytes) -> list_to_binary(Bytes). vsn() -> ?vsn. -specialized_decode_prepare(#gen{erule=ber,options=Options}=Gen, M) -> +specialized_decode_prepare(#gen{erule=ber,options=Options}=Gen, #module{name=Mod}) -> case lists:member(asn1config, Options) of - true -> - special_decode_prepare_1(Gen, M); - false -> - ok + true -> + case read_config_file(Gen, Mod) of + {ok,ConfigName,ConfigItems} -> + try + asn1ct_partial_decode:prepare(ConfigItems, Mod) + catch + throw:{structured_error,Error} -> + {error,[{structured_error,{ConfigName,none}, + asn1ct_partial_decode,Error}]} + end; + no_config_file -> + ok + end; + false -> + ok end; specialized_decode_prepare(_, _) -> ok. -%% Reads the configuration file if it exists and stores information -%% about partial decode and incomplete decode -special_decode_prepare_1(#gen{options=Options}=Gen, M) -> - %% read configure file - ModName = case lists:keyfind(asn1config, 1, Options) of - {_,MName} -> MName; - false -> M#module.name - end, -%% io:format("ModName: ~p~nM#module.name: ~p~n~n",[ModName,M#module.name]), - case read_config_file(Gen, ModName) of - no_config_file -> - ok; - CfgList -> - SelectedDecode = get_config_info(CfgList,selective_decode), - ExclusiveDecode = get_config_info(CfgList,exclusive_decode), - CommandList = create_partial_decode_gen_info(M#module.name, - SelectedDecode), - %% To convert CommandList to a proper list for the driver change - %% the list:[[choosen,Tag1],skip,[skip_optional,Tag2]] to L = - %% [5,2,Tag1,0,1,Tag2] where 5 is the length, and call - %% port_control(asn1_driver_port,3,[L| Bin]) - save_config(partial_decode,CommandList), - save_gen_state(selective_decode,SelectedDecode), - CommandList2 = create_partial_inc_decode_gen_info(M#module.name, - ExclusiveDecode), - Part_inc_tlv_tags = tlv_tags(CommandList2), - save_config(partial_incomplete_decode,Part_inc_tlv_tags), - save_gen_state(exclusive_decode,ExclusiveDecode,Part_inc_tlv_tags) - end. - -%% create_partial_inc_decode_gen_info/2 -%% -%% Creates a list of tags out of the information in TypeNameList that -%% tells which value will be incomplete decoded, i.e. each end -%% component/type in TypeNameList. The significant types/components in -%% the path from the toptype must be specified in the -%% TypeNameList. Significant elements are all constructed types that -%% branches the path to the leaf and the leaf it self. -%% -%% Returns a list of elements, where an element may be one of -%% mandatory|[opt,Tag]|[bin,Tag]. mandatory correspond to a mandatory -%% element that shall be decoded as usual. [opt,Tag] matches an -%% OPTIONAL or DEFAULT element that shall be decoded as -%% usual. [bin,Tag] corresponds to an element, mandatory, OPTIONAL or -%% DEFAULT, that shall be left encoded (incomplete decoded). -create_partial_inc_decode_gen_info(ModName,{Mod,[{Name,L}|Ls]}) when is_list(L) -> - TopTypeName = partial_inc_dec_toptype(L), - [{Name,TopTypeName, - create_partial_inc_decode_gen_info1(ModName,TopTypeName,{Mod,L})}| - create_partial_inc_decode_gen_info(ModName,{Mod,Ls})]; -create_partial_inc_decode_gen_info(_,{_,[]}) -> - []; -create_partial_inc_decode_gen_info(_,[]) -> - []. - -create_partial_inc_decode_gen_info1(ModName,TopTypeName,{ModName, - [_TopType|Rest]}) -> - case asn1_db:dbget(ModName,TopTypeName) of - #typedef{typespec=TS} -> - TagCommand = get_tag_command(TS,?MANDATORY,mandatory), - create_pdec_inc_command(ModName,get_components(TS#type.def), - Rest,[TagCommand]); - _ -> - throw({error,{"wrong type list in asn1 config file", - TopTypeName}}) - end; -create_partial_inc_decode_gen_info1(M1,_,{M2,_}) when M1 /= M2 -> - throw({error,{"wrong module name in asn1 config file", - M2}}); -create_partial_inc_decode_gen_info1(_,_,TNL) -> - throw({error,{"wrong type list in asn1 config file", - TNL}}). - -%% -%% Only when there is a 'ComponentType' the config data C1 may be a -%% list, where the incomplete decode is branched. So, C1 may be a -%% list, a "binary tuple", a "parts tuple" or an atom. The second -%% element of a binary tuple and a parts tuple is an atom. -create_pdec_inc_command(_ModName,_,[],Acc) -> - lists:reverse(Acc); -create_pdec_inc_command(ModName,{Comps1,Comps2},TNL,Acc) - when is_list(Comps1),is_list(Comps2) -> - create_pdec_inc_command(ModName,Comps1 ++ Comps2,TNL,Acc); -%% The following two clauses match on the type after the top -%% type. This one if the top type had no tag, i.e. a CHOICE. -create_pdec_inc_command(ModN,Clist,[CL|_Rest],[[]]) when is_list(CL) -> - create_pdec_inc_command(ModN,Clist,CL,[]); -create_pdec_inc_command(ModN,Clist,[CL|_Rest],Acc) when is_list(CL) -> - InnerDirectives=create_pdec_inc_command(ModN,Clist,CL,[]), - lists:reverse([InnerDirectives|Acc]); -create_pdec_inc_command(ModName, - CList=[#'ComponentType'{name=Name,typespec=TS, - prop=Prop}|Comps], - TNL=[C1|Cs],Acc) -> - case C1 of - {Name,undecoded} -> - TagCommand = get_tag_command(TS,?UNDECODED,Prop), - create_pdec_inc_command(ModName,Comps,Cs,concat_sequential(TagCommand,Acc)); - {Name,parts} -> - TagCommand = get_tag_command(TS,?PARTS,Prop), - create_pdec_inc_command(ModName,Comps,Cs,concat_sequential(TagCommand,Acc)); - L when is_list(L) -> - %% I guess this never happens due to previous clause. - %% This case is only possible as the first element after - %% the top type element, when top type is SEGUENCE or SET. - %% Follow each element in L. Must note every tag on the - %% way until the last command is reached, but it ought to - %% be enough to have a "complete" or "complete optional" - %% command for each component that is not specified in the - %% config file. Then in the TLV decode the components with - %% a "complete" command will be decoded by an ordinary TLV - %% decode. - create_pdec_inc_command(ModName,CList,L,Acc); - {Name,RestPartsList} when is_list(RestPartsList) -> - %% Same as previous, but this may occur at any place in - %% the structure. The previous is only possible as the - %% second element. - case get_tag_command(TS,?MANDATORY,Prop) of - ?MANDATORY -> - InnerDirectives= - create_pdec_inc_command(ModName,TS#type.def, - RestPartsList,[]), - create_pdec_inc_command(ModName,Comps,Cs, - [[?MANDATORY,InnerDirectives]|Acc]); - [Opt,EncTag] -> - InnerDirectives = - create_pdec_inc_command(ModName,TS#type.def, - RestPartsList,[]), - create_pdec_inc_command(ModName,Comps,Cs, - [[Opt,EncTag,InnerDirectives]|Acc]) - end; - _ -> - %% this component may not be in the config list - TagCommand = get_tag_command(TS,?MANDATORY,Prop), - create_pdec_inc_command(ModName,Comps,TNL,concat_sequential(TagCommand,Acc)) - end; -create_pdec_inc_command(ModName, - {'CHOICE',[#'ComponentType'{name=C1, - typespec=TS, - prop=Prop}|Comps]}, - [{C1,Directive}|Rest],Acc) -> - case Directive of - List when is_list(List) -> - TagCommand = get_tag_command(TS,?ALTERNATIVE,Prop), - CompAcc = - create_pdec_inc_command(ModName, - get_components(TS#type.def),List,[]), - NewAcc = case TagCommand of - [Command,Tag] when is_atom(Command) -> - [[Command,Tag,CompAcc]|Acc]; - [L1,_L2|Rest] when is_list(L1) -> - case lists:reverse(TagCommand) of - [Atom|Comms] when is_atom(Atom) -> - [concat_sequential(lists:reverse(Comms), - [Atom,CompAcc])|Acc]; - [[Command2,Tag2]|Comms] -> - [concat_sequential(lists:reverse(Comms), - [[Command2,Tag2,CompAcc]])|Acc] - end - end, - create_pdec_inc_command(ModName,{'CHOICE',Comps},Rest, - NewAcc); - undecoded -> - TagCommand = get_tag_command(TS,?ALTERNATIVE_UNDECODED,Prop), - create_pdec_inc_command(ModName,{'CHOICE',Comps},Rest, - concat_sequential(TagCommand,Acc)); - parts -> - TagCommand = get_tag_command(TS,?ALTERNATIVE_PARTS,Prop), - create_pdec_inc_command(ModName,{'CHOICE',Comps},Rest, - concat_sequential(TagCommand,Acc)) - end; -create_pdec_inc_command(ModName, - {'CHOICE',[#'ComponentType'{typespec=TS, - prop=Prop}|Comps]}, - TNL,Acc) -> - TagCommand = get_tag_command(TS,?ALTERNATIVE,Prop), - create_pdec_inc_command(ModName,{'CHOICE',Comps},TNL, - concat_sequential(TagCommand,Acc)); -create_pdec_inc_command(M,{'CHOICE',{Cs1,Cs2}},TNL,Acc) - when is_list(Cs1),is_list(Cs2) -> - create_pdec_inc_command(M,{'CHOICE',Cs1 ++ Cs2},TNL,Acc); -create_pdec_inc_command(ModName,#'Externaltypereference'{module=M,type=Name}, - TNL,Acc) -> - #type{def=Def} = get_referenced_type(M,Name), - create_pdec_inc_command(ModName,get_components(Def),TNL,Acc); -create_pdec_inc_command(_,_,TNL,_) -> - throw({error,{"unexpected error when creating partial " - "decode command",TNL}}). - partial_inc_dec_toptype([T|_]) when is_atom(T) -> T; partial_inc_dec_toptype([{T,_}|_]) when is_atom(T) -> @@ -1654,272 +1467,12 @@ partial_inc_dec_toptype([L|_]) when is_list(L) -> partial_inc_dec_toptype(_) -> throw({error,{"no top type found for partial incomplete decode"}}). - -%% Creates a list of tags out of the information in TypeList and Types -%% that tells which value will be decoded. Each constructed type that -%% is in the TypeList will get a "choosen" command. Only the last -%% type/component in the TypeList may be a primitive type. Components -%% "on the way" to the final element may get the "skip" or the -%% "skip_optional" command. -%% CommandList = [Elements] -%% Elements = {choosen,Tag}|{skip_optional,Tag}|skip -%% Tag is a binary with the tag BER encoded. -create_partial_decode_gen_info(ModName,{ModName,TypeLists}) -> - [create_partial_decode_gen_info1(ModName,TL) || TL <- TypeLists]; -create_partial_decode_gen_info(_,[]) -> - []; -create_partial_decode_gen_info(_M1,{M2,_}) -> - throw({error,{"wrong module name in asn1 config file", - M2}}). - -create_partial_decode_gen_info1(ModName,{FuncName,TypeList}) -> - case TypeList of - [TopType|Rest] -> - case asn1_db:dbget(ModName,TopType) of - #typedef{typespec=TS} -> - TagCommand = get_tag_command(TS,?CHOOSEN), - Ret=create_pdec_command(ModName, - get_components(TS#type.def), - Rest,concat_tags(TagCommand,[])), - {FuncName,Ret}; - _ -> - throw({error,{"wrong type list in asn1 config file", - TypeList}}) - end; - _ -> - [] - end; -create_partial_decode_gen_info1(_,_) -> - ok. - -%% create_pdec_command/4 for each name (type or component) in the -%% third argument, TypeNameList, a command is created. The command has -%% information whether the component/type shall be skipped, looked -%% into or returned. The list of commands is returned. -create_pdec_command(_ModName,_,[],Acc) -> - Remove_empty_lists = - fun([[]|L],Res,Fun) -> - Fun(L,Res,Fun); - ([],Res,_) -> - Res; - ([H|L],Res,Fun) -> - Fun(L,[H|Res],Fun) - end, - Remove_empty_lists(Acc,[],Remove_empty_lists); -create_pdec_command(ModName,[#'ComponentType'{name=C1,typespec=TS}|_Comps], - [C1|Cs],Acc) -> - %% this component is a constructed type or the last in the - %% TypeNameList otherwise the config spec is wrong - TagCommand = get_tag_command(TS,?CHOOSEN), - create_pdec_command(ModName,get_components(TS#type.def), - Cs,concat_tags(TagCommand,Acc)); -create_pdec_command(ModName,[#'ComponentType'{typespec=TS, - prop=Prop}|Comps], - [C2|Cs],Acc) -> - TagCommand = - case Prop of - mandatory -> - get_tag_command(TS,?SKIP); - _ -> - get_tag_command(TS,?SKIP_OPTIONAL) - end, - create_pdec_command(ModName,Comps,[C2|Cs],concat_tags(TagCommand,Acc)); -create_pdec_command(ModName,{'CHOICE',[Comp=#'ComponentType'{name=C1}|_]},TNL=[C1|_Cs],Acc) -> - create_pdec_command(ModName,[Comp],TNL,Acc); -create_pdec_command(ModName,{'CHOICE',[#'ComponentType'{}|Comps]},TNL,Acc) -> - create_pdec_command(ModName,{'CHOICE',Comps},TNL,Acc); -create_pdec_command(ModName,{'CHOICE',{Cs1,Cs2}},TNL,Acc) - when is_list(Cs1),is_list(Cs2) -> - create_pdec_command(ModName,{'CHOICE',Cs1 ++ Cs2},TNL,Acc); -create_pdec_command(ModName,#'Externaltypereference'{module=M,type=C1}, - TypeNameList,Acc) -> - #type{def=Def} = get_referenced_type(M,C1), - create_pdec_command(ModName,get_components(Def),TypeNameList, - Acc); -create_pdec_command(ModName,TS=#type{def=Def},[C1|Cs],Acc) -> - %% This case when we got the "components" of a SEQUENCE/SET OF - case C1 of - [1] -> - %% A list with an integer is the only valid option in a 'S - %% OF', the other valid option would be an empty - %% TypeNameList saying that the entire 'S OF' will be - %% decoded. - TagCommand = get_tag_command(TS,?CHOOSEN), - create_pdec_command(ModName,Def,Cs,concat_tags(TagCommand,Acc)); - [N] when is_integer(N) -> - TagCommand = get_tag_command(TS,?SKIP), - create_pdec_command(ModName,Def,[[N-1]|Cs], - concat_tags(TagCommand,Acc)); - Err -> - throw({error,{"unexpected error when creating partial " - "decode command",Err}}) - end; -create_pdec_command(_,_,TNL,_) -> - throw({error,{"unexpected error when creating partial " - "decode command",TNL}}). - -get_components(#'SEQUENCE'{components={C1,C2}}) when is_list(C1),is_list(C2) -> - C1++C2; -get_components(#'SEQUENCE'{components=Components}) -> - Components; -get_components(#'SET'{components={C1,C2}}) when is_list(C1),is_list(C2) -> - C1++C2; -get_components(#'SET'{components=Components}) -> - Components; -get_components({'SEQUENCE OF',Components}) -> - Components; -get_components({'SET OF',Components}) -> - Components; -get_components(Def) -> - Def. - -concat_sequential(L=[A,B],Acc) when is_atom(A),is_binary(B) -> - [L|Acc]; -concat_sequential(L,Acc) when is_list(L) -> - concat_sequential1(lists:reverse(L),Acc); -concat_sequential(A,Acc) -> - [A|Acc]. -concat_sequential1([],Acc) -> - Acc; -concat_sequential1([[]],Acc) -> - Acc; -concat_sequential1([El|RestEl],Acc) when is_list(El) -> - concat_sequential1(RestEl,[El|Acc]); -concat_sequential1([mandatory|RestEl],Acc) -> - concat_sequential1(RestEl,[mandatory|Acc]); -concat_sequential1(L,Acc) -> - [L|Acc]. - - -many_tags([?SKIP])-> - false; -many_tags([?SKIP_OPTIONAL,_]) -> - false; -many_tags([?CHOOSEN,_]) -> - false; -many_tags(_) -> - true. - -concat_tags(Ts,Acc) -> - case many_tags(Ts) of - true when is_list(Ts) -> - lists:reverse(Ts)++Acc; - true -> - [Ts|Acc]; - false -> - [Ts|Acc] - end. -%% get_tag_command(Type,Command) - -%% Type is the type that has information about the tag Command tells -%% what to do with the encoded value with the tag of Type when -%% decoding. -get_tag_command(#type{tag=[]},_) -> - []; -%% SKIP and SKIP_OPTIONAL shall return only one tag command regardless -get_tag_command(#type{},?SKIP) -> - ?SKIP; -get_tag_command(#type{tag=Tags},?SKIP_OPTIONAL) -> - Tag=hd(Tags), - [?SKIP_OPTIONAL,encode_tag_val(decode_class(Tag#tag.class), - Tag#tag.form,Tag#tag.number)]; -get_tag_command(#type{tag=[Tag]},Command) -> - %% encode the tag according to BER - [Command,encode_tag_val(decode_class(Tag#tag.class),Tag#tag.form, - Tag#tag.number)]; -get_tag_command(T=#type{tag=[Tag|Tags]},Command) -> - TC = get_tag_command(T#type{tag=[Tag]},Command), - TCs = get_tag_command(T#type{tag=Tags},Command), - case many_tags(TCs) of - true when is_list(TCs) -> - [TC|TCs]; - _ -> [TC|[TCs]] - end. - -%% get_tag_command/3 used by create_pdec_inc_command -get_tag_command(#type{tag=[]},_,_) -> - []; -get_tag_command(#type{tag=[Tag]},?MANDATORY,Prop) -> - case Prop of - mandatory -> - ?MANDATORY; - {'DEFAULT',_} -> - [?DEFAULT,encode_tag_val(decode_class(Tag#tag.class), - Tag#tag.form,Tag#tag.number)]; - _ -> [?OPTIONAL,encode_tag_val(decode_class(Tag#tag.class), - Tag#tag.form,Tag#tag.number)] - end; -get_tag_command(#type{tag=[Tag]},Command,Prop) -> - [anonymous_dec_command(Command,Prop),encode_tag_val(decode_class(Tag#tag.class),Tag#tag.form, Tag#tag.number)]; -get_tag_command(#type{tag=Tag},Command,Prop) when is_record(Tag,tag) -> - get_tag_command(#type{tag=[Tag]},Command,Prop); -get_tag_command(T=#type{tag=[Tag|Tags]},Command,Prop) -> - [get_tag_command(T#type{tag=[Tag]},Command,Prop)|[ - get_tag_command(T#type{tag=Tags},Command,Prop)]]. - -anonymous_dec_command(?UNDECODED,'OPTIONAL') -> - ?OPTIONAL_UNDECODED; -anonymous_dec_command(Command,_) -> - Command. - -get_referenced_type(M,Name) -> - case asn1_db:dbget(M,Name) of - #typedef{typespec=TS} -> - case TS of - #type{def=#'Externaltypereference'{module=M2,type=Name2}} -> - %% The tags have already been taken care of in the - %% first reference where they were gathered in a - %% list of tags. - get_referenced_type(M2,Name2); - #type{} -> TS; - _ -> - throw({error,{"unexpected element when" - " fetching referenced type",TS}}) - end; - T -> - throw({error,{"unexpected element when fetching " - "referenced type",T}}) - end. - - -tlv_tags([]) -> - []; -tlv_tags([mandatory|Rest]) -> - [mandatory|tlv_tags(Rest)]; -tlv_tags([[Command,Tag]|Rest]) when is_atom(Command),is_binary(Tag) -> - [[Command,tlv_tag(Tag)]|tlv_tags(Rest)]; -tlv_tags([[Command,Directives]|Rest]) when is_atom(Command),is_list(Directives) -> - [[Command,tlv_tags(Directives)]|tlv_tags(Rest)]; -%% remove all empty lists -tlv_tags([[]|Rest]) -> - tlv_tags(Rest); -tlv_tags([{Name,TopType,L1}|Rest]) when is_list(L1),is_atom(TopType) -> - [{Name,TopType,tlv_tags(L1)}|tlv_tags(Rest)]; -tlv_tags([[Command,Tag,L1]|Rest]) when is_list(L1),is_binary(Tag) -> - [[Command,tlv_tag(Tag),tlv_tags(L1)]|tlv_tags(Rest)]; -tlv_tags([[mandatory|Rest]]) -> - [[mandatory|tlv_tags(Rest)]]; -tlv_tags([L=[L1|_]|Rest]) when is_list(L1) -> - [tlv_tags(L)|tlv_tags(Rest)]. - -tlv_tag(<<Cl:2,_:1,TagNo:5>>) when TagNo < 31 -> - (Cl bsl 16) + TagNo; -tlv_tag(<<Cl:2,_:1,31:5,0:1,TagNo:7>>) -> - (Cl bsl 16) + TagNo; -tlv_tag(<<Cl:2,_:1,31:5,Buffer/binary>>) -> - TagNo = tlv_tag1(Buffer,0), - (Cl bsl 16) + TagNo. -tlv_tag1(<<0:1,PartialTag:7>>,Acc) -> - (Acc bsl 7) bor PartialTag; -tlv_tag1(<<1:1,PartialTag:7,Buffer/binary>>,Acc) -> - tlv_tag1(Buffer,(Acc bsl 7) bor PartialTag). - %% Reads the content from the configuration file and returns the %% selected part chosen by InfoType. Assumes that the config file %% content is an Erlang term. read_config_file_info(ModuleName, InfoType) when is_atom(InfoType) -> Name = ensure_ext(ModuleName, ".asn1config"), - CfgList = read_config_file0(Name, []), + {ok,_,CfgList} = read_config_file0(Name, []), get_config_info(CfgList, InfoType). read_config_file(#gen{options=Options}, ModuleName) -> @@ -1927,12 +1480,13 @@ read_config_file(#gen{options=Options}, ModuleName) -> Includes = [I || {i,I} <- Options], read_config_file0(Name, ["."|Includes]). -read_config_file0(Name, [D|Dirs]) -> - case file:consult(filename:join(D, Name)) of +read_config_file0(Name0, [Dir|Dirs]) -> + Name = filename:join(Dir, Name0), + case file:consult(Name) of {ok,CfgList} -> - CfgList; + {ok,Name,CfgList}; {error,enoent} -> - read_config_file0(Name, Dirs); + read_config_file0(Name0, Dirs); {error,Reason} -> Error = "error reading asn1 config file: " ++ file:format_error(Reason), @@ -2396,7 +1950,7 @@ verbose(Format, Args, S) -> end. format_error({write_error,File,Reason}) -> - io_lib:format("writing output file ~s failed: ~s", + io_lib:format(<<"writing output file ~ts failed: ~s">>, [File,file:format_error(Reason)]). is_error(#state{options=Opts}) -> diff --git a/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl b/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl index 8468010726..d6049a12a3 100644 --- a/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl +++ b/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl @@ -1194,14 +1194,23 @@ gen_dec_line(Erules,TopType,Cname,CTags,Type,OptOrMand,DecObjInf) -> Pdec; _ -> - emit(["[{",{asis,FirstTag}, - ",",{curr,v},"}|Temp", - {curr,tlv}, - "] ->",nl]), + DecTag = + case asn1ct:get_gen_state_field(namelist) of + [{Cname,undecoded}|_] -> + emit(["[",{curr,v},"|Temp",{curr,tlv},"] ", + "when is_binary(",{curr,v},") ->",nl]), + Tag; + _ -> + emit(["[{",{asis,FirstTag}, + ",",{curr,v},"}|Temp", + {curr,tlv}, + "] ->",nl]), + RestTag + end, emit([indent(4),"{"]), Pdec= gen_dec_call(InnerType,Erules,TopType,Cname, - Type,BytesVar,RestTag,mandatory, + Type,BytesVar,DecTag,mandatory, ", mandatory, ",DecObjInf, OptOrMand), @@ -1358,10 +1367,9 @@ gen_dec_call1(WhatKind, _, TopType, Cname, Type, BytesVar, Tag) -> %% This is to prepare SEQUENCE OF value in %% partial incomplete decode for a later %% part-decode, i.e. skip %% the tag. - asn1ct:add_generated_refed_func({[Cname|TopType], - parts, - [],Type}), - emit(["{'",asn1ct_gen:list2name([Cname|TopType]),"',"]), + Id = [parts,Cname|TopType], + asn1ct:add_generated_refed_func({Id,parts,[],Type}), + emit(["{'",asn1ct_gen:list2name(Id),"',"]), asn1ct_func:need({ber,match_tags,2}), EmitDecFunCall("match_tags"), emit("}"); diff --git a/lib/asn1/src/asn1ct_gen.erl b/lib/asn1/src/asn1ct_gen.erl index 5305260f95..308781491a 100644 --- a/lib/asn1/src/asn1ct_gen.erl +++ b/lib/asn1/src/asn1ct_gen.erl @@ -295,7 +295,7 @@ pgen_partial_types1(Erules,[{FuncName,[TopType|RestTypes]}|Rest]) -> CurrMod = get(currmod), TypeDef = asn1_db:dbget(CurrMod,TopType), traverse_type_structure(Erules,TypeDef,RestTypes,FuncName, - TypeDef#typedef.name), + [TypeDef#typedef.name]), pgen_partial_types1(Erules,Rest); pgen_partial_types1(_,[]) -> ok; @@ -479,24 +479,20 @@ pgen_partial_incomplete_decode1(#gen{erule=ber}) -> gen_part_decode_funcs(GeneratedFs,0); pgen_partial_incomplete_decode1(#gen{}) -> ok. -emit_partial_incomplete_decode({FuncName,TopType,Pattern}) -> +emit_partial_incomplete_decode({FuncName,TopType,Pattern}) + when is_atom(TopType) -> TypePattern = asn1ct:get_gen_state_field(inc_type_pattern), - TPattern = - case lists:keysearch(FuncName,1,TypePattern) of - {value,{_,TP}} -> TP; - _ -> exit({error,{asn1_internal_error,exclusive_decode}}) - end, + {_,TPattern} = lists:keyfind(FuncName, 1, TypePattern), TopTypeName = - case asn1ct:maybe_saved_sindex(TopType,TPattern) of - I when is_integer(I),I>0 -> - lists:concat([TopType,"_",I]); - _ -> - atom_to_list(TopType) - end, + case asn1ct:maybe_saved_sindex(TopType, TPattern) of + I when is_integer(I), I > 0 -> + list_to_atom(lists:concat([TopType,"_",I])); + _ -> + TopType + end, emit([{asis,FuncName},"(Bytes) ->",nl, - " decode_partial_incomplete('",TopTypeName,"',Bytes,",{asis,Pattern},").",nl]); -emit_partial_incomplete_decode(D) -> - throw({error,{asn1,{"bad data in asn1config file",D}}}). + " decode_partial_incomplete(",{asis,TopTypeName},", Bytes, ", + {asis,Pattern},").",nl]). gen_part_decode_funcs([Data={Name,_,_,Type}|GeneratedFs],N) -> InnerType = @@ -507,12 +503,18 @@ gen_part_decode_funcs([Data={Name,_,_,Type}|GeneratedFs],N) -> get_inner(Type#type.def) end, WhatKind = type(InnerType), - TypeName=list2name(Name), + DispatchId = list_to_atom(list2name(Name)), + TypeName = case Name of + [parts|TypeName0] -> + list2name(TypeName0); + _ -> + list2name(Name) + end, if N > 0 -> emit([";",nl]); true -> ok end, - emit(["decode_inc_disp('",TypeName,"',Data) ->",nl]), + emit(["decode_inc_disp(",{asis,DispatchId},",Data) ->",nl]), gen_part_decode_funcs(WhatKind,TypeName,Data), gen_part_decode_funcs(GeneratedFs,N+1); gen_part_decode_funcs([_H|T],N) -> @@ -779,7 +781,7 @@ pgen_dispatcher(Gen, Types) -> emit(["try ",Call," of",nl, " Bytes ->",nl, " {ok,Bytes}",nl, - try_catch()]) + try_catch(),".",nl]) end, emit([nl,nl]), @@ -794,7 +796,7 @@ pgen_dispatcher(Gen, Types) -> emit(["try ",JerCall," of",nl, " Bytes ->",nl, " {ok,Bytes}",nl, - try_catch()]) + try_catch(),".",nl]) end, emit([nl,nl]); false -> @@ -854,7 +856,7 @@ pgen_dispatcher(Gen, Types) -> case NoOkWrapper of false -> - emit([nl,try_catch(),nl,nl]); + emit([nl,try_catch(),".",nl,nl]); true -> emit([".",nl,nl]) end, @@ -874,7 +876,7 @@ pgen_dispatcher(Gen, Types) -> result_line(false, ["Result"]), case NoOkWrapper of false -> - emit([nl,try_catch(),nl,nl]); + emit([nl,try_catch(),".",nl,nl]); true -> emit([".",nl,nl]) end; @@ -884,7 +886,7 @@ pgen_dispatcher(Gen, Types) -> %% REST of MODULE - gen_decode_partial_incomplete(Gen), + gen_decode_partial_incomplete(Gen, NoOkWrapper), gen_partial_inc_dispatcher(Gen), case Gen of @@ -916,7 +918,7 @@ try_catch() -> " Reason ->",nl, " {error,{asn1,{Reason,Stk}}}",nl, " end",nl, - "end."]. + "end"]. gen_info_functions(Gen) -> Erule = case Gen of @@ -938,7 +940,7 @@ gen_info_functions(Gen) -> "legacy_erlang_types() -> ", {asis,asn1ct:use_legacy_types()},".",nl,nl]). -gen_decode_partial_incomplete(#gen{erule=ber}) -> +gen_decode_partial_incomplete(#gen{erule=ber}, NoOkWrapper) -> case {asn1ct:read_config_data(partial_incomplete_decode), asn1ct:get_gen_state_field(inc_type_pattern)} of {undefined,_} -> @@ -946,37 +948,44 @@ gen_decode_partial_incomplete(#gen{erule=ber}) -> {_,undefined} -> ok; _ -> - EmitCaseClauses = - fun() -> - emit([" {'EXIT',{error,Reason}} ->",nl, - " {error,Reason};",nl, - " {'EXIT',Reason} ->",nl, - " {error,{asn1,Reason}};",nl, - " Result ->",nl, - " {ok,Result}",nl, - " end"]) - end, - emit(["decode_partial_incomplete(Type,Data0,", - "Pattern) ->",nl]), - emit([" {Data,_RestBin} =",nl, - " ",{call,ber,decode_primitive_incomplete, - ["Pattern","Data0"]},com,nl, - " case catch decode_partial_inc_disp(Type,", - "Data) of",nl]), - EmitCaseClauses(), - emit([".",nl,nl]), - emit(["decode_part(Type, Data0) " - "when is_binary(Data0) ->",nl]), - emit([" case catch decode_inc_disp(Type,element(1, ", - {call,ber,ber_decode_nif,["Data0"]},")) of",nl]), - EmitCaseClauses(), - emit([";",nl]), - emit(["decode_part(Type, Data0) ->",nl]), - emit([" case catch decode_inc_disp(Type, Data0) of",nl]), - EmitCaseClauses(), - emit([".",nl,nl]) + emit(["decode_partial_incomplete(Type, Data0, Pattern) ->",nl, + " {Data,_RestBin} =",nl, + " ",{call,ber,decode_primitive_incomplete, + ["Pattern","Data0"]},com,nl]), + case NoOkWrapper of + true -> + emit([" decode_partial_inc_disp(Type, Data)",nl]); + false -> + emit([" try {ok,decode_partial_inc_disp(Type, Data)}",nl, + try_catch()]) + end, + emit([".",nl,nl]), + + emit(["decode_part(Type, Data0) when is_binary(Data0) ->",nl]), + case NoOkWrapper of + true -> + emit([" decode_inc_disp(Type, element(1, ", + {call,ber,ber_decode_nif,["Data0"]}, + "))",nl]); + false -> + emit([" try {ok,decode_inc_disp(Type, element(1, ", + {call,ber,ber_decode_nif,["Data0"]}, + "))}",nl, + try_catch()]) + end, + emit([";",nl]), + + emit(["decode_part(Type, Data0) ->",nl]), + case NoOkWrapper of + true -> + emit([" decode_inc_disp(Type, Data0)"]); + false -> + emit([" try {ok,decode_inc_disp(Type, Data0)}",nl, + try_catch()]) + end, + emit([".",nl,nl]) end; -gen_decode_partial_incomplete(#gen{}) -> +gen_decode_partial_incomplete(#gen{}, _) -> ok. gen_partial_inc_dispatcher(#gen{erule=ber}) -> diff --git a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl index 0e7d07e92a..1f0ac29582 100644 --- a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl +++ b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl @@ -30,7 +30,7 @@ -export([gen_encode_prim/4]). -export([gen_dec_prim/3]). -export([gen_objectset_code/2, gen_obj_code/3]). --export([encode_tag_val/3]). +-export([encode_tag_val/3,tag_to_integer/1]). -export([gen_inc_decode/2,gen_decode_selected/3]). -export([extaddgroup2sequence/1]). -export([dialyzer_suppressions/1]). @@ -72,16 +72,33 @@ dialyzer_suppressions(_) -> false -> ok; true -> suppress({ber,encode_bit_string,4}) end, - suppress({ber,decode_selective,2}), + + %% The `no_match` option for dialyzer will suppress clauses whose + %% patterns will never match. Here we must ensure that dialyzer + %% sees calls to all helper functions to avoid warnings for + %% functions that will never be called. + %% + %% We provide argument lists that avoid dialyzer warnings, + %% but stills allows the compiler to do some optimizations. + + Args1 = ["element(5, Arg)", "[skip,{skip_optional,<<0>>},{chosen,<<0>>}]"], + suppress({ber,decode_selective,2}, Args1), + + Args2 = ["[{undecoded,0},{alt_parts,0}]", "element(6, Arg)"], + suppress({ber,decode_primitive_incomplete,2}, Args2), + emit([" ok.",nl]). -suppress({M,F,A}=MFA) -> +suppress({_,_,A}=MFA) -> + Args = [lists:concat(["element(",I,", Arg)"]) || I <- lists:seq(1, A)], + suppress(MFA, Args). + +suppress({M,F,_}=MFA, Args) -> case asn1ct_func:is_used(MFA) of false -> ok; true -> - Args = [lists:concat(["element(",I,", Arg)"]) || I <- lists:seq(1, A)], - emit([" ",{call,M,F,Args},com,nl]) + emit([" _ = ",{call,M,F,Args},com,nl]) end. %%=============================================================================== @@ -353,19 +370,25 @@ gen_inc_decode(Erules,Type) when is_record(Type,typedef) -> gen_decode_selected(Erules,Type,FuncName) -> emit([FuncName,"(Bin) ->",nl]), Patterns = asn1ct:read_config_data(partial_decode), - Pattern = - case lists:keysearch(FuncName,1,Patterns) of - {value,{_,P}} -> P; - false -> exit({error,{internal,no_pattern_saved}}) - end, + {_,Pattern} = lists:keyfind(FuncName, 1, Patterns), emit([" case ",{call,ber,decode_selective, [{asis,Pattern},"Bin"]}," of",nl, " {ok,Bin2} when is_binary(Bin2) ->",nl, " {Tlv,_} = ", {call,ber,ber_decode_nif,["Bin2"]},com,nl]), - emit("{ok,"), - gen_decode_selected_type(Erules,Type), - emit(["};",nl," Err -> exit({error,{selective_decode,Err}})",nl, - " end.",nl]). + NoOkWrapper = proplists:get_bool(no_ok_wrapper, Erules#gen.options), + case NoOkWrapper of + true -> ok; + false -> emit("{ok,") + end, + gen_decode_selected_type(Erules, Type), + case NoOkWrapper of + true -> + ok; + false -> + emit(["};",nl, + " Err -> exit({error,{selective_decode,Err}})"]) + end, + emit([" end.",nl]). gen_decode_selected_type(_Erules,TypeDef) -> Def = TypeDef#typedef.typespec, @@ -382,14 +405,9 @@ gen_decode_selected_type(_Erules,TypeDef) -> asn1ct_name:new(len), gen_dec_prim(Def, BytesVar, Tag); {constructed,bif} -> - TopType = case TypeDef#typedef.name of - A when is_atom(A) -> [A]; - N -> N - end, - DecFunName = lists:concat(["'",dec,"_", - asn1ct_gen:list2name(TopType),"'"]), - emit([DecFunName,"(",BytesVar, - ", ",{asis,Tag},")"]); + TopType = TypeDef#typedef.name, + DecFunName = dec_func(asn1ct_gen:list2name(TopType)), + emit([DecFunName,"(",BytesVar,", ",{asis,Tag},")"]); TheType -> DecFunName = mkfuncname(TheType,dec), emit([DecFunName,"(",BytesVar, @@ -1527,6 +1545,10 @@ get_object_field(Name,ObjectFields) -> false -> false end. +tag_to_integer(#tag{class=Class,number=N}) + when is_integer(N), 0 =< N, N =< 1 bsl 16 -> + decode_class(Class) bsl 10 bor N. + %%encode_tag(TagClass(?UNI, APP etc), Form (?PRIM etx), TagInteger) -> %% 8bit Int | binary encode_tag_val(Class, Form, TagNo) when (TagNo =< 30) -> diff --git a/lib/asn1/src/asn1ct_partial_decode.erl b/lib/asn1/src/asn1ct_partial_decode.erl new file mode 100644 index 0000000000..c4df9be4df --- /dev/null +++ b/lib/asn1/src/asn1ct_partial_decode.erl @@ -0,0 +1,396 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2023. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +-module(asn1ct_partial_decode). +-export([prepare/2,format_error/1]). + +-include("asn1_records.hrl"). + +prepare(Items, Mod) -> + _ = [prepare_item(Item, Mod) || Item <- Items], + ok. + +format_error({bad_decode_instruction,Term}) -> + io_lib:format(<<"badly formed exclusive decode instruction: ~p">>, + [Term]); +format_error({bad_exclusive_decode,Term}) -> + io_lib:format(<<"badly formed exclusive decode instructions: ~p">>, + [Term]); +format_error({bad_module_name,Mod,ShouldBe}) -> + io_lib:format(<<"the module name is ~p; expected it to be the same as the name" + " of the ASN.1 module (~p)">>, + [Mod,ShouldBe]); +format_error({bad_selective_decode,Term}) -> + io_lib:format(<<"badly formed selective decode instructions: ~p">>, + [Term]); +format_error({bad_selective_decode_element,Term}) -> + io_lib:format(<<"badly formed element in selective decode instruction: ~p">>, + [Term]); +format_error({bad_selective_decode_type_list,Term}) -> + io_lib:format(<<"badly formed type list in selective decode instruction: ~p">>, + [Term]); +format_error({stepping_into_primitive,Path}) -> + io_lib:format(<<"the tail end of selective decode instructions attempts to ", + "step into a primitive type:\n ~p">>, + [Path]); +format_error({undefined_name,Type}) -> + io_lib:format(<<"name ~p not found">>, [Type]); +format_error({undefined_type,Type}) -> + io_lib:format(<<"type ~p does not exist">>, [Type]). + +%%% +%%% Common macros. +%%% + +-define(ASN1CT_GEN_BER, asn1ct_gen_ber_bin_v2). + +%%% +%%% Start of local functions. +%%% + +prepare_item({selective_decode,SelectedDecode}, Mod) -> + CommandList = selective_decode(Mod, SelectedDecode), + asn1ct:save_config(partial_decode, CommandList), + asn1ct:save_gen_state(selective_decode, SelectedDecode), + ok; +prepare_item({exclusive_decode,ExclusiveDecode}, Mod) -> + ExclusiveCommands = exclusive_decode(Mod, ExclusiveDecode), + asn1ct:save_config(partial_incomplete_decode, ExclusiveCommands), + asn1ct:save_gen_state(exclusive_decode, ExclusiveDecode, ExclusiveCommands), + ok. + +%%% +%%% Handle exclusive decode. +%%% + +-define(MANDATORY, mandatory). +-define(DEFAULT, default). +-define(DEFAULT_UNDECODED, default_undecoded). +-define(OPTIONAL, opt). +-define(OPTIONAL_UNDECODED, opt_undecoded). +-define(PARTS, parts). +-define(UNDECODED, undecoded). +-define(ALTERNATIVE, alt). +-define(ALTERNATIVE_UNDECODED, alt_undecoded). +-define(ALTERNATIVE_PARTS, alt_parts). + +exclusive_decode(Mod, {ModI,Instructions}) when is_list(Instructions) -> + if + Mod =:= ModI -> + exclusive_decode_1(Mod, Instructions); + true -> + cfg_error({bad_module_name,ModI,Mod}) + end; +exclusive_decode(_Mod, Term) -> + cfg_error({bad_exclusive_decode,Term}). + +exclusive_decode_1(Mod, [{FunName,[TopType,Directives0]}|Is]) + when is_atom(FunName), is_atom(TopType), is_list(Directives0) -> + Directives = exclusive_decode_map(Directives0), + [{FunName,TopType,exclusive_decode_2(Mod, TopType, Directives)} | + exclusive_decode_1(Mod, Is)]; +exclusive_decode_1(_Mod, []) -> + []; +exclusive_decode_1(_Mod, Term) -> + cfg_error({bad_exclusive_decode,Term}). + +exclusive_decode_2(ModName, TopType, Directives) -> + case asn1_db:dbget(ModName, TopType) of + #typedef{typespec=TS} -> + Acc = get_tag_command(TS, ?MANDATORY, mandatory), + exclusive_decode_command(get_components(TS#type.def), + Directives, Acc); + undefined -> + cfg_error({undefined_type,TopType}) + end. + +exclusive_decode_map(Commands) -> + exclusive_decode_map(Commands, []). + +exclusive_decode_map([H|T], Acc) -> + case H of + {Name,Command0} when is_atom(Name) -> + Command = + if + Command0 =:= ?UNDECODED; Command0 =:= ?PARTS -> + Command0; + is_list(Command0) -> + exclusive_decode_map(Command0, []); + true -> + cfg_error({bad_decode_instruction,H}) + end, + exclusive_decode_map(T, [{Name,Command}|Acc]); + _ -> + cfg_error({bad_decode_instruction,H}) + end; +exclusive_decode_map([], Acc) -> + maps:from_list(Acc). + +exclusive_decode_command(_, Commands, Acc) when map_size(Commands) =:= 0 -> + lists:reverse(Acc); +exclusive_decode_command([#'ComponentType'{name=Name,typespec=TS, + prop=Prop}|Comps], + Commands0, Acc) -> + case maps:take(Name, Commands0) of + {Command,Commands} when is_atom(Command) -> + TagCommands = get_tag_command(TS, Command, Prop), + exclusive_decode_command(Comps, Commands, TagCommands++Acc); + {InnerCommands0,Commands} when is_map(Commands0) -> + InnerCommands = exclusive_decode_command(TS#type.def, + InnerCommands0, []), + case get_tag_command(TS, ?MANDATORY, Prop) of + [?MANDATORY] -> + exclusive_decode_command(Comps, Commands, + [{?MANDATORY,InnerCommands}|Acc]); + [{Opt,EncTag}] -> + exclusive_decode_command(Comps, Commands, + [{Opt,EncTag,InnerCommands}|Acc]) + end; + error -> + case get_tag_command(TS, ?MANDATORY, Prop) of + [] -> + case TS of + #type{def=#'Externaltypereference'{}} -> + exclusive_decode_command(Comps, Commands0, [mandatory|Acc]); + _ -> + exclusive_decode_command(Comps, Commands0, Acc) + end; + [_|_]=TagCommands -> + exclusive_decode_command(Comps, Commands0, TagCommands ++ Acc) + end + end; +exclusive_decode_command({'CHOICE',[_|_]=Cs}, Commands, Acc) -> + exclusive_decode_choice_cs(Cs, Commands, Acc); +exclusive_decode_command({'CHOICE',{Cs1,Cs2}}, Commands, Acc) + when is_list(Cs1), is_list(Cs2) -> + exclusive_decode_choice_cs(Cs1 ++ Cs2, Commands, Acc); +exclusive_decode_command(#'Externaltypereference'{module=M,type=Name}, + Commands, Acc) -> + #type{def=Def} = get_referenced_type(M, Name), + exclusive_decode_command(get_components(Def), Commands, Acc); +exclusive_decode_command([], Commands, _) -> + [{Name,_}|_] = lists:keysort(1, maps:to_list(Commands)), + cfg_error({undefined_name,Name}). + +exclusive_decode_choice_cs(_, Commands, Acc) when map_size(Commands) =:= 0 -> + lists:reverse(Acc); +exclusive_decode_choice_cs([#'ComponentType'{name=Name,typespec=TS}|Cs], + Commands0, Acc) -> + case maps:take(Name, Commands0) of + {Inner,Commands} -> + case Inner of + ?UNDECODED -> + TagCommands = get_tag_command(TS, ?ALTERNATIVE_UNDECODED, mandatory), + exclusive_decode_choice_cs(Cs, Commands, TagCommands ++ Acc); + ?PARTS -> + TagCommands = get_tag_command(TS, ?ALTERNATIVE_PARTS, mandatory), + exclusive_decode_choice_cs(Cs, Commands, TagCommands ++ Acc); + _ when is_map(Inner) -> + [{Command,Tag}] = get_tag_command(TS, ?ALTERNATIVE, mandatory), + CompAcc = exclusive_decode_command(get_components(TS#type.def), Inner, []), + exclusive_decode_choice_cs(Cs, Commands, [{Command,Tag,CompAcc}|Acc]) + end; + error -> + TagCommands = get_tag_command(TS, ?ALTERNATIVE, mandatory), + exclusive_decode_choice_cs(Cs, Commands0, TagCommands ++ Acc) + end. + +get_tag_command(#type{tag=[]}, _, _) -> + []; +get_tag_command(#type{tag=[Tag]}, ?MANDATORY, Prop) -> + [case Prop of + mandatory -> + ?MANDATORY; + {'DEFAULT',_} -> + {?DEFAULT,?ASN1CT_GEN_BER:tag_to_integer(Tag)}; + _ -> + {?OPTIONAL,?ASN1CT_GEN_BER:tag_to_integer(Tag)} + end]; +get_tag_command(#type{tag=[_|_]=Tags}, ?PARTS=Command, Prop) -> + [{anonymous_dec_command(Command, Prop), + [?ASN1CT_GEN_BER:tag_to_integer(Tag) || Tag <- Tags]}]; +get_tag_command(#type{tag=[_|_]=Tags}, ?UNDECODED=Command, Prop) -> + [{anonymous_dec_command(Command, Prop), + [?ASN1CT_GEN_BER:tag_to_integer(Tag) || Tag <- Tags]}]; +get_tag_command(#type{tag=[Tag]}, Command, Prop) -> + [{anonymous_dec_command(Command, Prop), + ?ASN1CT_GEN_BER:tag_to_integer(Tag)}]; +get_tag_command(#type{tag=[_|_]=Tags}=Type, Command, Prop) -> + lists:reverse([hd(get_tag_command(Type#type{tag=[Tag]}, Command, Prop)) || + Tag <- Tags]). + +anonymous_dec_command(?UNDECODED, 'OPTIONAL') -> + ?OPTIONAL_UNDECODED; +anonymous_dec_command(?UNDECODED, {'DEFAULT',_}) -> + ?DEFAULT_UNDECODED; +anonymous_dec_command(Command,_) -> + Command. + +%%% +%%% Selective decode. +%%% + +-define(CHOSEN, chosen). +-define(SKIP, skip). +-define(SKIP_OPTIONAL, skip_optional). + +selective_decode(Mod, {ModI,TypeLists}) -> + if + Mod =:= ModI -> + selective_decode1(Mod, TypeLists); + true -> + cfg_error({bad_module_name,ModI,Mod}) + end; +selective_decode(_, Bad) -> + cfg_error({bad_selective_decode,Bad}). + +selective_decode1(Mod, [TL|TypeLists]) -> + [selective_decode2(Mod, TL) | + selective_decode1(Mod, TypeLists)]; +selective_decode1(_, []) -> + []; +selective_decode1(_, Bad) -> + cfg_error({bad_selective_decode,Bad}). + +selective_decode2(ModName, {FuncName,TypeList}) -> + case TypeList of + [TopType|Types] -> + case asn1_db:dbget(ModName, TopType) of + #typedef{typespec=TS} -> + TagCommand = get_tag_command(TS, ?CHOSEN), + Ret = selective_decode_command(get_components(TS#type.def), + Types, concat_tags(TagCommand, [])), + {FuncName,Ret}; + undefined -> + cfg_error({undefined_type,TopType}) + end; + _ -> + cfg_error({bad_selective_decode_type_list,TypeList}) + end; +selective_decode2(_, Bad) -> + cfg_error({bad_selective_decode,Bad}). + +selective_decode_command(_, [], Acc) -> + lists:reverse(Acc); +selective_decode_command([#'ComponentType'{name=Name,typespec=TS}|_], + [Name], Acc) -> + TagCommand = get_tag_command(TS, ?CHOSEN), + lists:reverse(concat_tags(TagCommand, Acc)); +selective_decode_command([#'ComponentType'{name=Name,typespec=TS}|_], + [Name|Cs], Acc) -> + case asn1ct_gen:type(asn1ct_gen:get_inner(TS#type.def)) of + {primitive,bif} -> + cfg_error({stepping_into_primitive,[Name|Cs]}); + _ -> + TagCommand = get_tag_command(TS, ?CHOSEN), + selective_decode_command(get_components(TS#type.def), + Cs, concat_tags(TagCommand, Acc)) + end; +selective_decode_command([#'ComponentType'{typespec=TS, + prop=Prop}|Comps], + [_|_]=Cs, Acc) -> + TagCommand = case Prop of + mandatory -> + get_tag_command(TS, ?SKIP); + _ -> + get_tag_command(TS, ?SKIP_OPTIONAL) + end, + selective_decode_command(Comps, Cs, concat_tags(TagCommand, Acc)); +selective_decode_command({'CHOICE',[_|_]=Cs}, [Name|_]=TNL, Acc) -> + case lists:keyfind(Name, #'ComponentType'.name, Cs) of + #'ComponentType'{}=C -> + selective_decode_command([C], TNL, Acc); + false -> + cfg_error({undefined_name,Name}) + end; +selective_decode_command({'CHOICE',{Cs1,Cs2}}, TNL, Acc) + when is_list(Cs1), is_list(Cs2) -> + selective_decode_command({'CHOICE',Cs1 ++ Cs2}, TNL, Acc); +selective_decode_command(#'Externaltypereference'{module=M,type=C1}, + TypeNameList, Acc) -> + #type{def=Def} = get_referenced_type(M, C1), + selective_decode_command(get_components(Def), TypeNameList, Acc); +selective_decode_command(#type{def=Def}=TS, [C1|Cs], Acc0) -> + case C1 of + [N] when is_integer(N), N >= 1 -> + SkipTags = lists:duplicate(N - 1, ?SKIP), + Acc = SkipTags ++ Acc0, + TagCommand = get_tag_command(TS, ?CHOSEN), + selective_decode_command(Def, Cs, concat_tags(TagCommand, Acc)); + Bad -> + cfg_error({bad_selective_decode_element,Bad}) + end; +selective_decode_command(_, [Name], _) -> + cfg_error({undefined_name,Name}). + +get_tag_command(#type{tag=[]}, _) -> + []; +get_tag_command(#type{}, ?SKIP) -> + [?SKIP]; +get_tag_command(#type{tag=[Tag|_]}, ?SKIP_OPTIONAL) -> + #tag{class=Class,form=Form,number=TagNo} = Tag, + [{?SKIP_OPTIONAL, + ?ASN1CT_GEN_BER:encode_tag_val(?ASN1CT_GEN_BER:decode_class(Class), + Form, TagNo)}]; +get_tag_command(#type{tag=[Tag]}, Command) -> + #tag{class=Class,form=Form,number=TagNo} = Tag, + [{Command, + ?ASN1CT_GEN_BER:encode_tag_val(?ASN1CT_GEN_BER:decode_class(Class), + Form, TagNo)}]; +get_tag_command(T=#type{tag=[Tag|Tags]}, Command) -> + TC = get_tag_command(T#type{tag=[Tag]}, Command), + TCs = get_tag_command(T#type{tag=Tags}, Command), + TC ++ TCs. + +concat_tags(Ts, Acc) when is_list(Ts) -> + lists:reverse(Ts, Acc). + +%%% +%%% Common utilities. +%%% + +get_components(#'SEQUENCE'{components={Cs1,Cs2}}) when is_list(Cs1), is_list(Cs2) -> + Cs1 ++ Cs2; +get_components(#'SEQUENCE'{components=Cs}) when is_list(Cs) -> + Cs; +get_components(#'SET'{components={Cs1,Cs2}}) when is_list(Cs1), is_list(Cs2) -> + Cs1 ++ Cs2; +get_components(#'SET'{components=Cs}) when is_list(Cs) -> + Cs; +get_components({'SEQUENCE OF',#type{}=Component})-> + Component; +get_components({'SET OF',#type{}=Component}) -> + Component; +get_components(Def) -> + Def. + +get_referenced_type(M0, Name0) -> + #typedef{typespec=TS} = asn1_db:dbget(M0, Name0), + case TS of + #type{def=#'Externaltypereference'{module=M,type=Name}} -> + %% The tags have already been taken care of in the first + %% reference where they were gathered in a list of tags. + get_referenced_type(M, Name); + #type{} -> + TS + end. + +cfg_error(Error) -> + throw({structured_error,Error}). diff --git a/lib/asn1/src/asn1rtt_ber.erl b/lib/asn1/src/asn1rtt_ber.erl index f2458d25c5..13df1c7327 100644 --- a/lib/asn1/src/asn1rtt_ber.erl +++ b/lib/asn1/src/asn1rtt_ber.erl @@ -160,22 +160,29 @@ decode_constructed_indefinite(Bin,Acc) -> %% decode_primitive_incomplete/2 decodes an encoded message incomplete %% by help of the pattern attribute (first argument). -decode_primitive_incomplete([[default,TagNo]],Bin) -> %default +decode_primitive_incomplete([{default,TagNo}], Bin) -> case decode_tag_and_length(Bin) of {Form,TagNo,V,Rest} -> - decode_incomplete2(Form,TagNo,V,[],Rest); + decode_incomplete2(Form, TagNo, V, [], Rest); _ -> asn1_NOVALUE end; -decode_primitive_incomplete([[default,TagNo,Directives]],Bin) -> +decode_primitive_incomplete([{default,TagNo,Directives}], Bin) -> %% default, constructed type, Directives points into this type case decode_tag_and_length(Bin) of {Form,TagNo,V,Rest} -> - decode_incomplete2(Form,TagNo,V,Directives,Rest); + decode_incomplete2(Form, TagNo, V, Directives, Rest); + _ -> + asn1_NOVALUE + end; +decode_primitive_incomplete([{default_undecoded,[Tag|_]}], Bin) -> + case decode_tag_and_length(Bin) of + {_,Tag,_,_} -> + decode_incomplete_bin(Bin); _ -> asn1_NOVALUE end; -decode_primitive_incomplete([[opt,TagNo]],Bin) -> +decode_primitive_incomplete([{opt,TagNo}],Bin) -> %% optional case decode_tag_and_length(Bin) of {Form,TagNo,V,Rest} -> @@ -183,74 +190,79 @@ decode_primitive_incomplete([[opt,TagNo]],Bin) -> _ -> asn1_NOVALUE end; -decode_primitive_incomplete([[opt,TagNo,Directives]],Bin) -> +decode_primitive_incomplete([{opt,TagNo,Directives}], Bin) -> %% optional case decode_tag_and_length(Bin) of {Form,TagNo,V,Rest} -> - decode_incomplete2(Form,TagNo,V,Directives,Rest); + decode_incomplete2(Form, TagNo, V, Directives, Rest); _ -> asn1_NOVALUE end; %% An optional that shall be undecoded -decode_primitive_incomplete([[opt_undec,Tag]],Bin) -> +decode_primitive_incomplete([{opt_undecoded,[Tag|_]}], Bin) -> case decode_tag_and_length(Bin) of {_,Tag,_,_} -> - decode_incomplete_bin(Bin); + decode_incomplete_bin(Bin); _ -> asn1_NOVALUE end; %% A choice alternative that shall be undecoded -decode_primitive_incomplete([[alt_undec,TagNo]|RestAlts],Bin) -> +decode_primitive_incomplete([{alt_undecoded,TagNo}|RestAlts], Bin) -> case decode_tag_and_length(Bin) of {_,TagNo,_,_} -> decode_incomplete_bin(Bin); _ -> - decode_primitive_incomplete(RestAlts,Bin) + decode_primitive_incomplete(RestAlts, Bin) end; -decode_primitive_incomplete([[alt,TagNo]|RestAlts],Bin) -> +decode_primitive_incomplete([{alt,TagNo}|RestAlts], Bin) -> case decode_tag_and_length(Bin) of {_Form,TagNo,V,Rest} -> {{TagNo,V},Rest}; _ -> decode_primitive_incomplete(RestAlts,Bin) end; -decode_primitive_incomplete([[alt,TagNo,Directives]|RestAlts],Bin) -> +decode_primitive_incomplete([{alt,TagNo,Directives}|RestAlts], Bin) -> case decode_tag_and_length(Bin) of {Form,TagNo,V,Rest} -> decode_incomplete2(Form,TagNo,V,Directives,Rest); _ -> decode_primitive_incomplete(RestAlts,Bin) end; -decode_primitive_incomplete([[alt_parts,TagNo]],Bin) -> +decode_primitive_incomplete([{alt_parts,TagNo}], Bin) -> case decode_tag_and_length(Bin) of {_Form,TagNo,V,Rest} -> {{TagNo,V},Rest}; _ -> asn1_NOVALUE end; -decode_primitive_incomplete([[alt_parts,TagNo]|RestAlts],Bin) -> +decode_primitive_incomplete([{alt_parts,TagNo}|RestAlts], Bin) -> case decode_tag_and_length(Bin) of {_Form,TagNo,V,Rest} -> {{TagNo,decode_parts_incomplete(V)},Rest}; _ -> - decode_primitive_incomplete(RestAlts,Bin) + decode_primitive_incomplete(RestAlts, Bin) end; -decode_primitive_incomplete([[undec,_TagNo]|_RestTag],Bin) -> - %% incomlete decode +decode_primitive_incomplete([{undecoded,_TagNo}|_RestTag], Bin) -> decode_incomplete_bin(Bin); -decode_primitive_incomplete([[parts,TagNo]|_RestTag],Bin) -> +decode_primitive_incomplete([{parts,[TagNo|MoreTags]}|_RestTag], Bin) -> case decode_tag_and_length(Bin) of {_Form,TagNo,V,Rest} -> - {{TagNo,decode_parts_incomplete(V)},Rest}; + case MoreTags of + [] -> + {{TagNo,decode_parts_incomplete(V)},Rest}; + [TagNo2] -> + {_,TagNo2,V2,<<>>} = decode_tag_and_length(V), + {{TagNo,{TagNo2,decode_parts_incomplete(V2)}},Rest} + end; Err -> {error,{asn1,"tag failure",TagNo,Err}} end; -decode_primitive_incomplete([mandatory|RestTag],Bin) -> +decode_primitive_incomplete([mandatory|RestTag], Bin) -> {Form,TagNo,V,Rest} = decode_tag_and_length(Bin), decode_incomplete2(Form,TagNo,V,RestTag,Rest); %% A choice that is a toptype or a mandatory component of a %% SEQUENCE or SET. -decode_primitive_incomplete([[mandatory|Directives]],Bin) -> +decode_primitive_incomplete([{mandatory,Directives}], Bin) -> {Form,TagNo,V,Rest} = decode_tag_and_length(Bin), decode_incomplete2(Form,TagNo,V,Directives,Rest); decode_primitive_incomplete([],Bin) -> @@ -269,7 +281,7 @@ decode_parts_incomplete(Bin) -> %% decode_incomplete2 checks if V is a value of a constructed or -%% primitive type, and continues the decode propeerly. +%% primitive type, and continues the decode properly. decode_incomplete2(_Form=2,TagNo,V,TagMatch,_) -> %% constructed indefinite length {Vlist,Rest2} = decode_constr_indef_incomplete(TagMatch,V,[]), @@ -288,16 +300,16 @@ decode_constructed_incomplete(_TagMatch,<<>>) -> decode_constructed_incomplete([mandatory|RestTag],Bin) -> {Tlv,Rest} = decode_primitive(Bin), [Tlv|decode_constructed_incomplete(RestTag,Rest)]; -decode_constructed_incomplete(Directives=[[Alt,_]|_],Bin) - when Alt =:= alt_undec; Alt =:= alt; Alt =:= alt_parts -> +decode_constructed_incomplete([{Alt,_}|_]=Directives, Bin) + when Alt =:= alt_undecoded; Alt =:= alt; Alt =:= alt_parts -> {_Form,TagNo,V,Rest} = decode_tag_and_length(Bin), case incomplete_choice_alt(TagNo, Directives) of - {alt_undec,_} -> + {alt_undecoded,_} -> LenA = byte_size(Bin) - byte_size(Rest), <<A:LenA/binary,Rest/binary>> = Bin, A; {alt,InnerDirectives} -> - {Tlv,Rest} = decode_primitive_incomplete(InnerDirectives,V), + {Tlv,Rest} = decode_primitive_incomplete(InnerDirectives, V), {TagNo,Tlv}; {alt_parts,_} -> [{TagNo,decode_parts_incomplete(V)}]; @@ -305,7 +317,7 @@ decode_constructed_incomplete(Directives=[[Alt,_]|_],Bin) %% if a choice alternative was encoded that %% was not specified in the config file, %% thus decode component anonomous. - {Tlv,_}=decode_primitive(Bin), + {Tlv,_} = decode_primitive(Bin), Tlv end; decode_constructed_incomplete([TagNo|RestTag],Bin) -> @@ -337,12 +349,12 @@ decode_incomplete_bin(Bin) -> <<IncBin:IncLen/binary,Ret/binary>> = Bin, {IncBin,Ret}. -incomplete_choice_alt(TagNo,[[Alt,TagNo]|Directives]) -> +incomplete_choice_alt(TagNo, [{Alt,TagNo}|Directives]) -> {Alt,Directives}; -incomplete_choice_alt(TagNo,[D]) when is_list(D) -> - incomplete_choice_alt(TagNo,D); -incomplete_choice_alt(TagNo,[_H|Directives]) -> - incomplete_choice_alt(TagNo,Directives); +incomplete_choice_alt(TagNo, [D]) when is_list(D) -> + incomplete_choice_alt(TagNo, D); +incomplete_choice_alt(TagNo, [_H|Directives]) -> + incomplete_choice_alt(TagNo, Directives); incomplete_choice_alt(_,[]) -> no_match. @@ -353,35 +365,35 @@ incomplete_choice_alt(_,[]) -> %% Returns {ok,Value} or {error,Reason} %% Value is a binary that in turn must be decoded to get the decoded %% value. -decode_selective([],Binary) -> +decode_selective([], Binary) -> {ok,Binary}; -decode_selective([skip|RestPattern],Binary)-> - {ok,RestBinary}=skip_tag(Binary), - {ok,RestBinary2}=skip_length_and_value(RestBinary), - decode_selective(RestPattern,RestBinary2); -decode_selective([[skip_optional,Tag]|RestPattern],Binary) -> - case skip_optional_tag(Tag,Binary) of - {ok,RestBinary} -> - {ok,RestBinary2}=skip_length_and_value(RestBinary), - decode_selective(RestPattern,RestBinary2); - missing -> - decode_selective(RestPattern,Binary) +decode_selective([skip|RestPattern], Binary)-> + {ok,RestBinary} = skip_tag(Binary), + {ok,RestBinary2} = skip_length_and_value(RestBinary), + decode_selective(RestPattern, RestBinary2); +decode_selective([{skip_optional,Tag}|RestPattern], Binary) -> + case skip_optional_tag(Tag, Binary) of + {ok,RestBinary} -> + {ok,RestBinary2} = skip_length_and_value(RestBinary), + decode_selective(RestPattern, RestBinary2); + missing -> + decode_selective(RestPattern, Binary) end; -decode_selective([[choosen,Tag]],Binary) -> - return_value(Tag,Binary); -decode_selective([[choosen,Tag]|RestPattern],Binary) -> - case skip_optional_tag(Tag,Binary) of - {ok,RestBinary} -> - {ok,Value} = get_value(RestBinary), - decode_selective(RestPattern,Value); - missing -> - {ok,<<>>} +decode_selective([{chosen,Tag}], Binary) -> + return_value(Tag, Binary); +decode_selective([{chosen,Tag}|RestPattern], Binary) -> + case skip_optional_tag(Tag, Binary) of + {ok,RestBinary} -> + {ok,Value} = get_value(RestBinary), + decode_selective(RestPattern,Value); + missing -> + {ok,<<>>} end; decode_selective(P,_) -> {error,{asn1,{partial_decode,"bad pattern",P}}}. return_value(Tag,Binary) -> - {ok,{Tag,RestBinary}}=get_tag(Binary), + {ok,{Tag,RestBinary}} = get_tag(Binary), {ok,{LenVal,_RestBinary2}} = get_length_and_value(RestBinary), {ok,<<Tag/binary,LenVal/binary>>}. diff --git a/lib/asn1/test/asn1_SUITE.erl b/lib/asn1/test/asn1_SUITE.erl index e531b346f8..e4e3b0b767 100644 --- a/lib/asn1/test/asn1_SUITE.erl +++ b/lib/asn1/test/asn1_SUITE.erl @@ -965,11 +965,15 @@ specialized_decodes(Config, Rule, Opts) -> "PartialDecSeq2.asn", "PartialDecSeq3.asn", "PartialDecMyHTTP.asn", - "MEDIA-GATEWAY-CONTROL.asn", "P-Record", - "PartialDecChoExtension.asn"], + "PartialDecChoExtension.asn", + "OCSP-2013-88.asn1", + "PKIX1Explicit88.asn1"], Config, - [Rule,legacy_erlang_types,asn1config|Opts]), + [Rule,asn1config|Opts]), + asn1_test_lib:compile("MEDIA-GATEWAY-CONTROL.asn", + Config, + [Rule,legacy_erlang_types,asn1config|Opts]), test_partial_incomplete_decode:test(Config), test_selective_decode:test(). diff --git a/lib/asn1/test/asn1_SUITE_data/BasicOCSPResponse.ber b/lib/asn1/test/asn1_SUITE_data/BasicOCSPResponse.ber new file mode 100644 index 0000000000000000000000000000000000000000..016ce972c3c43ee50986eca0ddcb1f3aa2ef05e9 GIT binary patch literal 1577 zcmXqLVpBC}Vq{*}*lEz%W+-UD&&C|e!py^wT9lKRm+tInC~P1I65{6Jas>-1_=f}- ziWmri1i5&)U5hf4i;MH~6daulg$x8hqU=1J!6o_0*%|pcxrW>ZoFFkaVWwbLLsbK1 zHcqWJkGAi;jGT;OVg;!ssYMQ{MVS?PKmu%zUU8}fzmb8Fv4N$bg`uH=xshd*L5e|= zffdjoR&73JCMi}17Lmu3|7@F`+NgVIkyvcKUctd5G9O>Ch@88$XMbE#QHYG7h5okA zZ;76{-?W(+IU5)-j9egyEM#Z|vV|A!Hb!n%2IeM4Muy&bOaF`1FV&b<=kznldoS;I z_LifJdO|DfeoYr%9R4`E_PE^ZXJPW2+#as$xc|#*XD^rRzn7)l*3p-yacun}Bf0cs zLz~Q}9iF$YE#jHExKQe|#oWi%cHBxQ#Z;~&=2-9fe^QRaHjR1ptcBJtIycYEOP%W& z^R_11Ow3-Nap#sErBhSh@MNg^n+dH9ew$N%NNrxQmYPP?sq^cuzH?sj+%e(3lSzxi z<i>gHf-=Kq2J+PD{+SbR^iJfhsa|LOJ<Ad<|2E~Py3N9-HS4eR@3>Gnare~Tb+?av zZg?tkVeO82x(_X;P296Uut7hpZ)TyKPNf0I_u6u2q2yb_JeNGx{r@U0XkuYAXkuY9 zXkz|n(8Tm?0W%XL6O)K{%r>W`Ggq*F@14VK@wrVTp{!~GTJQ~;)MOwh&Wn_qOrt<t zLj!2yGL0HM4sBvoLJmYma2n)i0A^1vCQxEr-6JD&c3zgHpz_l6sx>XWy$N^vIllX3 zsyuQlU)b$Z$1`g~uw3KBsE%95qIE8d_upDCzWUTn>xx&3Rc@Uvo!r;UZ>R-0JhxjS z`rhkK*k2DLm6*IPei=LV!#20FRo+XNeN*0(;gz&hb#l<GQx|U}@#y)SQ{3##tr;qD z#r+$jQ%>KC9?8}^Z3pc?UpGu#wc1qDxY8_M&h59)&1@sF>!C0B4O92H%q?9Z#6Gts zbK99n*~bTac{VigeEHfVzO~Jb=Re2PwSnO?zs+8EOkrnv_lnzr`*?%&9g>1V4>6vX zulGy8VizxPAZx)Chl}%8eTmeLy}|fAOW@h_XoalWqKmRj%!~|-i<1lzz=f2oFeBrC z7FGjhAZ5S}EMsK(Sy+I{lg&UD#NlHRV-e|Jrrz{?bxzmZpOOLjm6Nsv{rYyzKoKM@ z$RcGR(TJk}LXHJkNx-A+v+RiV^d`&s`P-HU`l}o_p5gR*>$iJ{mx}tXW&5loWs>$T zzh>_BPgg%htg)Rf9HX8!y{qIh?_1k~?`-ostxAuc@BjGOyOqDl{OKkqCf7?7gMafT z@Yzji%l!5)`PRbLXy%l=ZLK|5EPtsr<vG`!Y+Bd5|3EY6o`k<sm`txY{tq;me#38G ztkBWvoHExJTJrkMQ?wRYEU^1g^Ir9{KOF<kI(`d~`}J^o!t3Bzz6+-$otAXm>5Z>s zJ1+9S#(BqbllPOhdOgxs+a|HLJoWW`<C&e4zMc%?J)pL}<Bx-vg~yeP2h<I#ykeiE ccW1t^)B5=GtMJ3m_ZBi_rF@ukd!o}i0GF&hvH$=8 literal 0 HcmV?d00001 diff --git a/lib/asn1/test/asn1_SUITE_data/MEDIA-GATEWAY-CONTROL.asn1config b/lib/asn1/test/asn1_SUITE_data/MEDIA-GATEWAY-CONTROL.asn1config index b7dba3c95c..cff991dbc3 100644 --- a/lib/asn1/test/asn1_SUITE_data/MEDIA-GATEWAY-CONTROL.asn1config +++ b/lib/asn1/test/asn1_SUITE_data/MEDIA-GATEWAY-CONTROL.asn1config @@ -1,7 +1,9 @@ +%% -*- erlang -*- {exclusive_decode, - {'MEDIA-GATEWAY-CONTROL', - [{decode_MegacoMessage_exclusive,['MegacoMessage',[{authHeader,undecoded},{mess,[{mId,undecoded},{messageBody,undecoded}]}]]}, - {decode_Message_version,['Message',[{mId,undecoded},{messageBody,undecoded}]]}]}}. + {'MEDIA-GATEWAY-CONTROL', + [{decode_MegacoMessage_exclusive, + ['MegacoMessage',[{authHeader,undecoded},{mess,[{mId,undecoded},{messageBody,undecoded}]}]]}, + {decode_Message_version,['Message',[{mId,undecoded},{messageBody,undecoded}]]}]}}. {selective_decode, - {'MEDIA-GATEWAY-CONTROL', - [{decode_MegacoMessage_selective,['MegacoMessage',mess,version]}]}}. + {'MEDIA-GATEWAY-CONTROL', + [{decode_MegacoMessage_selective,['MegacoMessage',mess,version]}]}}. diff --git a/lib/asn1/test/asn1_SUITE_data/OCSP-2013-88.asn1 b/lib/asn1/test/asn1_SUITE_data/OCSP-2013-88.asn1 new file mode 100644 index 0000000000..32b1eed962 --- /dev/null +++ b/lib/asn1/test/asn1_SUITE_data/OCSP-2013-88.asn1 @@ -0,0 +1,149 @@ +-- OCSP definition from RFC6960, 1998 Syntax + +OCSP-2013-88 { + iso(1) identified-organization(3) dod(6) internet(1) + security(5) mechanisms(5) pkix(7) id-mod(0) + id-mod-ocsp-2013-88(81) +} + +DEFINITIONS EXPLICIT TAGS ::= + +BEGIN + +IMPORTS + + -- PKIX Certificate Extensions + AuthorityInfoAccessSyntax, CRLReason, GeneralName + FROM PKIX1Implicit88 { iso(1) identified-organization(3) + dod(6) internet(1) security(5) mechanisms(5) pkix(7) + id-mod(0) id-pkix1-implicit(19) } + + Name, CertificateSerialNumber, Extensions, + id-kp, id-ad-ocsp, Certificate, AlgorithmIdentifier + FROM PKIX1Explicit88 { iso(1) identified-organization(3) + dod(6) internet(1) security(5) mechanisms(5) pkix(7) + id-mod(0) id-pkix1-explicit(18) }; + +OCSPRequest ::= SEQUENCE { + tbsRequest TBSRequest, + optionalSignature [0] EXPLICIT Signature OPTIONAL } + +TBSRequest ::= SEQUENCE { + version [0] EXPLICIT Version DEFAULT v1, + requestorName [1] EXPLICIT GeneralName OPTIONAL, + requestList SEQUENCE OF Request, + requestExtensions [2] EXPLICIT Extensions OPTIONAL } + +Signature ::= SEQUENCE { + signatureAlgorithm AlgorithmIdentifier, + signature BIT STRING, + certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL } + +Version ::= INTEGER { v1(0) } + +Request ::= SEQUENCE { + reqCert CertID, + singleRequestExtensions [0] EXPLICIT Extensions OPTIONAL } + +CertID ::= SEQUENCE { + hashAlgorithm AlgorithmIdentifier, + issuerNameHash OCTET STRING, -- Hash of issuer's DN + issuerKeyHash OCTET STRING, -- Hash of issuer's public key + serialNumber CertificateSerialNumber } + +OCSPResponse ::= SEQUENCE { + responseStatus OCSPResponseStatus, + responseBytes [0] EXPLICIT ResponseBytes OPTIONAL } + +OCSPResponseStatus ::= ENUMERATED { + successful (0), -- Response has valid confirmations + malformedRequest (1), -- Illegal confirmation request + internalError (2), -- Internal error in issuer + tryLater (3), -- Try again later + -- (4) is not used + sigRequired (5), -- Must sign the request + unauthorized (6) -- Request unauthorized +} + +ResponseBytes ::= SEQUENCE { + responseType OBJECT IDENTIFIER, + response OCTET STRING } + +BasicOCSPResponse ::= SEQUENCE { + tbsResponseData ResponseData, + signatureAlgorithm AlgorithmIdentifier, + signature BIT STRING, + certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL } + +ResponseData ::= SEQUENCE { + version [0] EXPLICIT Version DEFAULT v1, + responderID ResponderID, + producedAt GeneralizedTime, + responses SEQUENCE OF SingleResponse, + responseExtensions [1] EXPLICIT Extensions OPTIONAL } + +ResponderID ::= CHOICE { + byName [1] Name, + byKey [2] KeyHash } + +KeyHash ::= OCTET STRING -- SHA-1 hash of responder's public key + -- (i.e., the SHA-1 hash of the value of the + -- BIT STRING subjectPublicKey [excluding + -- the tag, length, and number of unused + -- bits] in the responder's certificate) + +SingleResponse ::= SEQUENCE { + certID CertID, + certStatus CertStatus, + thisUpdate GeneralizedTime, + nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL, + singleExtensions [1] EXPLICIT Extensions OPTIONAL } + +CertStatus ::= CHOICE { + good [0] IMPLICIT NULL, + revoked [1] IMPLICIT RevokedInfo, + unknown [2] IMPLICIT UnknownInfo } + +RevokedInfo ::= SEQUENCE { + revocationTime GeneralizedTime, + revocationReason [0] EXPLICIT CRLReason OPTIONAL } + +UnknownInfo ::= NULL + +ArchiveCutoff ::= GeneralizedTime + +AcceptableResponses ::= SEQUENCE OF OBJECT IDENTIFIER + +ServiceLocator ::= SEQUENCE { + issuer Name, + locator AuthorityInfoAccessSyntax } + +CrlID ::= SEQUENCE { + crlUrl [0] EXPLICIT IA5String OPTIONAL, + crlNum [1] EXPLICIT INTEGER OPTIONAL, + crlTime [2] EXPLICIT GeneralizedTime OPTIONAL } + +PreferredSignatureAlgorithms ::= SEQUENCE OF PreferredSignatureAlgorithm + +PreferredSignatureAlgorithm ::= SEQUENCE { + sigIdentifier AlgorithmIdentifier, + certIdentifier AlgorithmIdentifier OPTIONAL } + +Nonce ::= OCTET STRING + +-- Object Identifiers + +-- Already defined in PKIX1Implicit88 +--id-kp-OCSPSigning OBJECT IDENTIFIER ::= { id-kp 9 } +id-pkix-ocsp OBJECT IDENTIFIER ::= { id-ad-ocsp } +id-pkix-ocsp-basic OBJECT IDENTIFIER ::= { id-pkix-ocsp 1 } +id-pkix-ocsp-nonce OBJECT IDENTIFIER ::= { id-pkix-ocsp 2 } +id-pkix-ocsp-crl OBJECT IDENTIFIER ::= { id-pkix-ocsp 3 } +id-pkix-ocsp-response OBJECT IDENTIFIER ::= { id-pkix-ocsp 4 } +id-pkix-ocsp-nocheck OBJECT IDENTIFIER ::= { id-pkix-ocsp 5 } +id-pkix-ocsp-archive-cutoff OBJECT IDENTIFIER ::= { id-pkix-ocsp 6 } +id-pkix-ocsp-service-locator OBJECT IDENTIFIER ::= { id-pkix-ocsp 7 } +id-pkix-ocsp-pref-sig-algs OBJECT IDENTIFIER ::= { id-pkix-ocsp 8 } +id-pkix-ocsp-extended-revoke OBJECT IDENTIFIER ::= { id-pkix-ocsp 9 } + +END diff --git a/lib/asn1/test/asn1_SUITE_data/OCSP-2013-88.asn1config b/lib/asn1/test/asn1_SUITE_data/OCSP-2013-88.asn1config new file mode 100644 index 0000000000..45aadf179d --- /dev/null +++ b/lib/asn1/test/asn1_SUITE_data/OCSP-2013-88.asn1config @@ -0,0 +1,23 @@ +%% -*- erlang -*- +{exclusive_decode, + {'OCSP-2013-88', + [ + {decode_version_undec, + ['BasicOCSPResponse',[{tbsResponseData,[{version,undecoded}]}]]}, + {decode_responderID_undec, + ['BasicOCSPResponse',[{tbsResponseData,[{responderID,undecoded}]}]]}, + {decode_producedAt_undec, + ['BasicOCSPResponse',[{tbsResponseData,[{producedAt,undecoded}]}]]}, + {decode_responses_undec, + ['BasicOCSPResponse',[{tbsResponseData,[{responses,undecoded}]}]]}, + {decode_responses_parts, + ['BasicOCSPResponse',[{tbsResponseData,[{responses,parts}]}]]}, + {decode_tbsResponseData_undec, + ['BasicOCSPResponse',[{tbsResponseData,undecoded}]]}, + {decode_BasicOCSPResponse_signature_undec, + ['BasicOCSPResponse',[{signature,undecoded}]]}, + {decode_BasicOCSPResponse_certs_undec, + ['BasicOCSPResponse',[{certs,undecoded}]]}, + {decode_BasicOCSPResponse_certs_parts, + ['BasicOCSPResponse',[{certs,parts}]]} + ]}}. diff --git a/lib/asn1/test/asn1_SUITE_data/PartialDecSeq.asn1config b/lib/asn1/test/asn1_SUITE_data/PartialDecSeq.asn1config index 0d91e0c3b3..bf388c0754 100644 --- a/lib/asn1/test/asn1_SUITE_data/PartialDecSeq.asn1config +++ b/lib/asn1/test/asn1_SUITE_data/PartialDecSeq.asn1config @@ -1,14 +1,19 @@ -{selective_decode,{'PartialDecSeq', - [{selected_decode_F1,['F',fb,b,[1],a]}, - {selected_decode_F2,['F',fb,b]}, - {selected_decode_F3,['F',fb,b,[1]]}, - {selected_decode_F4,['F',fb,d,da,[1],b,a]}, - {selected_decode_E1,['E',d,dc,dcc,dcca]}]}}. -{exclusive_decode,{'PartialDecSeq', - [{decode_F_fb_incomplete,['F',[{fb,[{b,parts},{d,undecoded}]}]]}, - {decode_D_incomplete,['D',[{a,undecoded}]]}, - {decode_F_fb_exclusive2,['F',[{fb,[{b,parts},{d,[{da,parts}]}]}]]}, {decode_F_fb_exclusive3,['F',[{fb,[{b,parts},{d,[{da,parts},{dc,[{dcc,undecoded}]}]}]}]]}]}}. -{module_name,'Seq.asn1'}. -{compile_options,[ber,debug_info]}. -{multifile_compile,['M1.asn','M2.asn']}. - +%% -*- erlang -*- +{selective_decode, + {'PartialDecSeq', + [{selected_decode_F1_1,['F',fb,b,[1],a]}, + {selected_decode_F1_2,['F',fb,b,[2],a]}, + {selected_decode_F2,['F',fb,b]}, + {selected_decode_F3,['F',fb,b,[1]]}, + {selected_decode_F4,['F',fb,d,da,[1],b,a]}, + {selected_decode_F5,['F',fb]}, + {selected_decode_E1,['E',d,dc,dcc,dcca]}, + {selected_decode_E2,['E',b]} + ]}}. +{exclusive_decode, + {'PartialDecSeq', + [{decode_E_b_incomplete,['E',[{b,parts}]]}, + {decode_F_fb_incomplete,['F',[{fb,[{b,parts},{d,undecoded}]}]]}, + {decode_D_incomplete,['D',[{a,undecoded}]]}, + {decode_F_fb_exclusive2,['F',[{fb,[{b,parts},{d,[{da,parts}]}]}]]}, + {decode_F_fb_exclusive3,['F',[{fb,[{b,parts},{d,[{da,parts},{dc,[{dcc,undecoded}]}]}]}]]}]}}. diff --git a/lib/asn1/test/asn1_SUITE_data/PartialDecSeq2.asn b/lib/asn1/test/asn1_SUITE_data/PartialDecSeq2.asn index 2e77d250d2..33a88048b1 100644 --- a/lib/asn1/test/asn1_SUITE_data/PartialDecSeq2.asn +++ b/lib/asn1/test/asn1_SUITE_data/PartialDecSeq2.asn @@ -8,6 +8,13 @@ B ::= CHOICE { c S } +Bext ::= CHOICE { + a INTEGER, + b SEQUENCE {aa INTEGER, ba INTEGER}, + ..., + c S +} + S ::= SEQUENCE { a BOOLEAN, b BOOLEAN @@ -26,4 +33,14 @@ D ::= SEQUENCE { b BOOLEAN } +SeqChoice ::= SEQUENCE { + c CHOICE { + b BOOLEAN, + i INTEGER, + s VisibleString + }, + d OCTET STRING +} + + END diff --git a/lib/asn1/test/asn1_SUITE_data/PartialDecSeq2.asn1config b/lib/asn1/test/asn1_SUITE_data/PartialDecSeq2.asn1config index d9967d9958..776c106ab4 100644 --- a/lib/asn1/test/asn1_SUITE_data/PartialDecSeq2.asn1config +++ b/lib/asn1/test/asn1_SUITE_data/PartialDecSeq2.asn1config @@ -1,4 +1,15 @@ -%{partial_decode,{{module,'Seq2'},['F',fb,b,[1],a]}}. -{exclusive_decode,{'PartialDecSeq2', - [{decode_A_c_b_incomplete,['A',[{c,[{a,undecoded},{b,undecoded}]}]]}, - {decode_D_incomplete,['D',[{a,undecoded}]]}]}}. +%% -*- erlang -*- +{exclusive_decode, + {'PartialDecSeq2', + [{decode_A_a_incomplete,['A',[{a,undecoded}]]}, + {decode_A_c_b_incomplete,['A',[{c,[{a,undecoded},{b,undecoded}]}]]}, + {decode_D_incomplete,['D',[{a,undecoded}]]}, + {decode_Bext_c_incomplete,['Bext',[{c,undecoded}]]}, + {decode_Bext_c_b_incomplete,['Bext',[{c,[{b,undecoded}]}]]}, + {decode_SeqChoice_c_b_d_incomplete, + ['SeqChoice',[{c,[{b,undecoded}]}, + {d,undecoded}]]}, + {decode_SeqChoice_c_bis_incomplete, + ['SeqChoice', + [{c,[{b,undecoded},{i,undecoded},{s,undecoded}]}]]} + ]}}. diff --git a/lib/asn1/test/asn1_SUITE_data/PartialDecSeq3.asn1config b/lib/asn1/test/asn1_SUITE_data/PartialDecSeq3.asn1config index 44d22aa1d4..72423d682d 100644 --- a/lib/asn1/test/asn1_SUITE_data/PartialDecSeq3.asn1config +++ b/lib/asn1/test/asn1_SUITE_data/PartialDecSeq3.asn1config @@ -1,2 +1,7 @@ -{exclusive_decode,{'PartialDecSeq3', - [{decode_S1_incomplete,['S1',[{b,[{c,parts}]},{c,[{a,parts}]},{d,parts}]]}]}}. +%% -*- erlang -*- +{exclusive_decode, + {'PartialDecSeq3', + [{decode_S1_incomplete,['S1',[{b,[{c,parts}]},{c,[{a,parts}]},{d,parts}]]}, + {decode_S1_b_incomplete,['S1',[{b,undecoded}]]}, + {decode_S3_second,['S3',[{second,undecoded}]]} + ]}}. diff --git a/lib/asn1/test/asn1_SUITE_data/TCAPPackage.asn1config b/lib/asn1/test/asn1_SUITE_data/TCAPPackage.asn1config index b0ccd7d34c..74c95105d1 100644 --- a/lib/asn1/test/asn1_SUITE_data/TCAPPackage.asn1config +++ b/lib/asn1/test/asn1_SUITE_data/TCAPPackage.asn1config @@ -1,12 +1,15 @@ -{exclusive_decode, {'TCAPPackage', - [{decode_PackageType, ['PackageType', - [{unidirectional, undecoded}, - {queryWithPerm, [{componentPortion, parts}]}, - {queryWithoutPerm, undecoded}, - {response, [{componentPortion, parts}]}, - {conversationWithPerm, undecoded}, - {conversationWithoutPerm, undecoded}, - {abort, undecoded}]]}, - {decode_TransactionPDU, ['TransactionPDU', - [{componentPortion, parts}]]}]}}. - +%% -*- erlang -*- +{exclusive_decode, + {'TCAPPackage', + [{decode_PackageType, + ['PackageType', + [{unidirectional, undecoded}, + {queryWithPerm, [{componentPortion, parts}]}, + {queryWithoutPerm, undecoded}, + {response, [{componentPortion, parts}]}, + {conversationWithPerm, undecoded}, + {conversationWithoutPerm, undecoded}, + {abort, undecoded}]]}, + {decode_TransactionPDU, + ['TransactionPDU', + [{componentPortion, parts}]]}]}}. diff --git a/lib/asn1/test/asn1_app_SUITE.erl b/lib/asn1/test/asn1_app_SUITE.erl index 12b0b509eb..1cbac5a62c 100644 --- a/lib/asn1/test/asn1_app_SUITE.erl +++ b/lib/asn1/test/asn1_app_SUITE.erl @@ -133,7 +133,8 @@ check_asn1ct_modules(Extra) -> asn1ct_gen_ber_bin_v2,asn1ct_value, asn1ct_tok,asn1ct_parser2,asn1ct_table, asn1ct_imm,asn1ct_func,asn1ct_rtt, - asn1ct_eval_ext,asn1ct_gen_jer], + asn1ct_eval_ext,asn1ct_gen_jer, + asn1ct_partial_decode], case Extra -- ASN1CTMods of [] -> ok; diff --git a/lib/asn1/test/error_SUITE.erl b/lib/asn1/test/error_SUITE.erl index aa2b36bbd7..eeb83deb08 100644 --- a/lib/asn1/test/error_SUITE.erl +++ b/lib/asn1/test/error_SUITE.erl @@ -20,7 +20,9 @@ -module(error_SUITE). -export([suite/0,all/0,groups/0, - already_defined/1,bitstrings/1, + already_defined/1, + bad_config_exclusive/1,bad_config_selective/1, + bitstrings/1, classes/1,constraints/1,constructed/1,enumerated/1, imports_exports/1,instance_of/1,integers/1,objects/1, object_field_extraction/1,oids/1,rel_oids/1, @@ -38,6 +40,8 @@ groups() -> [{p,parallel(), [already_defined, bitstrings, + bad_config_exclusive, + bad_config_selective, classes, constraints, constructed, @@ -91,6 +95,99 @@ already_defined(Config) -> } = run(P, Config), ok. +bad_config_exclusive(Config) -> + M = 'BadConfigExclusive', + P = {M, + <<"BadConfigExclusive DEFINITIONS AUTOMATIC TAGS ::= BEGIN + Seq ::= SEQUENCE { + a INTEGER, + b SEQUENCE OF INTEGER, + c BOOLEAN + } + END\n">>}, + + Conf1 = [{exclusive_decode,{'WrongModuleName',[whatever]}}], + {error,{bad_module_name,'WrongModuleName',M}} = run(P, Config, Conf1), + + Conf2 = [{exclusive_decode,wrong}], + {error,{bad_exclusive_decode,wrong}} = run(P, Config, Conf2), + + Conf3 = [{exclusive_decode,{M,[wrong]}}], + {error,{bad_exclusive_decode,[wrong]}} = run(P, Config, Conf3), + + Conf4 = [{exclusive_decode,{M,{42,[top_type,[]]}}}], + {error,{bad_exclusive_decode,_}} = run(P, Config, Conf4), + + Conf5 = [{exclusive_decode, + {M, [{exclusive_Seq, ['TopType', [{b,parts}]]}]}}], + {error,{undefined_type,'TopType'}} = run(P, Config, Conf5), + + Conf6 = [{exclusive_decode, + {M, [{exclusive_Seq, ['Seq', [{d,parts}]]}]}}], + {error,{undefined_name,d}} = run(P, Config, Conf6), + + Conf7 = [{exclusive_decode, + {M, [{exclusive_Seq, ['Seq', [{b,whatever}]]}]}}], + {error,{bad_decode_instruction,{b,whatever}}} = run(P, Config, Conf7), + + Conf8 = [{exclusive_decode, + {M, [{exclusive_Seq, ['Seq', [{a,b,c}]]}]}}], + {error,{bad_decode_instruction,{a,b,c}}} = run(P, Config, Conf8), + + ok. + +bad_config_selective(Config) -> + M = 'BadConfigSelective', + P = {M, + <<"BadConfigSelective DEFINITIONS AUTOMATIC TAGS ::= BEGIN + Seq ::= SEQUENCE { + a INTEGER, + b SEQUENCE OF INTEGER, + c BOOLEAN, + cc CHOICE { + ca INTEGER, + cb BOOLEAN + } + } + END\n">>}, + + Conf1 = [{selective_decode,{'WrongModuleName',[whatever]}}], + {error,{bad_module_name,'WrongModuleName',M}} = run(P, Config, Conf1), + + Conf2 = [{selective_decode,wrong}], + {error,{bad_selective_decode,wrong}} = run(P, Config, Conf2), + + Conf3 = [{selective_decode,{M,[wrong]}}], + {error,{bad_selective_decode,wrong}} = run(P, Config, Conf3), + + Conf4 = [{selective_decode,{M,[{f,not_list}]}}], + {error,{bad_selective_decode_type_list,not_list}} = run(P, Config, Conf4), + + Conf5 = [{selective_decode,{M,{42,[top_type,[]]}}}], + {error,{bad_selective_decode,{42,_}}} = run(P, Config, Conf5), + + Conf6 = [{selective_decode, + {M, [{only_Seq_b, ['TopType', [{b,parts}]]}]}}], + {error,{undefined_type,'TopType'}} = run(P, Config, Conf6), + + Conf7 = [{selective_decode, + {M, [{only_Seq_b, ['Seq', d]}]}}], + {error,{undefined_name,d}} = run(P, Config, Conf7), + + Conf8 = [{selective_decode, + {M, [{only_Seq_b, ['Seq', b, whatever]}]}}], + {error,{bad_selective_decode_element,whatever}} = run(P, Config, Conf8), + + Conf9 = [{selective_decode, + {M, [{only_Seq_something, ['Seq', cc, whatever]}]}}], + {error,{undefined_name,whatever}} = run(P, Config, Conf9), + + Conf10 = [{selective_decode, + {M, [{only_Seq_something, ['Seq', a, x]}]}}], + {error,{stepping_into_primitive,[a,x]}} = run(P, Config, Conf10), + + ok. + bitstrings(Config) -> M = 'Bitstrings', P = {M, @@ -928,7 +1025,6 @@ values(Config) -> } = run(P, Config), ok. - run({Mod,Spec}, Config) -> Base = atom_to_list(Mod) ++ ".asn1", File = filename:join(proplists:get_value(priv_dir, Config), Base), @@ -936,3 +1032,22 @@ run({Mod,Spec}, Config) -> Include = filename:join(filename:dirname(Include0), "asn1_SUITE_data"), ok = file:write_file(File, Spec), asn1ct:compile(File, [{i, Include}]). + +run({Mod,Spec}, Config, Asn1ConfigTerm) -> + PrivDir = proplists:get_value(priv_dir, Config), + + Asn1ConfigFile = filename:join(PrivDir, atom_to_list(Mod) ++ ".asn1config"), + Asn1ConfigData = [io_lib:format("~p. \n", [Term]) || Term <- Asn1ConfigTerm], + ok = file:write_file(Asn1ConfigFile, Asn1ConfigData), + + Base = atom_to_list(Mod) ++ ".asn1", + File = filename:join(PrivDir, Base), + ok = file:write_file(File, Spec), + + case asn1ct:compile(File, [{i, PrivDir}, asn1config]) of + {error,[{structured_error,{Asn1ConfigFile,none}, + asn1ct_partial_decode,Error}]} -> + {error,Error}; + Other -> + Other + end. diff --git a/lib/asn1/test/test_partial_incomplete_decode.erl b/lib/asn1/test/test_partial_incomplete_decode.erl index 7c5cfab10a..f4c46f3cb0 100644 --- a/lib/asn1/test/test_partial_incomplete_decode.erl +++ b/lib/asn1/test/test_partial_incomplete_decode.erl @@ -25,49 +25,89 @@ -include_lib("common_test/include/ct.hrl"). test(Config) -> + DataDir = proplists:get_value(data_dir, Config), + test_PartialDecSeq(), + test_PartialDecSeq2(), + test_PartialDecSeq3(), + test_MyHTTPMsg(), + test_megaco(DataDir), + test_OCSP(DataDir), + ok. + +test_PartialDecSeq() -> + M = 'PartialDecSeq', + FMsg = msg('F'), - Bytes1 = roundtrip('PartialDecSeq', 'F', FMsg), - {ok,IncFMsg} = 'PartialDecSeq':decode_F_fb_incomplete(Bytes1), - decode_parts('F', IncFMsg), - {ok,IncF2Msg} = 'PartialDecSeq':decode_F_fb_exclusive2(Bytes1), - decode_parts('F2', IncF2Msg), - + test_exclusive(fun M:decode_F_fb_incomplete/1, 'F', FMsg), + DMsg = msg('D'), - Bytes2 = roundtrip('PartialDecSeq', 'D', DMsg), - {ok,IncDMsg} = 'PartialDecSeq':decode_D_incomplete(Bytes2), - decode_parts('D', IncDMsg), + test_exclusive(fun M:decode_D_incomplete/1, 'D', DMsg), F3Msg = msg('F3'), - BytesF3 = roundtrip('PartialDecSeq', 'F', F3Msg), - {ok,IncF3Msg} = 'PartialDecSeq':decode_F_fb_exclusive3(BytesF3), - decode_parts('F3', IncF3Msg), - - AMsg = msg('A'), - Bytes3 = roundtrip('PartialDecSeq2', 'A', AMsg), - {ok,IncFMsg3} = 'PartialDecSeq2':decode_A_c_b_incomplete(Bytes3), - decode_parts('A', IncFMsg3), - - MyHTTPMsg = msg('GetRequest'), - Bytes4 = roundtrip('PartialDecMyHTTP', 'GetRequest', MyHTTPMsg), - {ok,IncFMsg4} = 'PartialDecMyHTTP':decode_GetRequest_incomplete(Bytes4), - decode_parts('GetRequest', IncFMsg4), - + test_exclusive(fun M:decode_F_fb_exclusive3/1, 'F', F3Msg), + + EMsg = msg('E'), + test_exclusive(fun M:decode_E_b_incomplete/1, 'E', EMsg), + + ok. + +test_PartialDecSeq2() -> + M = 'PartialDecSeq2', + + %% Test DEFAULT value. + AMsg1 = msg('A_1'), + AMsg1Encoded = roundtrip(M, 'A', AMsg1), + {ok,AMsg1} = M:decode_A_a_incomplete(AMsg1Encoded), + + AMsg2 = msg('A_2'), + test_exclusive(fun M:decode_A_a_incomplete/1, 'A', AMsg2), + test_exclusive(fun M:decode_A_c_b_incomplete/1, 'A', AMsg2), + + SMsg = {'S',true,false}, + BextMsg = {c,SMsg}, + + test_exclusive(fun M:decode_Bext_c_incomplete/1, 'Bext', BextMsg), + test_exclusive(fun M:decode_Bext_c_b_incomplete/1, 'Bext', BextMsg), + + T = 'SeqChoice', + + SeqChoiceMsg1 = {'SeqChoice',{b,true},<<"abc">>}, + test_exclusive(fun M:decode_SeqChoice_c_b_d_incomplete/1, T, SeqChoiceMsg1), + + test_exclusive(fun M:decode_SeqChoice_c_bis_incomplete/1, T, SeqChoiceMsg1), + + SeqChoiceMsg2 = {'SeqChoice',{i,42},<<"cde">>}, + test_exclusive(fun M:decode_SeqChoice_c_bis_incomplete/1, T, SeqChoiceMsg2), + + SeqChoiceMsg3 = {'SeqChoice',{s,"xyz"},<<"fgh">>}, + test_exclusive(fun M:decode_SeqChoice_c_bis_incomplete/1, T, SeqChoiceMsg3), + + ok. + +test_PartialDecSeq3() -> + M = 'PartialDecSeq3', + MsgS1_1 = msg('S1_1'), - Bytes5 = roundtrip('PartialDecSeq3', 'S1', MsgS1_1), - {ok,IncFMsg5} = 'PartialDecSeq3':decode_S1_incomplete(Bytes5), - decode_parts('S1_1', IncFMsg5), + test_exclusive(fun M:decode_S1_incomplete/1, 'S1', MsgS1_1), + test_exclusive(fun M:decode_S1_b_incomplete/1, 'S1', MsgS1_1), MsgS1_2 = msg('S1_2'), - Bytes6 = roundtrip('PartialDecSeq3', 'S1', MsgS1_2), - {ok,IncFMsg6} = 'PartialDecSeq3':decode_S1_incomplete(Bytes6), - decode_parts('S1_2', IncFMsg6), + test_exclusive(fun M:decode_S1_incomplete/1, 'S1', MsgS1_2), + + MsgS3 = msg('S3'), + test_exclusive(fun M:decode_S3_second/1, 'S3', MsgS3), - %% test of MEDIA-GATEWAY-CONTROL - test_megaco(Config), ok. -test_megaco(Config) -> - DataDir = proplists:get_value(data_dir, Config), +test_MyHTTPMsg() -> + MyHTTPMsg = msg('GetRequest'), + Bytes1 = roundtrip('PartialDecMyHTTP', 'GetRequest', MyHTTPMsg), + {ok,IncFMsg4} = 'PartialDecMyHTTP':decode_GetRequest_incomplete(Bytes1), + decode_parts('GetRequest', IncFMsg4), + + ok. + +test_megaco(DataDir) -> Files = filelib:wildcard(filename:join([DataDir,megacomessages,"*.val"])), Mod = 'MEDIA-GATEWAY-CONTROL', lists:foreach(fun(File) -> @@ -87,34 +127,67 @@ exclusive_decode(Bin,F) -> {ok,_} = Mod:decode_part(MsgMBodyKey,MsgMBody), ok. -decode_parts('F',PartDecMsg) -> - {fb,{'E',35,{NameE_b,ListBinE_b},false,{NameE_d,BinE_d}}} = PartDecMsg, - {ok,[{'D',3,true}|_]} = 'PartialDecSeq':decode_part(NameE_b,ListBinE_b), - {ok,{'D',3,true}} = 'PartialDecSeq':decode_part(NameE_b, - hd(ListBinE_b)), - {ok,{da,[{'A',16,{'D',17,true}}]}} = - 'PartialDecSeq':decode_part(NameE_d,BinE_d), - ok; -decode_parts('F2',PartDecMsg) -> - {fb,{'E',35,{E_bkey,E_b},false,{da,{E_d_akey,E_d_a}}}} = PartDecMsg, - {ok,[{'D',3,true},{'D',4,false},{'D',5,true},{'D',6,true},{'D',7,false},{'D',8,true},{'D',9,true},{'D',10,false},{'D',11,true},{'D',12,true},{'D',13,false},{'D',14,true}]} = 'PartialDecSeq':decode_part(E_bkey,E_b), - {ok,[{'A',16,{'D',17,true}}]} = 'PartialDecSeq':decode_part(E_d_akey,E_d_a); +test_OCSP(DataDir) -> + Mod = 'OCSP-2013-88', + + ResponseData = {'ResponseData', + v1, %Version + {byKey,<<"key hash">>}, + "factory", + [], + asn1_NOVALUE}, + + Type = 'BasicOCSPResponse', + + BasicMsg = {Type, + ResponseData, + {'AlgorithmIdentifier',Mod:'id-pkix-ocsp-basic'(),asn1_NOVALUE}, + <<"signature">>, + []}, + + test_exclusive(fun Mod:decode_version_undec/1, Type, BasicMsg), + test_exclusive(fun Mod:decode_responderID_undec/1, Type, BasicMsg), + test_exclusive(fun Mod:decode_producedAt_undec/1, Type, BasicMsg), + test_exclusive(fun Mod:decode_responses_undec/1, Type, BasicMsg), + test_exclusive(fun Mod:decode_responses_parts/1, Type, BasicMsg), + test_exclusive(fun Mod:decode_tbsResponseData_undec/1, Type, BasicMsg), + test_exclusive(fun Mod:decode_BasicOCSPResponse_signature_undec/1, Type, BasicMsg), + test_exclusive(fun Mod:decode_BasicOCSPResponse_certs_undec/1, Type, BasicMsg), + test_exclusive(fun Mod:decode_BasicOCSPResponse_certs_parts/1, Type, BasicMsg), + + %% Test undecoded/parts for an absent element. + MsgWithoutCerts = + {Type, + ResponseData, + {'AlgorithmIdentifier',Mod:'id-pkix-ocsp-basic'(),asn1_NOVALUE}, + <<"signature">>, + asn1_NOVALUE}, + {ok,Enc} = Mod:encode(Type, MsgWithoutCerts), + {ok,MsgWithoutCerts} = Mod:decode_BasicOCSPResponse_certs_undec(Enc), + {ok,MsgWithoutCerts} = Mod:decode_BasicOCSPResponse_certs_parts(Enc), + + DataFileName = filename:join(DataDir, "BasicOCSPResponse.ber"), + {ok,CannedData} = file:read_file(DataFileName), + {ok,HugeMsg} = Mod:decode('BasicOCSPResponse', CannedData), + + %% Decode version with a default value. + {ok,HugeMsg} = Mod:decode_version_undec(CannedData), + + test_exclusive(fun Mod:decode_responderID_undec/1, Type, HugeMsg), + test_exclusive(fun Mod:decode_producedAt_undec/1, Type, HugeMsg), + test_exclusive(fun Mod:decode_responses_undec/1, Type, HugeMsg), + test_exclusive(fun Mod:decode_responses_parts/1, Type, HugeMsg), + test_exclusive(fun Mod:decode_tbsResponseData_undec/1, Type, HugeMsg), + test_exclusive(fun Mod:decode_BasicOCSPResponse_signature_undec/1, Type, HugeMsg), + test_exclusive(fun Mod:decode_BasicOCSPResponse_certs_undec/1, Type, HugeMsg), + test_exclusive(fun Mod:decode_BasicOCSPResponse_certs_parts/1, Type, HugeMsg), + + ok. decode_parts('F3',PartDecMsg) -> {fb,{'E',10,{E_bkey,E_b},false,{dc,{'E_d_dc',13,true,{E_d_dc_dcckey,E_d_dc_dcc}}}}} = PartDecMsg, {ok,[{'D',11,true},{'D',12,false}]} = 'PartialDecSeq':decode_part(E_bkey,E_b), {ok,{'E_d_dc_dcc',14,15}} = 'PartialDecSeq':decode_part(E_d_dc_dcckey,E_d_dc_dcc); - - -decode_parts('D',PartDecMsg) -> - {'D',{NameD_a,BinD_a},true} = PartDecMsg, - {ok,123} = 'PartialDecSeq':decode_part(NameD_a,BinD_a), - ok; -decode_parts('A',PartDecMsg) -> - {'A',12,{c,{'S',true,false}},{b,{NameA_c_b,BinA_c_b}}} = PartDecMsg, - {ok,{'A_c_b',false,false}} = - 'PartialDecSeq2':decode_part(NameA_c_b,BinA_c_b), - ok; decode_parts('GetRequest',PartDecMsg) -> {'GetRequest',true,false, {'AcceptTypes',[html,'plain-text',gif,jpeg], @@ -126,34 +199,17 @@ decode_parts('GetRequest',PartDecMsg) -> {ok,"hell"} = 'PartialDecMyHTTP':decode_part(NameAcceptTypes_others, hd(ListBinAcceptTypes_others)), - ok; -decode_parts('S1_1',PartDecMsg) -> - {'S1',14,{'S2',false,12,{NameS2c,BinS2c}}, - {_,{NameS1c_a,ListBinS1c_a}},{NameS1d,BinS1d}} = PartDecMsg, - {ok,[{'S3',10,"PrintableString","OCTETSTRING", - [one,two,three,four]}|_Rest1]} = - 'PartialDecSeq3':decode_part(NameS2c,BinS2c), - {ok,[{'S3',10,"PrintableString","OCTETSTRING", - [one,two,three,four]}|_Rest2]} = - 'PartialDecSeq3':decode_part(NameS1c_a,ListBinS1c_a), - {ok,{'S3',10,"PrintableString","OCTETSTRING", - [one,two,three,four]}} = - 'PartialDecSeq3':decode_part(NameS1c_a,hd(ListBinS1c_a)), - {ok,[{'Name',"Hans","HCA","Andersen"}|_Rest3]} = - 'PartialDecSeq3':decode_part(NameS1d,BinS1d), - ok; -decode_parts('S1_2',PartDecMsg) -> - {'S1',14,{'S2',false,12,_S2c},S1c_b,{NameS1d,BinS1d}} = PartDecMsg, - {b,{'C1_b',11,true, - {'S4',{'Name',"Hans","HCA","Andersen"},"MSc"}}}=S1c_b, - {ok,[{'Name',"Hans","HCA","Andersen"}|_Rest3]} = - 'PartialDecSeq3':decode_part(NameS1d,BinS1d), ok. - - +msg('E') -> + {'E',35,msg('D_many'),false,{da,[{'A',16,{'D',17,true}}]}}; + +msg('D_many') -> + [{'D',3,true},{'D',4,false},{'D',5,true},{'D',6,true},{'D',7,false},{'D',8,true},{'D',9,true}, + {'D',10,false},{'D',11,true},{'D',12,true},{'D',13,false},{'D',14,true}]; + msg('F') -> - {fb,{'E',35,[{'D',3,true},{'D',4,false},{'D',5,true},{'D',6,true},{'D',7,false},{'D',8,true},{'D',9,true},{'D',10,false},{'D',11,true},{'D',12,true},{'D',13,false},{'D',14,true}],false,{da,[{'A',16,{'D',17,true}}]}}}; + {fb,msg('E')}; msg('F3') -> {fb,{'E',10,[{'D',11,true},{'D',12,false}],false,{dc,{'E_d_dc',13,true,{'E_d_dc_dcc',14,15}}}}}; @@ -161,8 +217,10 @@ msg('F3') -> msg('D') -> {'D',123,true}; -msg('A') -> - {'A',12,{c,{'S',true,false}},{b,{'A_c_b',false,false}}}; +msg('A_1') -> + {'A',15,{c,{'S',true,false}},{b,{'A_c_b',false,false}}}; +msg('A_2') -> + {'A',42,{c,{'S',true,false}},{b,{'A_c_b',false,false}}}; msg('GetRequest') -> {'GetRequest',true,false, @@ -181,7 +239,7 @@ msg('C1_a') -> msg('C1_b') -> {b,{'C1_b',11,true,msg('S4')}}; msg('S3') -> - {'S3',10,"PrintableString","OCTETSTRING",[one,two,three,four]}; + {'S3',10,"PrintableString",<<"OCTETSTRING">>,[one,two,three,four]}; msg('S4') -> {'S4',msg('Name'),"MSc"}; msg('SO1') -> @@ -191,3 +249,51 @@ msg('Name') -> roundtrip(M, T, V) -> asn1_test_lib:roundtrip_enc(M, T, V). + +test_exclusive(DecodeFun, Type, Msg) -> + {module,Mod} = erlang:fun_info(DecodeFun, module), + Encoded = roundtrip(Mod, Type, Msg), + case DecodeFun(Encoded) of + {ok,Msg} -> + error({should_be_different,Msg}); + {ok,Decoded} -> + case dec_parts(Decoded, Msg, Mod) of + Msg -> + ok; + OtherMsg -> + io:format("Partial decoding: +~p + +Expected: +~p + +Got: +~p +", [Decoded,Msg,OtherMsg]), + error(full_and_partial_decode_differ) + end + end. + +dec_parts(Same, Same, _Mod) -> + Same; +dec_parts({Name,Parts}, Expected, Mod) when is_atom(Name), is_list(Parts), is_list(Expected) -> + [begin + {ok,Dec} = Mod:decode_part(Name, Bin), + Dec + end || Bin <- Parts]; +dec_parts({Name,Undec}, _Expected, Mod) when is_atom(Name), is_binary(Undec) -> + {ok,Dec} = Mod:decode_part(Name, Undec), + Dec; +dec_parts({Name,{Tag,_}=Undec}, _Expected, Mod) when is_atom(Name), is_integer(Tag) -> + {ok,Dec} = Mod:decode_part(Name, Undec), + Dec; +dec_parts(Tuple0, Expected, Mod) when is_tuple(Tuple0), is_tuple(Expected) -> + Tuple = dec_parts_list(tuple_to_list(Tuple0), tuple_to_list(Expected), Mod), + list_to_tuple(Tuple); +dec_parts(List, Expected, Mod) when is_list(List), is_list(Expected) -> + dec_parts_list(List, Expected, Mod). + +dec_parts_list([H1|T1], [H2|T2], Mod) -> + [dec_parts(H1, H2, Mod) | dec_parts_list(T1, T2, Mod)]; +dec_parts_list([], [], _Mod) -> + []. diff --git a/lib/asn1/test/test_selective_decode.erl b/lib/asn1/test/test_selective_decode.erl index 3220ccc2ed..5953292f0f 100644 --- a/lib/asn1/test/test_selective_decode.erl +++ b/lib/asn1/test/test_selective_decode.erl @@ -26,17 +26,22 @@ test() -> FMsg = msg('F'), Bytes = roundtrip('PartialDecSeq', 'F', FMsg), - {ok,3} = 'PartialDecSeq':selected_decode_F1(Bytes), + {ok,3} = 'PartialDecSeq':selected_decode_F1_1(Bytes), + {ok,4} = 'PartialDecSeq':selected_decode_F1_2(Bytes), {ok,[{'D',3,true},{'D',4,false},{'D',5,true},{'D',6,true}, {'D',7,false},{'D',8,true},{'D',9,true},{'D',10,false}, {'D',11,true},{'D',12,true},{'D',13,false},{'D',14,true}]} = 'PartialDecSeq':selected_decode_F2(Bytes), {ok,{'D',3,true}} = 'PartialDecSeq':selected_decode_F3(Bytes), {ok,17} = 'PartialDecSeq':selected_decode_F4(Bytes), - + E_from_F = element(2, FMsg), + {ok,E_from_F} = 'PartialDecSeq':selected_decode_F5(Bytes), + EMsg = msg('E'), Bytes2 = roundtrip('PartialDecSeq', 'E', EMsg), {ok,14} = 'PartialDecSeq':selected_decode_E1(Bytes2), + Emsg_b = msg('E_b'), + {ok,Emsg_b} = 'PartialDecSeq':selected_decode_E2(Bytes2), MGCMsg = msg('M-G-C'), Bytes3 = roundtrip('MEDIA-GATEWAY-CONTROL', 'MegacoMessage', MGCMsg), @@ -61,7 +66,10 @@ msg('F') -> {fb,{'E',35,[{'D',3,true},{'D',4,false},{'D',5,true},{'D',6,true},{'D',7,false},{'D',8,true},{'D',9,true},{'D',10,false},{'D',11,true},{'D',12,true},{'D',13,false},{'D',14,true}],false,{da,[{'A',16,{'D',17,true}}]}}}; msg('E') -> - {'E',10,[{'D',11,true},{'D',12,false}],false,{dc,{'E_d_dc',13,true,{'E_d_dc_dcc',14,15}}}}; + {'E',10,msg('E_b'),false,{dc,{'E_d_dc',13,true,{'E_d_dc_dcc',14,15}}}}; + +msg('E_b') -> + [{'D',11,true},{'D',12,false}]; msg('M-G-C') -> {'MegacoMessage',asn1_NOVALUE,{'Message',1,{ip4Address,{'IP4Address',[125,125,125,111],55555}},{transactions,[{transactionReply,{'TransactionReply',50007,asn1_NOVALUE,{actionReplies,[{'ActionReply',0,asn1_NOVALUE,asn1_NOVALUE,[{auditValueReply,{auditResult,{'AuditResult',{'TerminationID',[],[255,255,255]},[{mediaDescriptor,{'MediaDescriptor',asn1_NOVALUE,{multiStream,[{'StreamDescriptor',1,{'StreamParms',{'LocalControlDescriptor',sendRecv,asn1_NOVALUE,asn1_NOVALUE,[{'PropertyParm',[0,11,0,7],[[52,48]],asn1_NOVALUE}]},{'LocalRemoteDescriptor',[[{'PropertyParm',[0,0,176,1],[[48]],asn1_NOVALUE},{'PropertyParm',[0,0,176,8],[[73,78,32,73,80,52,32,49,50,53,46,49,50,53,46,49,50,53,46,49,49,49]],asn1_NOVALUE},{'PropertyParm',[0,0,176,15],[[97,117,100,105,111,32,49,49,49,49,32,82,84,80,47,65,86,80,32,32,52]],asn1_NOVALUE},{'PropertyParm',[0,0,176,12],[[112,116,105,109,101,58,51,48]],asn1_NOVALUE}]]},{'LocalRemoteDescriptor',[[{'PropertyParm',[0,0,176,1],[[48]],asn1_NOVALUE},{'PropertyParm',[0,0,176,8],[[73,78,32,73,80,52,32,49,50,52,46,49,50,52,46,49,50,52,46,50,50,50]],asn1_NOVALUE},{'PropertyParm',[0,0,176,15],[[97,117,100,105,111,32,50,50,50,50,32,82,84,80,47,65,86,80,32,32,52]],asn1_NOVALUE},{'PropertyParm',[0,0,176,12],[[112,116,105,109,101,58,51,48]],asn1_NOVALUE}]]}}}]}}},{packagesDescriptor,[{'PackagesItem',[0,11],1},{'PackagesItem',[0,11],1}]},{statisticsDescriptor,[{'StatisticsParameter',[0,12,0,4],[[49,50,48,48]]},{'StatisticsParameter',[0,11,0,2],[[54,50,51,48,48]]},{'StatisticsParameter',[0,12,0,5],[[55,48,48]]},{'StatisticsParameter',[0,11,0,3],[[52,53,49,48,48]]},{'StatisticsParameter',[0,12,0,6],[[48,46,50]]},{'StatisticsParameter',[0,12,0,7],[[50,48]]},{'StatisticsParameter',[0,12,0,8],[[52,48]]}]}]}}}]}]}}}]}}}; diff --git a/lib/asn1/test/test_special_decode_performance.erl b/lib/asn1/test/test_special_decode_performance.erl index 35c396575b..6814bbfaf3 100644 --- a/lib/asn1/test/test_special_decode_performance.erl +++ b/lib/asn1/test/test_special_decode_performance.erl @@ -120,7 +120,7 @@ loop2(Mod,FS,Bin,N) -> get_selective_funcs('PartialDecSeq') -> % [selected_decode_F1,selected_decode_F2,selected_decode_F3,selected_decode_F4]; - [selected_decode_F1,selected_decode_F3,selected_decode_F4]; + [selected_decode_F1_1,selected_decode_F3,selected_decode_F4]; get_selective_funcs('MEDIA-GATEWAY-CONTROL') -> [decode_MegacoMessage_selective]. -- 2.35.3
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor